mirror of
https://github.com/PrimalHQ/primal-web-app.git
synced 2024-09-30 08:50:48 +00:00
Fix modals to use native form submit
This commit is contained in:
parent
ec836bab70
commit
f8c5211dd1
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
Loading…
Reference in New Issue
Block a user