Add KeygenStep, only show ExtensionStep when window.nostr is present
This commit is contained in:
parent
f2bfa6e2f6
commit
49bde675c3
|
@ -77,10 +77,11 @@ export class NKeyStorage implements ReadonlyMap<string, NostrSigner> {
|
||||||
return this.#keypairs.has(pubkey);
|
return this.#keypairs.has(pubkey);
|
||||||
}
|
}
|
||||||
|
|
||||||
add(secretKey: Uint8Array): void {
|
add(secretKey: Uint8Array): NostrSigner {
|
||||||
const pubkey = getPublicKey(secretKey);
|
const pubkey = getPublicKey(secretKey);
|
||||||
this.#keypairs.set(pubkey, secretKey);
|
this.#keypairs.set(pubkey, secretKey);
|
||||||
this.#syncStorage();
|
this.#syncStorage();
|
||||||
|
return this.get(pubkey)!;
|
||||||
}
|
}
|
||||||
|
|
||||||
*entries(): IterableIterator<[string, NostrSigner]> {
|
*entries(): IterableIterator<[string, NostrSigner]> {
|
||||||
|
|
|
@ -10,7 +10,7 @@ interface IEmojiGraphic {
|
||||||
const EmojiGraphic: React.FC<IEmojiGraphic> = ({ emoji }) => {
|
const EmojiGraphic: React.FC<IEmojiGraphic> = ({ emoji }) => {
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center justify-center'>
|
<div className='flex items-center justify-center'>
|
||||||
<div className='my-6 rounded-full bg-gray-100 p-8 dark:bg-gray-800'>
|
<div className='rounded-full bg-gray-100 p-8 dark:bg-gray-800'>
|
||||||
<Emoji className='h-24 w-24' emoji={emoji} />
|
<Emoji className='h-24 w-24' emoji={emoji} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
|
import { NostrSigner } from 'nspec';
|
||||||
import React, { useState } from 'react';
|
import React, { useState } from 'react';
|
||||||
|
|
||||||
import AccountStep from './steps/account-step';
|
import AccountStep from './steps/account-step';
|
||||||
import ExtensionStep from './steps/extension-step';
|
import ExtensionStep from './steps/extension-step';
|
||||||
import IdentityStep from './steps/identity-step';
|
import IdentityStep from './steps/identity-step';
|
||||||
import KeyStep from './steps/key-step';
|
import KeyStep from './steps/key-step';
|
||||||
|
import KeygenStep from './steps/keygen-step';
|
||||||
import RegisterStep from './steps/register-step';
|
import RegisterStep from './steps/register-step';
|
||||||
|
|
||||||
interface INostrSigninModal {
|
interface INostrSigninModal {
|
||||||
|
@ -11,8 +13,9 @@ interface INostrSigninModal {
|
||||||
}
|
}
|
||||||
|
|
||||||
const NostrSigninModal: React.FC<INostrSigninModal> = ({ onClose }) => {
|
const NostrSigninModal: React.FC<INostrSigninModal> = ({ onClose }) => {
|
||||||
const [step, setStep] = useState(0);
|
const [step, setStep] = useState(window.nostr ? 0 : 1);
|
||||||
|
|
||||||
|
const [, setSigner] = useState<NostrSigner | undefined>();
|
||||||
const [accountId, setAccountId] = useState<string | undefined>();
|
const [accountId, setAccountId] = useState<string | undefined>();
|
||||||
|
|
||||||
const handleClose = () => onClose('NOSTR_SIGNIN');
|
const handleClose = () => onClose('NOSTR_SIGNIN');
|
||||||
|
@ -28,6 +31,8 @@ const NostrSigninModal: React.FC<INostrSigninModal> = ({ onClose }) => {
|
||||||
return <AccountStep accountId={accountId!} setStep={setStep} onClose={handleClose} />;
|
return <AccountStep accountId={accountId!} setStep={setStep} onClose={handleClose} />;
|
||||||
case 4:
|
case 4:
|
||||||
return <RegisterStep onClose={handleClose} />;
|
return <RegisterStep onClose={handleClose} />;
|
||||||
|
case 5:
|
||||||
|
return <KeygenStep setSigner={setSigner} setStep={setStep} onClose={handleClose} />;
|
||||||
default:
|
default:
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
@ -53,7 +53,9 @@ const IdentityStep: React.FC<IIdentityStep> = ({ setAccountId, setStep, onClose
|
||||||
<Modal title={<FormattedMessage id='nostr_signin.identity.title' defaultMessage='Who are you?' />} onClose={onClose}>
|
<Modal title={<FormattedMessage id='nostr_signin.identity.title' defaultMessage='Who are you?' />} onClose={onClose}>
|
||||||
<Form>
|
<Form>
|
||||||
<Stack className='mt-3' space={3}>
|
<Stack className='mt-3' space={3}>
|
||||||
|
<div className='my-3'>
|
||||||
<NostrExtensionIndicator />
|
<NostrExtensionIndicator />
|
||||||
|
</div>
|
||||||
|
|
||||||
<EmojiGraphic emoji='🕵️' />
|
<EmojiGraphic emoji='🕵️' />
|
||||||
|
|
||||||
|
|
|
@ -14,13 +14,13 @@ interface IKeyStep {
|
||||||
const KeyStep: React.FC<IKeyStep> = ({ setStep, onClose }) => {
|
const KeyStep: React.FC<IKeyStep> = ({ setStep, onClose }) => {
|
||||||
return (
|
return (
|
||||||
<Modal title={<FormattedMessage id='nostr_signin.key.title' defaultMessage='You need a key to continue' />} onClose={onClose}>
|
<Modal title={<FormattedMessage id='nostr_signin.key.title' defaultMessage='You need a key to continue' />} onClose={onClose}>
|
||||||
<Stack className='my-3' space={6} justifyContent='center'>
|
<Stack className='my-3' space={6}>
|
||||||
<NostrExtensionIndicator />
|
<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' onClick={() => setStep(5)}>
|
||||||
Generate key
|
Generate key
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
import { generateSecretKey, getPublicKey, nip19 } from 'nostr-tools';
|
||||||
|
import { NostrSigner } from 'nspec';
|
||||||
|
import React, { useMemo, useState } from 'react';
|
||||||
|
import { FormattedMessage } from 'react-intl';
|
||||||
|
|
||||||
|
import { Button, Stack, Modal } from 'soapbox/components/ui';
|
||||||
|
import { NKeys } from 'soapbox/features/nostr/keys';
|
||||||
|
import { useInstance } from 'soapbox/hooks';
|
||||||
|
import { download } from 'soapbox/utils/download';
|
||||||
|
import { slugify } from 'soapbox/utils/input';
|
||||||
|
|
||||||
|
import EmojiGraphic from '../components/emoji-graphic';
|
||||||
|
|
||||||
|
interface IKeygenStep {
|
||||||
|
setSigner(signer: NostrSigner): void;
|
||||||
|
setStep(step: number): void;
|
||||||
|
onClose(): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const KeygenStep: React.FC<IKeygenStep> = ({ setSigner, setStep, onClose }) => {
|
||||||
|
const instance = useInstance();
|
||||||
|
|
||||||
|
const secretKey = useMemo(() => generateSecretKey(), []);
|
||||||
|
const pubkey = useMemo(() => getPublicKey(secretKey), [secretKey]);
|
||||||
|
|
||||||
|
const nsec = useMemo(() => nip19.nsecEncode(secretKey), [secretKey]);
|
||||||
|
const npub = useMemo(() => nip19.npubEncode(pubkey), [pubkey]);
|
||||||
|
|
||||||
|
const [downloaded, setDownloaded] = useState(false);
|
||||||
|
|
||||||
|
const handleDownload = () => {
|
||||||
|
download(nsec, `${slugify(instance.title)}-${npub.slice(5, 9)}.nsec`);
|
||||||
|
setDownloaded(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleNext = () => {
|
||||||
|
const signer = NKeys.add(secretKey);
|
||||||
|
setSigner(signer);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Modal title={<FormattedMessage id='nostr_signin.keygen.title' defaultMessage='Your new key' />} onClose={onClose}>
|
||||||
|
<Stack className='my-3' space={6}>
|
||||||
|
<EmojiGraphic emoji='🔑' />
|
||||||
|
|
||||||
|
<Stack alignItems='center'>
|
||||||
|
<Button theme='primary' size='lg' icon={require('@tabler/icons/download.svg')} onClick={handleDownload}>
|
||||||
|
Download key
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
|
||||||
|
<Stack space={3} alignItems='end'>
|
||||||
|
<Button theme='accent' disabled={!downloaded} size='lg' onClick={handleNext}>
|
||||||
|
Next
|
||||||
|
</Button>
|
||||||
|
</Stack>
|
||||||
|
</Stack>
|
||||||
|
</Modal>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default KeygenStep;
|
|
@ -8,6 +8,15 @@ const normalizeUsername = (username: string): string => {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function slugify(text: string): string {
|
||||||
|
return text
|
||||||
|
.trim()
|
||||||
|
.toLowerCase()
|
||||||
|
.replace(/[^\w]/g, '-') // replace non-word characters with a hyphen
|
||||||
|
.replace(/-+/g, '-'); // replace multiple hyphens with a single hyphen
|
||||||
|
}
|
||||||
|
|
||||||
export {
|
export {
|
||||||
normalizeUsername,
|
normalizeUsername,
|
||||||
|
slugify,
|
||||||
};
|
};
|
Loading…
Reference in New Issue