aboutsummaryrefslogtreecommitdiff
path: root/packages/challenger-ui
diff options
context:
space:
mode:
authorSebastian <sebasjm@gmail.com>2024-07-01 20:04:47 -0300
committerSebastian <sebasjm@gmail.com>2024-07-01 20:19:42 -0300
commit8d6a114ece9887624a0f82585cce969146e657e1 (patch)
treeb9ad24db4c357a082e5218637e5e2e6cffaecaba /packages/challenger-ui
parent12c82b172eada574d7183979f39bc93d437c28de (diff)
downloadwallet-core-8d6a114ece9887624a0f82585cce969146e657e1.tar.xz
tested with phone and email
Diffstat (limited to 'packages/challenger-ui')
-rw-r--r--packages/challenger-ui/src/hooks/session.ts79
-rw-r--r--packages/challenger-ui/src/pages/AnswerChallenge.tsx44
-rw-r--r--packages/challenger-ui/src/pages/AskChallenge.tsx408
3 files changed, 350 insertions, 181 deletions
diff --git a/packages/challenger-ui/src/hooks/session.ts b/packages/challenger-ui/src/hooks/session.ts
index f1798885c..2c466147f 100644
--- a/packages/challenger-ui/src/hooks/session.ts
+++ b/packages/challenger-ui/src/hooks/session.ts
@@ -24,6 +24,7 @@ import {
codecForStringURL,
codecOptional,
ChallengerApi,
+ codecForList,
} from "@gnu-taler/taler-util";
import { buildStorageKey, useLocalStorage } from "@gnu-taler/web-util/browser";
@@ -46,11 +47,22 @@ export type LastChallengeResponse = {
transmitted: boolean;
};
+interface LastAddress {
+ address: Record<string, string>;
+ type: string;
+ savedAt: AbsoluteTime;
+}
+
export type SessionState = SessionId & {
completedURL: string | undefined;
- lastAddress: Record<string, string> | undefined;
- lastAddressSavedAt: AbsoluteTime | undefined;
+ lastAddress: Array<LastAddress> | undefined;
};
+export const codecForLastAddress = (): Codec<LastAddress> =>
+ buildCodecForObject<LastAddress>()
+ .property("address", codecForAny())
+ .property("type", codecForString())
+ .property("savedAt", codecForAbsoluteTime)
+ .build("LastAddress");
export const codecForSessionState = (): Codec<SessionState> =>
buildCodecForObject<SessionState>()
@@ -59,14 +71,14 @@ export const codecForSessionState = (): Codec<SessionState> =>
.property("redirectURL", codecForStringURL())
.property("state", codecForString())
.property("completedURL", codecOptional(codecForStringURL()))
- .property("lastAddress", codecOptional(codecForAny()))
- .property("lastAddressSavedAt", codecOptional(codecForAbsoluteTime))
+ .property("lastAddress", codecOptional(codecForList(codecForLastAddress())))
.build("SessionState");
export interface SessionStateHandler {
state: SessionState | undefined;
start(s: SessionId): void;
- saveAddress(address: Record<string, string> | undefined): void;
+ saveAddress(type: string, address: Record<string, string>): void;
+ removeAddress(index: number): void;
sent(info: ChallengerApi.ChallengeCreateResponse): void;
failed(info: ChallengerApi.InvalidPinResponse): void;
completed(info: ChallengerApi.ChallengeRedirect): void;
@@ -91,27 +103,39 @@ export function useSessionState(): SessionStateHandler {
update({
...info,
completedURL: undefined,
- lastAddress: state?.lastAddress,
- lastAddressSavedAt: state?.lastAddressSavedAt,
+ lastAddress: state?.lastAddress ?? [],
});
- // cleanAllCache();
},
- saveAddress(address) {
+ removeAddress(index) {
if (!state) throw Error("should have an state");
+ const lastAddr = [...(state?.lastAddress ?? [])];
+ lastAddr.splice(index, 1);
update({
...state,
- // completedURL: url.href,
- lastAddress: address,
- lastAddressSavedAt: AbsoluteTime.now(),
+ lastAddress: lastAddr,
});
},
- sent(info) {
+ saveAddress(type, address) {
if (!state) throw Error("should have an state");
+ const lastAddr = [...(state?.lastAddress ?? [])];
+ lastAddr.push({
+ type,
+ address: address ?? {},
+ savedAt: AbsoluteTime.now(),
+ });
update({
...state,
+ lastAddress: lastAddr,
});
},
- failed(info) {},
+ sent(info) {
+ if (!state) throw Error("should have an state");
+ // instead of reloading state from server we can update client state
+ },
+ failed(info) {
+ if (!state) throw Error("should have an state");
+ // instead of reloading state from server we can update client state
+ },
completed(info) {
if (!state) throw Error("should have an state");
update({
@@ -119,32 +143,5 @@ export function useSessionState(): SessionStateHandler {
completedURL: info.redirect_url,
});
},
- // updateStatus(st: ChallengerApi.ChallengeStatus) {
- // if (!state) return;
- // if (!state.lastStatus) {
- // update({
- // ...state,
- // lastStatus: st,
- // });
- // return;
- // }
- // // current status, FIXME: better check to know if the state changed
- // const ls = state.lastStatus;
- // if (
- // ls.changes_left !== st.changes_left ||
- // ls.fix_address !== st.fix_address ||
- // ls.last_address !== st.last_address
- // ) {
- // update({
- // ...state,
- // lastStatus: st,
- // });
- // return;
- // }
- // },
};
}
-
-// function cleanAllCache(): void {
-// mutate(() => true, undefined, { revalidate: false });
-// }
diff --git a/packages/challenger-ui/src/pages/AnswerChallenge.tsx b/packages/challenger-ui/src/pages/AnswerChallenge.tsx
index 265bda038..13ae16a33 100644
--- a/packages/challenger-ui/src/pages/AnswerChallenge.tsx
+++ b/packages/challenger-ui/src/pages/AnswerChallenge.tsx
@@ -39,8 +39,6 @@ import {
} from "../hooks/challenge.js";
import { useSessionState } from "../hooks/session.js";
-export const EMAIL_REGEX = /^[\w-.]+@([\w-]+\.)+[\w-]{2,4}$/;
-
type Props = {
focus?: boolean;
onComplete: () => void;
@@ -66,7 +64,7 @@ function useReloadOnDeadline(deadline: AbsoluteTime): void {
}
export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
- const { lib } = useChallengerApiContext();
+ const { config, lib } = useChallengerApiContext();
const { i18n } = useTranslationContext();
const { state, sent, failed, completed } = useSessionState();
const [notification, withErrorHandler] = useLocalNotificationHandler();
@@ -75,6 +73,13 @@ export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
pin: !pin ? i18n.str`Can't be empty` : undefined,
});
+ const restrictionKeys = !config.restrictions
+ ? []
+ : Object.keys(config.restrictions);
+ const restrictionKey = !restrictionKeys.length
+ ? undefined
+ : restrictionKeys[0];
+
const result = useChallengeSession(state);
const lastStatus =
@@ -82,13 +87,6 @@ export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
? result.body
: undefined;
- const lastEmail = !lastStatus?.last_address
- ? undefined
- : lastStatus.last_address["email"];
-
- const unableToChangeAddr = !lastStatus || lastStatus.changes_left < 1;
- const contact = lastEmail ? { email: lastEmail } : undefined;
-
const deadline =
lastStatus == undefined
? undefined
@@ -96,6 +94,21 @@ export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
useReloadOnDeadline(deadline ?? AbsoluteTime.never());
+ if (!restrictionKey) {
+ return (
+ <div>
+ invalid server configuration, there is no restriction in /config
+ </div>
+ );
+ }
+
+ const lastAddr = !lastStatus?.last_address
+ ? undefined
+ : lastStatus.last_address[restrictionKey];
+
+ const unableToChangeAddr = !lastStatus || lastStatus.changes_left < 1;
+ const contact = lastAddr ? { [restrictionKey]: lastAddr } : undefined;
+
const onSendAgain =
!state?.nonce ||
contact === undefined ||
@@ -179,12 +192,12 @@ export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
<p class="mt-2 text-lg leading-8 text-gray-600">
{!lastStatus || !deadline || AbsoluteTime.isExpired(deadline) ? (
<i18n.Translate>
- Last TAN code was sent to your address &quot;{contact?.email}
+ Last TAN code was sent to your address &quot;{lastAddr}
&quot; is not valid anymore.
</i18n.Translate>
) : (
<Attention
- title={i18n.str`A TAN code was sent to your address "${contact?.email}"`}
+ title={i18n.str`A TAN code was sent to your address "${lastAddr}"`}
>
<i18n.Translate>
You should wait until &quot;
@@ -212,15 +225,16 @@ export function AnswerChallenge({ focus, onComplete, routeAsk }: Props): VNode {
<p class="mt-2 text-sm leading-6 text-gray-400">
{lastStatus.changes_left < 1 ? (
<i18n.Translate>
- You can&#39;t change the email anymore.
+ You can&#39;t change the contact address anymore.
</i18n.Translate>
) : lastStatus.changes_left === 1 ? (
<i18n.Translate>
- You can change the email one last time.
+ You can change the contact address one last time.
</i18n.Translate>
) : (
<i18n.Translate>
- You can change the email {lastStatus.changes_left} more times.
+ You can change the contact address {lastStatus.changes_left}{" "}
+ more times.
</i18n.Translate>
)}
</p>
diff --git a/packages/challenger-ui/src/pages/AskChallenge.tsx b/packages/challenger-ui/src/pages/AskChallenge.tsx
index 63901f1d9..f0cfb4cc3 100644
--- a/packages/challenger-ui/src/pages/AskChallenge.tsx
+++ b/packages/challenger-ui/src/pages/AskChallenge.tsx
@@ -18,6 +18,7 @@ import {
EmptyObject,
HttpStatusCode,
TalerError,
+ TranslatedString,
} from "@gnu-taler/taler-util";
import {
Attention,
@@ -25,6 +26,7 @@ import {
LocalNotificationBanner,
RouteDefinition,
ShowInputErrorLabel,
+ Time,
useChallengerApiContext,
useLocalNotificationHandler,
useTranslationContext,
@@ -51,47 +53,63 @@ export function AskChallenge({
const { state, sent, saveAddress, completed } = useSessionState();
const { lib, config } = useChallengerApiContext();
- const regexEmail = !config.restrictions
- ? undefined
- : config.restrictions["email"];
-
const { i18n } = useTranslationContext();
const [notification, withErrorHandler] = useLocalNotificationHandler();
- const [email, setEmail] = useState<string | undefined>();
+ const [address, setEmail] = useState<string | undefined>();
const [repeat, setRepeat] = useState<string | undefined>();
const [remember, setRemember] = useState<boolean>(false);
+ const [addrIndex, setAddrIndex] = useState<number | undefined>();
+
+ const restrictionKeys = !config.restrictions
+ ? []
+ : Object.keys(config.restrictions);
+ const restrictionKey = !restrictionKeys.length
+ ? undefined
+ : restrictionKeys[0];
+
+ const result = useChallengeSession(state);
+
+ if (!restrictionKey) {
+ return (
+ <div>
+ invalid server configuration, there is no restriction in /config
+ </div>
+ );
+ }
+ const regexEmail = !config.restrictions
+ ? undefined
+ : config.restrictions[restrictionKey];
const regexTest =
regexEmail && regexEmail.regex ? new RegExp(regexEmail.regex) : EMAIL_REGEX;
const regexHint =
regexEmail && regexEmail.hint ? regexEmail.hint : i18n.str`invalid email`;
- const result = useChallengeSession(state);
-
const lastStatus =
result && !(result instanceof TalerError) && result.type !== "fail"
? result.body
: undefined;
- const prevEmail = !lastStatus?.last_address
+ const prevAddr = !lastStatus?.last_address
? undefined
- : lastStatus.last_address["email"];
+ : lastStatus.last_address[restrictionKey];
const errors = undefinedIfEmpty({
- email: !email
+ address: !address
? i18n.str`required`
- : !regexTest.test(email)
+ : !regexTest.test(address)
? regexHint
- : prevEmail !== undefined && email === prevEmail
- ? i18n.str`email should be different`
+ : prevAddr !== undefined && address === prevAddr
+ ? i18n.str`can't use the same address`
: undefined,
repeat: !repeat
? i18n.str`required`
- : email !== repeat
- ? i18n.str`emails doesn't match`
+ : address !== repeat
+ ? i18n.str`doesn't match`
: undefined,
});
- const contact = email ? { email } : undefined;
+
+ const contact = address ? { [restrictionKey]: address } : undefined;
const onSend =
errors || !contact || !state?.nonce
@@ -105,7 +123,7 @@ export function AskChallenge({
completed(ok.body);
} else {
if (remember) {
- saveAddress(contact);
+ saveAddress(config.address_type, contact);
}
sent(ok.body);
}
@@ -140,17 +158,33 @@ export function AskChallenge({
<h2 class="text-3xl font-bold tracking-tight text-gray-900 sm:text-4xl">
<i18n.Translate>Enter contact details</i18n.Translate>
</h2>
- <p class="mt-2 text-lg leading-8 text-gray-600">
- <i18n.Translate>
- You will receive an email with a TAN code that must be provided on
- the next page.
- </i18n.Translate>
- </p>
+ {config.address_type === "email" ? (
+ <p class="mt-2 text-lg leading-8 text-gray-600">
+ <i18n.Translate>
+ You will receive an email with a TAN code that must be provided
+ on the next page.
+ </i18n.Translate>
+ </p>
+ ) : config.address_type === "phone" ? (
+ <p class="mt-2 text-lg leading-8 text-gray-600">
+ <i18n.Translate>
+ You will receive an SMS with a TAN code that must be provided on
+ the next page.
+ </i18n.Translate>
+ </p>
+ ) : (
+ <p class="mt-2 text-lg leading-8 text-gray-600">
+ <i18n.Translate>
+ You will receive an message with a TAN code that must be
+ provided on the next page.
+ </i18n.Translate>
+ </p>
+ )}
</div>
{lastStatus?.last_address && (
<Fragment>
- <Attention title={i18n.str`A code has been sent to ${prevEmail}`}>
+ <Attention title={i18n.str`A code has been sent to ${prevAddr}`}>
<i18n.Translate>
<a href={routeSolveChallenge.url({})} class="underline">
<i18n.Translate>Complete the challenge here.</i18n.Translate>
@@ -160,90 +194,232 @@ export function AskChallenge({
</Fragment>
)}
- <form
- method="POST"
- class="mx-auto mt-4 max-w-xl "
- onSubmit={(e) => {
- e.preventDefault();
- }}
- >
- <div class="sm:col-span-2">
- <label
- for="email"
- class="block text-sm font-semibold leading-6 text-gray-900"
- >
- <i18n.Translate>Email</i18n.Translate>
- </label>
- <div class="mt-2.5">
- <input
- type="email"
- name="email"
- id="email"
- ref={focus ? doAutoFocus : undefined}
- maxLength={512}
- autocomplete="email"
- value={email}
- onChange={(e) => {
- setEmail(e.currentTarget.value);
- }}
- placeholder={prevEmail}
- readOnly={lastStatus.fix_address}
- class="block w-full read-only:bg-slate-200 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?.email}
- isDirty={email !== undefined}
- />
- </div>
+ {!state?.lastAddress || !state.lastAddress.length ? undefined : (
+ <div class="mx-auto max-w-xl mt-4">
+ <h3>
+ <i18n.Translate>Previous address</i18n.Translate>
+ </h3>
+ <fieldset>
+ <div class="relative -space-y-px rounded-md bg-white">
+ {state.lastAddress.map((addr, idx) => {
+ return (
+ <label
+ data-checked={addrIndex === idx}
+ class="relative flex border-gray-200 data-[checked=true]:z-10 data-[checked=true]:bg-indigo-50 cursor-pointer flex-col rounded-tl-md rounded-tr-md border p-4 focus:outline-none md:grid md:grid-cols-2 md:pl-4 md:pr-6"
+ >
+ <span class="flex items-center text-sm">
+ <input
+ type="radio"
+ name={`addr-${idx}`}
+ value={addr.address[restrictionKey]}
+ checked={addrIndex === idx}
+ onClick={() => {
+ setAddrIndex(idx);
+ setEmail(addr.address[restrictionKey]);
+ setRepeat(addr.address[restrictionKey]);
+ }}
+ class="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600 active:ring-2 active:ring-indigo-600 active:ring-offset-2"
+ />
+ <span
+ data-checked={addrIndex === idx}
+ class="ml-3 font-medium text-gray-900 data-[checked=true]:text-indigo-900 "
+ >
+ {addr.address[restrictionKey]}
+ </span>
+ </span>
+ <span
+ data-checked={addrIndex === idx}
+ class="ml-6 pl-1 text-sm md:ml-0 md:pl-0 md:text-right text-gray-500 data-[checked=true]:text-indigo-700"
+ >
+ <i18n.Translate>
+ Last used at{" "}
+ <Time
+ format="dd/MM/yyyy HH:mm:ss"
+ timestamp={addr.savedAt}
+ />
+ </i18n.Translate>
+ </span>
+ </label>
+ );
+ })}
+ <label
+ data-checked={addrIndex === undefined}
+ class="relative rounded-bl-md rounded-br-md flex border-gray-200 data-[checked=true]:z-10 data-[checked=true]:bg-indigo-50 cursor-pointer flex-col rounded-tl-md rounded-tr-md border p-4 focus:outline-none md:grid md:grid-cols-2 md:pl-4 md:pr-6"
+ >
+ <span class="flex items-center text-sm">
+ <input
+ type="radio"
+ name="new-addr"
+ value="new-addr"
+ checked={addrIndex === undefined}
+ onClick={() => {
+ setAddrIndex(undefined);
+ setEmail(undefined);
+ setRepeat(undefined);
+ }}
+ class="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600 active:ring-2 active:ring-indigo-600 active:ring-offset-2"
+ />
+ <span
+ data-checked={addrIndex === undefined}
+ class="ml-3 font-medium text-gray-900 data-[checked=true]:text-indigo-900 "
+ >
+ <i18n.Translate>Use new address</i18n.Translate>
+ </span>
+ </span>
+ </label>
+ </div>
+ </fieldset>
</div>
+ )}
- {lastStatus.fix_address ? undefined : (
+ {addrIndex !== undefined ? undefined : (
+ <form
+ method="POST"
+ class="mx-auto mt-4 max-w-xl "
+ onSubmit={(e) => {
+ e.preventDefault();
+ }}
+ >
<div class="sm:col-span-2">
<label
- for="repeat-email"
+ for="adress"
class="block text-sm font-semibold leading-6 text-gray-900"
>
- <i18n.Translate>Repeat email</i18n.Translate>
+ {(function (): TranslatedString {
+ switch (config.address_type) {
+ case "email":
+ return i18n.str`Email`;
+ case "phone":
+ return i18n.str`Phone`;
+ }
+ })()}
</label>
<div class="mt-2.5">
<input
- type="email"
- name="repeat-email"
- id="repeat-email"
- value={repeat}
+ type="text"
+ name="adress"
+ id="adress"
+ ref={focus ? doAutoFocus : undefined}
+ maxLength={512}
+ autocomplete={(function (): string {
+ switch (config.address_type) {
+ case "email":
+ return "email";
+ case "phone":
+ return "phone";
+ }
+ })()}
+ value={address}
onChange={(e) => {
- setRepeat(e.currentTarget.value);
+ setEmail(e.currentTarget.value);
}}
- 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"
+ placeholder={prevAddr}
+ readOnly={lastStatus.fix_address}
+ class="block w-full read-only:bg-slate-200 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}
+ message={errors?.address}
+ isDirty={address !== undefined}
/>
</div>
</div>
- )}
- {lastStatus === undefined ? undefined : (
- <p class="mt-2 text-sm leading-6 text-gray-400">
- {lastStatus.changes_left < 1 ? (
- <i18n.Translate>
- You can&#39;t change the email anymore.
- </i18n.Translate>
- ) : lastStatus.changes_left === 1 ? (
- <i18n.Translate>
- You can change the email one last time.
- </i18n.Translate>
- ) : (
- <i18n.Translate>
- You can change the email {lastStatus.changes_left} more times.
- </i18n.Translate>
- )}
- </p>
- )}
+ {lastStatus.fix_address ? undefined : (
+ <div class="sm:col-span-2">
+ <label
+ for="repeat-address"
+ class="block text-sm font-semibold leading-6 text-gray-900"
+ >
+ {(function (): TranslatedString {
+ switch (config.address_type) {
+ case "email":
+ return i18n.str`Repeat email`;
+ case "phone":
+ return i18n.str`Repeat phone`;
+ }
+ })()}
+ </label>
+ <div class="mt-2.5">
+ <input
+ type="text"
+ name="repeat-address"
+ id="repeat-address"
+ value={repeat}
+ onChange={(e) => {
+ setRepeat(e.currentTarget.value);
+ }}
+ autocomplete={(function (): string {
+ switch (config.address_type) {
+ case "email":
+ return "email";
+ case "phone":
+ return "phone";
+ }
+ })()}
+ 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>
+ )}
+
+ {lastStatus === undefined ? undefined : (
+ <p class="mt-2 text-sm leading-6 text-gray-400">
+ {lastStatus.changes_left < 1 ? (
+ <i18n.Translate>
+ You can&#39;t change the contact address anymore.
+ </i18n.Translate>
+ ) : lastStatus.changes_left === 1 ? (
+ <i18n.Translate>
+ You can change the contact address one last time.
+ </i18n.Translate>
+ ) : (
+ <i18n.Translate>
+ You can change the contact address {lastStatus.changes_left}{" "}
+ more times.
+ </i18n.Translate>
+ )}
+ </p>
+ )}
- {!prevEmail ? (
+ <div class="flex items-center justify-between py-2">
+ <span class="flex flex-grow flex-col">
+ <span
+ class="text-sm text-black font-medium leading-6 "
+ id="availability-label"
+ >
+ <i18n.Translate>
+ Remember this address for future challenges.
+ </i18n.Translate>
+ </span>
+ </span>
+ <button
+ type="button"
+ name={`remember switch`}
+ data-enabled={remember}
+ class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
+ role="switch"
+ aria-checked="false"
+ aria-labelledby="availability-label"
+ aria-describedby="availability-description"
+ onClick={() => {
+ setRemember(!remember);
+ }}
+ >
+ <span
+ aria-hidden="true"
+ data-enabled={remember}
+ class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
+ ></span>
+ </button>
+ </div>
+ </form>
+ )}
+ <div class="mx-auto mt-4 max-w-xl ">
+ {!prevAddr ? (
<div class="mt-10">
<Button
type="submit"
@@ -251,7 +427,14 @@ export function AskChallenge({
class="block w-full disabled:bg-gray-300 rounded-md bg-indigo-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
handler={onSend}
>
- <i18n.Translate>Send email</i18n.Translate>
+ {(function (): TranslatedString {
+ switch (config.address_type) {
+ case "email":
+ return i18n.str`Send email`;
+ case "phone":
+ return i18n.str`Send SMS`;
+ }
+ })()}
</Button>
</div>
) : (
@@ -262,43 +445,18 @@ export function AskChallenge({
class="block w-full disabled:bg-gray-300 rounded-md bg-indigo-600 px-3.5 py-2.5 text-center text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
handler={onSend}
>
- <i18n.Translate>Change email</i18n.Translate>
+ {(function (): TranslatedString {
+ switch (config.address_type) {
+ case "email":
+ return i18n.str`Change email`;
+ case "phone":
+ return i18n.str`Change phone`;
+ }
+ })()}
</Button>
</div>
)}
-
- <div class="flex items-center justify-between py-2">
- <span class="flex flex-grow flex-col">
- <span
- class="text-sm text-black font-medium leading-6 "
- id="availability-label"
- >
- <i18n.Translate>
- Remember this address for future challenges.
- </i18n.Translate>
- </span>
- </span>
- <button
- type="button"
- name={`remember switch`}
- data-enabled={remember}
- class="bg-indigo-600 data-[enabled=false]:bg-gray-200 relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-indigo-600 focus:ring-offset-2"
- role="switch"
- aria-checked="false"
- aria-labelledby="availability-label"
- aria-describedby="availability-description"
- onClick={() => {
- setRemember(!remember);
- }}
- >
- <span
- aria-hidden="true"
- data-enabled={remember}
- class="translate-x-5 data-[enabled=false]:translate-x-0 pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out"
- ></span>
- </button>
- </div>
- </form>
+ </div>
</div>
</Fragment>
);