NostrSigninModal: make each step render a Modal directly

This commit is contained in:
Alex Gleason 2024-02-18 13:27:30 -06:00
parent 9267ef28e3
commit ba04c43477
No known key found for this signature in database
GPG key ID: 7211D1F99744FBB7
6 changed files with 135 additions and 147 deletions

View file

@ -1,7 +1,4 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { Modal, Stack } from 'soapbox/components/ui';
import AccountStep from './steps/account-step'; import AccountStep from './steps/account-step';
import ExtensionStep from './steps/extension-step'; import ExtensionStep from './steps/extension-step';
@ -18,50 +15,22 @@ const NostrSigninModal: React.FC<INostrSigninModal> = ({ onClose }) => {
const [accountId, setAccountId] = useState<string | undefined>(); const [accountId, setAccountId] = useState<string | undefined>();
const handleClose = () => { const handleClose = () => onClose('NOSTR_SIGNIN');
onClose('NOSTR_SIGNIN');
};
const renderStep = () => { switch (step) {
switch (step) { case 0:
case 0: return <ExtensionStep setStep={setStep} onClose={handleClose} />;
return <ExtensionStep setStep={setStep} />; case 1:
case 1: return <IdentityStep setAccountId={setAccountId} setStep={setStep} onClose={handleClose} />;
return <IdentityStep setAccountId={setAccountId} setStep={setStep} />; case 2:
case 2: return <KeyStep setStep={setStep} onClose={handleClose} />;
return <KeyStep setStep={setStep} />; case 3:
case 3: return <AccountStep accountId={accountId!} setStep={setStep} onClose={handleClose} />;
return <AccountStep accountId={accountId!} />; case 4:
case 4: return <RegisterStep onClose={handleClose} />;
return <RegisterStep />; default:
} return null;
}; }
const renderModalTitle = () => {
switch (step) {
case 0:
return <FormattedMessage id='nostr_signin.siwe.title' defaultMessage='Sign in' />;
case 1:
return <FormattedMessage id='nostr_signin.identity.title' defaultMessage='Who are you?' />;
case 2:
return <FormattedMessage id='nostr_signin.key.title' defaultMessage='You need a key to continue' />;
case 3:
return <FormattedMessage id='nostr_signin.account.title' defaultMessage='Create an account' />;
default:
return null;
}
};
return (
<Modal
title={renderModalTitle()}
onClose={handleClose}
>
<Stack space={2}>
{renderStep()}
</Stack>
</Modal>
);
}; };
export default NostrSigninModal; export default NostrSigninModal;

View file

@ -1,15 +1,18 @@
import { NSchema as n } from 'nspec'; import { NSchema as n } from 'nspec';
import React, { useMemo } from 'react'; import React, { useMemo } from 'react';
import { FormattedMessage } from 'react-intl';
import { useAccount } from 'soapbox/api/hooks'; import { useAccount } from 'soapbox/api/hooks';
import { Avatar, Text, Stack, Emoji, Button, Tooltip } from 'soapbox/components/ui'; import { Avatar, Text, Stack, Emoji, Button, Tooltip, HStack, Modal } from 'soapbox/components/ui';
import { useInstance } from 'soapbox/hooks'; import { useInstance } from 'soapbox/hooks';
interface IAccountStep { interface IAccountStep {
accountId: string; accountId: string;
setStep(step: number): void;
onClose(): void;
} }
const AccountStep: React.FC<IAccountStep> = ({ accountId }) => { const AccountStep: React.FC<IAccountStep> = ({ accountId, setStep, onClose }) => {
const { account } = useAccount(accountId); const { account } = useAccount(accountId);
const instance = useInstance(); const instance = useInstance();
@ -18,50 +21,58 @@ const AccountStep: React.FC<IAccountStep> = ({ accountId }) => {
[account?.acct], [account?.acct],
); );
const goBack = () => setStep(1);
if (!account) { if (!account) {
return null; return null;
} }
return ( return (
<Stack space={6}> <Modal title={<FormattedMessage id='nostr_signin.account.title' defaultMessage='Your account' />} onClose={onClose}>
<Stack space={3} alignItems='center'> <Stack space={6}>
<Avatar className='bg-gray-100 dark:bg-gray-800' src={account.avatar} size={160} /> <Stack space={3} alignItems='center'>
<Avatar className='bg-gray-100 dark:bg-gray-800' src={account.avatar} size={160} />
<Stack space={1}> <Stack space={1}>
<Text <Text
size='xl' size='xl'
weight='semibold' weight='semibold'
align='center' align='center'
dangerouslySetInnerHTML={{ __html: account.display_name_html }} dangerouslySetInnerHTML={{ __html: account.display_name_html }}
truncate truncate
/> />
<Tooltip text={account.nostr.npub ?? account.acct}> <Tooltip text={account.nostr.npub ?? account.acct}>
<Text size='sm' theme='muted' align='center' truncate> <Text size='sm' theme='muted' align='center' truncate>
{isBech32 ? ( {isBech32 ? account.acct.slice(0, 13) : account.acct}
account.acct.slice(0, 13) </Text>
) : ( </Tooltip>
account.acct
)}
</Text>
</Tooltip>
</Stack>
</Stack>
{!account.ditto.is_registered && (
<Stack space={6}>
<Stack space={3} alignItems='center' className='rounded-xl bg-gray-100 p-4 dark:bg-gray-800'>
<Emoji className='h-16 w-16' emoji='🫂' />
<Text align='center' className='max-w-72'>
You need an account on {instance.title} to continue.
</Text>
</Stack> </Stack>
<Button theme='accent' size='lg'>Join</Button>
</Stack> </Stack>
)}
</Stack> {account.ditto.is_registered ? (
<HStack space={3}>
<Button theme='transparent' size='lg' block onClick={goBack}>Not me</Button>
<Button theme='accent' size='lg' block>Continue</Button>
</HStack>
) : (
<Stack space={6}>
<Stack space={3} alignItems='center' className='rounded-xl bg-gray-100 p-4 dark:bg-gray-800'>
<Emoji className='h-16 w-16' emoji='🫂' />
<Text align='center' className='max-w-72'>
You need an account on {instance.title} to continue.
</Text>
</Stack>
<HStack space={3}>
<Button theme='transparent' size='lg' block onClick={goBack}>Not me</Button>
<Button theme='accent' size='lg' block>Join</Button>
</HStack>
</Stack>
)}
</Stack>
</Modal>
); );
}; };

View file

@ -2,34 +2,36 @@ import React from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { nostrExtensionLogIn } from 'soapbox/actions/nostr'; import { nostrExtensionLogIn } from 'soapbox/actions/nostr';
import Button from 'soapbox/components/ui/button/button'; import { Button, Stack, Modal } from 'soapbox/components/ui';
import Stack from 'soapbox/components/ui/stack/stack';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
import EmojiGraphic from '../components/emoji-graphic'; import EmojiGraphic from '../components/emoji-graphic';
interface IExtensionStep { interface IExtensionStep {
setStep: (step: number) => void; setStep: (step: number) => void;
onClose(): void;
} }
const ExtensionStep: React.FC<IExtensionStep> = ({ setStep }) => { const ExtensionStep: React.FC<IExtensionStep> = ({ setStep, onClose }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const onClick = () => dispatch(nostrExtensionLogIn()); const onClick = () => dispatch(nostrExtensionLogIn());
const onClickAlt = () => setStep(1); const onClickAlt = () => setStep(1);
return ( return (
<Stack className='my-6' space={3}> <Modal title={<FormattedMessage id='nostr_signin.siwe.title' defaultMessage='Sign in' />} onClose={onClose}>
<EmojiGraphic emoji='🔐' /> <Stack className='my-6' space={3}>
<EmojiGraphic emoji='🔐' />
<Button theme='accent' size='lg' onClick={onClick}> <Button theme='accent' size='lg' onClick={onClick}>
<FormattedMessage id='nostr_signin.siwe.action' defaultMessage='Sign in with extension' /> <FormattedMessage id='nostr_signin.siwe.action' defaultMessage='Sign in with extension' />
</Button> </Button>
<Button theme='transparent' onClick={onClickAlt}> <Button theme='transparent' onClick={onClickAlt}>
<FormattedMessage id='nostr_signin.siwe.alt' defaultMessage='Sign in with key' /> <FormattedMessage id='nostr_signin.siwe.alt' defaultMessage='Sign in with key' />
</Button> </Button>
</Stack> </Stack>
</Modal>
); );
}; };

View file

@ -1,12 +1,8 @@
import React, { useState } from 'react'; import React, { useState } from 'react';
import { FormattedMessage } from 'react-intl';
import { accountLookup } from 'soapbox/actions/accounts'; import { accountLookup } from 'soapbox/actions/accounts';
import Button from 'soapbox/components/ui/button/button'; import { Button, Form, FormGroup, HStack, Input, Stack, Modal } from 'soapbox/components/ui';
import Form from 'soapbox/components/ui/form/form';
import FormGroup from 'soapbox/components/ui/form-group/form-group';
import HStack from 'soapbox/components/ui/hstack/hstack';
import Input from 'soapbox/components/ui/input/input';
import Stack from 'soapbox/components/ui/stack/stack';
import { useAppDispatch } from 'soapbox/hooks'; import { useAppDispatch } from 'soapbox/hooks';
import EmojiGraphic from '../components/emoji-graphic'; import EmojiGraphic from '../components/emoji-graphic';
@ -15,9 +11,10 @@ import NostrExtensionIndicator from '../components/nostr-extension-indicator';
interface IIdentityStep { interface IIdentityStep {
setAccountId(accountId: string): void; setAccountId(accountId: string): void;
setStep(step: number): void; setStep(step: number): void;
onClose(): void;
} }
const IdentityStep: React.FC<IIdentityStep> = ({ setAccountId, setStep }) => { const IdentityStep: React.FC<IIdentityStep> = ({ setAccountId, setStep, onClose }) => {
const dispatch = useAppDispatch(); const dispatch = useAppDispatch();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
@ -53,36 +50,38 @@ const IdentityStep: React.FC<IIdentityStep> = ({ setAccountId, setStep }) => {
} }
return ( return (
<Form> <Modal title={<FormattedMessage id='nostr_signin.identity.title' defaultMessage='Who are you?' />} onClose={onClose}>
<Stack className='mt-3' space={3}> <Form>
<NostrExtensionIndicator /> <Stack className='mt-3' space={3}>
<NostrExtensionIndicator />
<EmojiGraphic emoji='🕵️' /> <EmojiGraphic emoji='🕵️' />
<FormGroup labelText='Username' errors={errors}> <FormGroup labelText='Username' errors={errors}>
<Input <Input
icon={require('@tabler/icons/at.svg')} icon={require('@tabler/icons/at.svg')}
placeholder='Username or npub' placeholder='Username or npub'
value={username} value={username}
onChange={handleChangeUsername} onChange={handleChangeUsername}
disabled={loading} disabled={loading}
autoFocus autoFocus
/> />
</FormGroup> </FormGroup>
<HStack space={2} alignItems='center' justifyContent='between'> <HStack space={2} alignItems='center' justifyContent='between'>
<Button theme='transparent' onClick={() => setStep(2)} disabled={loading}>Sign up</Button> <Button theme='transparent' onClick={() => setStep(2)} disabled={loading}>Sign up</Button>
<Button <Button
theme='accent' theme='accent'
type='submit' disabled={!username || loading || notFound} type='submit' disabled={!username || loading || notFound}
onClick={handleSubmit} onClick={handleSubmit}
> >
Next Next
</Button> </Button>
</HStack> </HStack>
</Stack> </Stack>
</Form> </Form>
</Modal>
); );
}; };

View file

@ -1,32 +1,35 @@
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl';
import Button from 'soapbox/components/ui/button/button'; import { Button, Stack, Modal } from 'soapbox/components/ui';
import Stack from 'soapbox/components/ui/stack/stack';
import EmojiGraphic from '../components/emoji-graphic'; import EmojiGraphic from '../components/emoji-graphic';
import NostrExtensionIndicator from '../components/nostr-extension-indicator'; import NostrExtensionIndicator from '../components/nostr-extension-indicator';
interface IKeyStep { interface IKeyStep {
setStep(step: number): void; setStep(step: number): void;
onClose(): void;
} }
const KeyStep: React.FC<IKeyStep> = ({ setStep }) => { const KeyStep: React.FC<IKeyStep> = ({ setStep, onClose }) => {
return ( return (
<Stack className='my-3' space={6} justifyContent='center'> <Modal title={<FormattedMessage id='nostr_signin.key.title' defaultMessage='You need a key to continue' />} onClose={onClose}>
<NostrExtensionIndicator /> <Stack className='my-3' space={6} justifyContent='center'>
<NostrExtensionIndicator />
<EmojiGraphic emoji='🔑' /> <EmojiGraphic emoji='🔑' />
<Stack space={3} alignItems='center'> <Stack space={3} alignItems='center'>
<Button theme='accent' size='lg'> <Button theme='accent' size='lg'>
Generate key Generate key
</Button> </Button>
<Button theme='transparent' onClick={() => setStep(1)}> <Button theme='transparent' onClick={() => setStep(1)}>
I already have a key I already have a key
</Button> </Button>
</Stack>
</Stack> </Stack>
</Stack> </Modal>
); );
}; };

View file

@ -1,15 +1,19 @@
import React from 'react'; import React from 'react';
import { FormattedMessage } from 'react-intl';
import Stack from 'soapbox/components/ui/stack/stack'; import { Stack, Modal } from 'soapbox/components/ui';
interface IRegisterStep { interface IRegisterStep {
onClose(): void;
} }
const RegisterStep: React.FC<IRegisterStep> = () => { const RegisterStep: React.FC<IRegisterStep> = ({ onClose }) => {
return ( return (
<Stack space={3}> <Modal title={<FormattedMessage id='nostr_signin.register.title' defaultMessage='Create account' />} onClose={onClose}>
register step <Stack space={3}>
</Stack> register step
</Stack>
</Modal>
); );
}; };