ui: simplify adaptive sign-in choices

This commit is contained in:
SkateSpots 2026-11-01 11:16:05 +07:00
parent 807b48bafc
commit 53380a99e1
3 changed files with 41 additions and 8 deletions

View file

@ -66,7 +66,7 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({ isOpen, onClose, onLog
const isAndroid = /Android/i.test(navigator.userAgent); const isAndroid = /Android/i.test(navigator.userAgent);
// Which login method panel is showing. The three method cards below switch it. // Which login method panel is showing. The three method cards below switch it.
const [loginTab, setLoginTab] = useState<'amber' | 'bunker' | 'nsec' | 'extension' | 'connect'>( const [loginTab, setLoginTab] = useState<'amber' | 'bunker' | 'nsec' | 'extension' | 'connect'>(
isAndroid ? 'amber' : 'bunker', isAndroid ? 'amber' : 'connect',
); );
// Step 1: open Amber // Step 1: open Amber
@ -360,18 +360,19 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({ isOpen, onClose, onLog
<Tabs value={loginTab} onValueChange={(v) => setLoginTab(v as typeof loginTab)} className="w-full"> <Tabs value={loginTab} onValueChange={(v) => setLoginTab(v as typeof loginTab)} className="w-full">
{/* Method cards — plain-language labels; protocol names move to a {/* Method cards — plain-language labels; protocol names move to a
tiny hint so newcomers aren't met with "NIP-46" as the main word. */} tiny hint so newcomers aren't met with "NIP-46" as the main word. */}
<div className="mb-3 grid grid-cols-3 gap-2"> <div className="mb-3 grid grid-cols-2 gap-2">
{([ {([
{ id: 'extension', Icon: Shield, label: t('settingsUi.loginDialog.extensionCard'), hint: 'NIP-07' }, { id: 'extension', Icon: Shield, label: t('settingsUi.loginDialog.extensionCard'), hint: 'NIP-07' },
{ id: isAndroid ? 'amber' : 'bunker', Icon: isAndroid ? Smartphone : Cloud, label: t('settingsUi.loginDialog.signerCard'), hint: isAndroid ? 'NIP-55' : 'NIP-46' }, isAndroid
{ id: 'connect', Icon: QrCode, label: t('settingsUi.loginDialog.signInWithPhone'), hint: 'NIP-46' }, ? { id: 'amber', Icon: Smartphone, label: t('settingsUi.loginDialog.amberCard'), hint: 'NIP-55' }
: { id: 'connect', Icon: QrCode, label: t('settingsUi.loginDialog.pairSignerCard'), hint: 'NIP-46' },
] as const).map(({ id, Icon, label, hint }) => { ] as const).map(({ id, Icon, label, hint }) => {
const active = loginTab === id; const active = loginTab === id || (id === 'connect' && loginTab === 'bunker');
return ( return (
<button <button
key={id} key={id}
type="button" type="button"
onClick={() => setLoginTab(id)} onClick={() => setLoginTab(id as typeof loginTab)}
className={cn( className={cn(
'flex min-h-0 flex-col items-center gap-1 rounded-xl border p-2.5 text-center transition-colors', 'flex min-h-0 flex-col items-center gap-1 rounded-xl border p-2.5 text-center transition-colors',
active active
@ -425,6 +426,13 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({ isOpen, onClose, onLog
> >
{t('settingsUi.loginDialog.noAmberGetIt')} {t('settingsUi.loginDialog.noAmberGetIt')}
</a> </a>
<button
type="button"
onClick={() => setLoginTab('connect')}
className="text-[11px] text-gray-500 underline underline-offset-2 hover:text-gray-300"
>
{t('settingsUi.loginDialog.useAnotherSigner')}
</button>
</div> </div>
)} )}
@ -566,6 +574,14 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({ isOpen, onClose, onLog
</Button> </Button>
</div> </div>
)} )}
<button
type='button'
onClick={() => setLoginTab('bunker')}
className='w-full text-[11px] text-gray-500 underline underline-offset-2 hover:text-gray-300'
>
{t('settingsUi.loginDialog.pasteBunker')}
</button>
</div> </div>
</TabsContent> </TabsContent>
@ -581,6 +597,13 @@ export const LoginDialog: React.FC<LoginDialogProps> = ({ isOpen, onClose, onLog
<Button className='w-full rounded-full py-2.5 bg-blue-500 hover:bg-blue-600 text-white font-bold uppercase' disabled={isLoading || !bunkerUri.trim()} onClick={handleBunkerLogin}> <Button className='w-full rounded-full py-2.5 bg-blue-500 hover:bg-blue-600 text-white font-bold uppercase' disabled={isLoading || !bunkerUri.trim()} onClick={handleBunkerLogin}>
{isLoading ? <Loader2 className='w-4 h-4 animate-spin mr-2' /> : <Cloud className='w-4 h-4 mr-2' />}{t('settingsUi.loginDialog.connect')} {isLoading ? <Loader2 className='w-4 h-4 animate-spin mr-2' /> : <Cloud className='w-4 h-4 mr-2' />}{t('settingsUi.loginDialog.connect')}
</Button> </Button>
<button
type='button'
onClick={() => setLoginTab('connect')}
className='w-full text-[11px] text-gray-500 underline underline-offset-2 hover:text-gray-300'
>
{t('settingsUi.loginDialog.backToQR')}
</button>
{/* Instructions collapse: the default state is just input + Connect. */} {/* Instructions collapse: the default state is just input + Connect. */}
<details> <details>
<summary className='cursor-pointer text-[11px] text-gray-500 hover:text-gray-300'> <summary className='cursor-pointer text-[11px] text-gray-500 hover:text-gray-300'>

View file

@ -4590,6 +4590,11 @@ settingsUi: {
welcomeSubtitle: 'Sign up or log in to continue', welcomeSubtitle: 'Sign up or log in to continue',
welcomeSubtitleBrowse: 'Sign up or log in — or keep browsing.', welcomeSubtitleBrowse: 'Sign up or log in — or keep browsing.',
extensionCard: 'Browser extension', extensionCard: 'Browser extension',
amberCard: 'Amber',
pairSignerCard: 'Pair a signer',
useAnotherSigner: 'Use another signer (QR)',
pasteBunker: 'Paste a bunker URI instead',
backToQR: '← Back to QR pairing',
signerCard: 'Signer app', signerCard: 'Signer app',
secretKeyLink: 'Use a secret key instead', secretKeyLink: 'Use a secret key instead',
whereBunker: 'Where do I get a bunker?', whereBunker: 'Where do I get a bunker?',
@ -4632,7 +4637,7 @@ settingsUi: {
connect: 'Connect', connect: 'Connect',
back: '← Back', back: '← Back',
signInWithPhone: 'Sign in with your phone', signInWithPhone: 'Sign in with your phone',
signInWithPhoneDesc: 'Scan with a signer app (Amber, nsec.app). Your key never leaves your phone.', signInWithPhoneDesc: 'Scan with a NIP-46 signer such as nsec.app or Nostrum. On Android, Amber is the one-tap option above.',
showPairingCode: 'Show pairing code', showPairingCode: 'Show pairing code',
waitingSigner: 'Waiting for your signer…', waitingSigner: 'Waiting for your signer…',
copyLink: 'Copy link', copyLink: 'Copy link',

View file

@ -4590,6 +4590,11 @@ settingsUi: {
welcomeSubtitle: 'Regístrate o inicia sesión para continuar', welcomeSubtitle: 'Regístrate o inicia sesión para continuar',
welcomeSubtitleBrowse: 'Regístrate o inicia sesión, o sigue explorando.', welcomeSubtitleBrowse: 'Regístrate o inicia sesión, o sigue explorando.',
extensionCard: 'Extensión del navegador', extensionCard: 'Extensión del navegador',
amberCard: 'Amber',
pairSignerCard: 'Emparejar un firmador',
useAnotherSigner: 'Usar otro firmador (QR)',
pasteBunker: 'Pegar una URI de bunker',
backToQR: '← Volver al emparejamiento QR',
signerCard: 'App firmante', signerCard: 'App firmante',
secretKeyLink: 'Usar una clave secreta', secretKeyLink: 'Usar una clave secreta',
whereBunker: '¿Dónde consigo un bunker?', whereBunker: '¿Dónde consigo un bunker?',
@ -4632,7 +4637,7 @@ settingsUi: {
connect: 'Conectar', connect: 'Conectar',
back: '← Atrás', back: '← Atrás',
signInWithPhone: 'Inicia sesión con tu teléfono', signInWithPhone: 'Inicia sesión con tu teléfono',
signInWithPhoneDesc: 'Escanea con una app firmadora (Amber, nsec.app). Tu clave nunca sale de tu teléfono.', signInWithPhoneDesc: 'Escanea con un firmador NIP-46 como nsec.app o Nostrum. En Android, Amber es la opción de un toque de arriba.',
showPairingCode: 'Mostrar código de emparejamiento', showPairingCode: 'Mostrar código de emparejamiento',
waitingSigner: 'Esperando a tu firmador…', waitingSigner: 'Esperando a tu firmador…',
copyLink: 'Copiar enlace', copyLink: 'Copiar enlace',