Fix modals to use native form submit

This commit is contained in:
Parmesh Krishen 2024-05-28 10:17:44 -05:00
parent ec836bab70
commit f8c5211dd1
3 changed files with 75 additions and 84 deletions

View File

@ -2,7 +2,11 @@ import { useIntl } from '@cookbook/solid-intl';
import { Component, createEffect, createSignal } from 'solid-js';
import Modal from '../Modal/Modal';
import { login as tLogin, pin as tPin, actions as tActions } from '../../translations';
import {
login as tLogin,
pin as tPin,
actions as tActions,
} from '../../translations';
import styles from './CreatePinModal.module.scss';
import { hookForDev } from '../../lib/devTools';
@ -12,13 +16,12 @@ import ButtonSecondary from '../Buttons/ButtonSecondary';
import { encryptWithPin, setCurrentPin } from '../../lib/PrimalNostr';
const CreatePinModal: Component<{
id?: string,
open?: boolean,
valueToEncrypt?: string,
onPinApplied?: (encryptedValue: string) => void,
onAbort?: () => void,
id?: string;
open?: boolean;
valueToEncrypt?: string;
onPinApplied?: (encryptedValue: string) => void;
onAbort?: () => void;
}> = (props) => {
const intl = useIntl();
let pinInput: HTMLInputElement | undefined;
@ -32,7 +35,7 @@ const CreatePinModal: Component<{
return enc;
};
const onSetPin = async() => {
const onSetPin = async () => {
if (!isValidPin || !isValidRePin()) return;
// Encrypt private key
@ -57,29 +60,27 @@ const CreatePinModal: Component<{
const isValidPin = () => {
return pin().length > 3;
}
};
const isValidRePin = () => {
return rePin() === pin();
};
const onKeyUp = (e: KeyboardEvent) => {
if (e.code === 'Enter' && isValidPin() && isValidRePin()) {
const onSubmit = (e: Event) => {
e.preventDefault();
if (isValidPin() && isValidRePin()) {
onSetPin();
}
};
return (
<Modal open={props.open} onClose={props.onAbort}>
<div id={props.id} class={styles.modal}>
<form id={props.id} class={styles.modal} onSubmit={onSubmit}>
<button class={styles.xClose} onClick={props.onAbort}>
<div class={styles.iconClose}></div>
</button>
<div class={styles.title}>
{intl.formatMessage(tPin.title)}
</div>
<div class={styles.title}>{intl.formatMessage(tPin.title)}</div>
<div class={styles.description}>
{intl.formatMessage(tPin.description)}
</div>
@ -88,41 +89,38 @@ const CreatePinModal: Component<{
type="password"
ref={pinInput}
value={pin()}
onKeyUp={onKeyUp}
onChange={(val: string) => setPin(val)}
validationState={pin().length === 0 || isValidPin() ? 'valid' : 'invalid'}
validationState={
pin().length === 0 || isValidPin() ? 'valid' : 'invalid'
}
errorMessage={intl.formatMessage(tPin.invalidPin)}
/>
<div class={styles.description}>
{intl.formatMessage(tPin.reEnter)}
</div>
<div class={styles.description}>{intl.formatMessage(tPin.reEnter)}</div>
<TextInput
type="password"
value={rePin()}
onKeyUp={onKeyUp}
onChange={(val: string) => setRePin(val)}
validationState={rePin().length === 0 || isValidRePin() ? 'valid' : 'invalid'}
validationState={
rePin().length === 0 || isValidRePin() ? 'valid' : 'invalid'
}
errorMessage={intl.formatMessage(tPin.invalidRePin)}
/>
<div class={styles.actions}>
<ButtonPrimary
onClick={onSetPin}
type="submit"
disabled={!isValidPin() || !isValidRePin()}
>
{intl.formatMessage(tActions.createPin)}
</ButtonPrimary>
<ButtonSecondary
onClick={onOptout}
light={true}
>
<ButtonSecondary onClick={onOptout} light={true}>
{intl.formatMessage(tActions.optoutPin)}
</ButtonSecondary>
</div>
</div>
</form>
</Modal>
);
}
};
export default hookForDev(CreatePinModal);

View File

@ -5,7 +5,6 @@ import { useToastContext } from '../Toaster/Toaster';
import { nip19 } from 'nostr-tools';
import { pin as tPin, actions as tActions } from '../../translations';
import styles from './EnterPinModal.module.scss';
@ -18,14 +17,13 @@ import ButtonSecondary from '../Buttons/ButtonSecondary';
import { useAccountContext } from '../../contexts/AccountContext';
const EnterPinModal: Component<{
id?: string,
open?: boolean,
valueToDecrypt?: string,
onSuccess?: (decryptedValue: string) => void,
onAbort?: () => void,
onForgot?: () => void,
id?: string;
open?: boolean;
valueToDecrypt?: string;
onSuccess?: (decryptedValue: string) => void;
onAbort?: () => void;
onForgot?: () => void;
}> = (props) => {
const intl = useIntl();
const toast = useToastContext();
const account = useAccountContext();
@ -40,7 +38,7 @@ const EnterPinModal: Component<{
return dec;
};
const onConfirm = async() => {
const onConfirm = async () => {
if (!isValidPin) return;
// Decrypt private key
@ -50,7 +48,7 @@ const EnterPinModal: Component<{
const decoded = nip19.decode(enc);
if (decoded.type !== 'nsec' || !decoded.data) {
throw('invalid-nsec-decoded');
throw 'invalid-nsec-decoded';
}
// Save PIN for the session
@ -58,11 +56,10 @@ const EnterPinModal: Component<{
// Execute callback
props.onSuccess && props.onSuccess(enc);
} catch(e) {
} catch (e) {
logError('Failed to decode nsec: ', e);
toast?.sendWarning('PIN is incorrect');
}
};
createEffect(() => {
@ -73,54 +70,48 @@ const EnterPinModal: Component<{
const isValidPin = () => {
return pin().length > 3;
}
};
const onKeyUp = (e: KeyboardEvent) => {
if (e.code === 'Enter' && isValidPin()) {
const onSubmit = (e: Event) => {
e.preventDefault();
if (isValidPin()) {
onConfirm();
}
};
return (
<Modal open={props.open} opaqueBackdrop={true}>
<div id={props.id} class={styles.modal}>
<form id={props.id} class={styles.modal} onSubmit={onSubmit}>
<button class={styles.xClose} onClick={props.onAbort}>
<div class={styles.iconClose}></div>
</button>
<div class={styles.title}>
{intl.formatMessage(tPin.enterTitle)}
</div>
<div class={styles.description}>
{intl.formatMessage(tPin.enter)}
</div>
<div class={styles.title}>{intl.formatMessage(tPin.enterTitle)}</div>
<div class={styles.description}>{intl.formatMessage(tPin.enter)}</div>
<div class={styles.inputs}>
<TextInput
type="password"
ref={pinInput}
value={pin()}
onKeyUp={onKeyUp}
onChange={(val: string) => setPin(val)}
validationState={pin().length === 0 || isValidPin() ? 'valid' : 'invalid'}
validationState={
pin().length === 0 || isValidPin() ? 'valid' : 'invalid'
}
errorMessage={intl.formatMessage(tPin.invalidRePin)}
/>
</div>
<div class={styles.actions}>
<ButtonPrimary
onClick={onConfirm}
disabled={!isValidPin()}
>
<ButtonPrimary type="submit" disabled={!isValidPin()}>
{intl.formatMessage(tActions.login)}
</ButtonPrimary>
<ButtonSecondary
onClick={props.onForgot}
>
<ButtonSecondary onClick={props.onForgot}>
{intl.formatMessage(tActions.forgotPin)}
</ButtonSecondary>
</div>
</div>
</form>
</Modal>
);
}
};
export default hookForDev(EnterPinModal);

View File

@ -14,15 +14,14 @@ import { nip19 } from 'nostr-tools';
import { storeSec } from '../../lib/localStore';
const LoginModal: Component<{
id?: string,
open?: boolean,
onAbort?: () => void,
id?: string;
open?: boolean;
onAbort?: () => void;
}> = (props) => {
const intl = useIntl();
const account = useAccountContext();
const [step, setStep] = createSignal<'login' | 'pin' | 'none'>('login')
const [step, setStep] = createSignal<'login' | 'pin' | 'none'>('login');
const [enteredKey, setEnteredKey] = createSignal('');
let loginInput: HTMLInputElement | undefined;
@ -39,13 +38,13 @@ const LoginModal: Component<{
const onStoreSec = (sec: string | undefined) => {
storeSec(sec);
onAbort();
}
};
const onAbort = () => {
setStep(() => 'login');
setEnteredKey('');
props.onAbort && props.onAbort();
}
};
const isValidNsec: () => boolean = () => {
const key = enteredKey();
@ -58,8 +57,8 @@ const LoginModal: Component<{
try {
const decoded = nip19.decode(key);
return decoded.type === 'nsec' && decoded.data;
} catch(e) {
return Boolean(decoded.type === 'nsec' && decoded.data);
} catch (e) {
return false;
}
}
@ -73,8 +72,10 @@ const LoginModal: Component<{
}
});
const onKeyUp = (e: KeyboardEvent) => {
if (e.code === 'Enter' && isValidNsec()) {
const onSubmit = (e: Event) => {
e.preventDefault();
if (isValidNsec()) {
onLogin();
}
};
@ -83,13 +84,11 @@ const LoginModal: Component<{
<Switch>
<Match when={step() === 'login'}>
<Modal open={props.open} onClose={onAbort}>
<div id={props.id} class={styles.modal}>
<form id={props.id} class={styles.modal} onSubmit={onSubmit}>
<button class={styles.xClose} onClick={onAbort}>
<div class={styles.iconClose}></div>
</button>
<div class={styles.title}>
{intl.formatMessage(tLogin.title)}
</div>
<div class={styles.title}>{intl.formatMessage(tLogin.title)}</div>
<div class={styles.description}>
{intl.formatMessage(tLogin.description)}
</div>
@ -98,21 +97,24 @@ const LoginModal: Component<{
ref={loginInput}
type="password"
value={enteredKey()}
onKeyUp={onKeyUp}
onChange={setEnteredKey}
validationState={enteredKey().length === 0 || isValidNsec() ? 'valid' : 'invalid'}
validationState={
enteredKey().length === 0 || isValidNsec()
? 'valid'
: 'invalid'
}
errorMessage={intl.formatMessage(tLogin.invalidNsec)}
/>
</div>
<div class={styles.actions}>
<ButtonPrimary
onClick={onLogin}
type="submit"
disabled={enteredKey().length === 0 || !isValidNsec()}
>
{intl.formatMessage(tActions.login)}
</ButtonPrimary>
</div>
</div>
</form>
</Modal>
</Match>
@ -128,6 +130,6 @@ const LoginModal: Component<{
</Match>
</Switch>
);
}
};
export default hookForDev(LoginModal);