mirror of
https://gitlab.com/soapbox-pub/mkstack.git
synced 2025-09-24 02:06:07 +00:00
Refactor SignupDialog to match Soapbox design with improved UX
- Simplify SignupDialog UI to clean Soapbox-style layout with key emoji - Fix button width to match text size instead of full width - Implement proper key generation flow using existing generateSecretKey code - Replace toast message with direct login modal opening for 'I already have a key' - Remove duplicate NostrExtensionIndicator (LoginDialog already handles extensions) - Update LoginArea to handle modal transitions between signup and login - Maintain 3-step flow: key selection → generation → confirmation - Reuse existing MKStack functionality instead of reinventing wheel
This commit is contained in:
parent
05c0ec92f7
commit
a0bd6729ae
@ -58,6 +58,10 @@ export function LoginArea({ className }: LoginAreaProps) {
|
|||||||
isOpen={signupDialogOpen}
|
isOpen={signupDialogOpen}
|
||||||
onClose={() => setSignupDialogOpen(false)}
|
onClose={() => setSignupDialogOpen(false)}
|
||||||
onLogin={handleLogin}
|
onLogin={handleLogin}
|
||||||
|
onOpenLogin={() => {
|
||||||
|
setSignupDialogOpen(false);
|
||||||
|
setLoginDialogOpen(true);
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -1,50 +0,0 @@
|
|||||||
import React from 'react';
|
|
||||||
import { useLoginActions } from '@/hooks/useLoginActions';
|
|
||||||
|
|
||||||
interface NostrExtensionIndicatorProps {
|
|
||||||
onLogin?: () => void;
|
|
||||||
onClose?: () => void;
|
|
||||||
}
|
|
||||||
|
|
||||||
const NostrExtensionIndicator: React.FC<NostrExtensionIndicatorProps> = ({ onLogin, onClose }) => {
|
|
||||||
const login = useLoginActions();
|
|
||||||
|
|
||||||
const handleExtensionLogin = async () => {
|
|
||||||
try {
|
|
||||||
await login.extension();
|
|
||||||
onLogin?.();
|
|
||||||
onClose?.();
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Extension login failed:', error);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function renderBody(): React.ReactNode {
|
|
||||||
if ('nostr' in window) {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
<button
|
|
||||||
type='button'
|
|
||||||
className='underline text-blue-600 hover:text-blue-800 dark:text-blue-400 dark:hover:text-blue-300'
|
|
||||||
onClick={handleExtensionLogin}
|
|
||||||
>
|
|
||||||
Sign in
|
|
||||||
</button>
|
|
||||||
{' '}with browser extension.
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
return 'Browser extension not found.';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className='flex items-center rounded-lg bg-gray-100 p-3 dark:bg-gray-800'>
|
|
||||||
<p className='text-xs text-gray-600 dark:text-gray-400'>
|
|
||||||
{renderBody()}
|
|
||||||
</p>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
export default NostrExtensionIndicator;
|
|
@ -2,13 +2,12 @@
|
|||||||
// It is important that all functionality in this file is preserved, and should only be modified if explicitly requested.
|
// It is important that all functionality in this file is preserved, and should only be modified if explicitly requested.
|
||||||
|
|
||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
import { Dialog, DialogContent, DialogHeader, DialogTitle } from "@/components/ui/dialog";
|
||||||
|
|
||||||
import { toast } from '@/hooks/useToast';
|
import { toast } from '@/hooks/useToast';
|
||||||
|
import { useLoginActions } from '@/hooks/useLoginActions';
|
||||||
|
import { generateSecretKey, nip19 } from 'nostr-tools';
|
||||||
|
|
||||||
import NostrExtensionIndicator from './NostrExtensionIndicator';
|
|
||||||
import { cn } from '@/lib/utils';
|
import { cn } from '@/lib/utils';
|
||||||
|
|
||||||
interface SignupDialogProps {
|
interface SignupDialogProps {
|
||||||
@ -16,36 +15,76 @@ interface SignupDialogProps {
|
|||||||
onClose: () => void;
|
onClose: () => void;
|
||||||
onComplete?: () => void;
|
onComplete?: () => void;
|
||||||
onLogin?: () => void;
|
onLogin?: () => void;
|
||||||
|
onOpenLogin?: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onLogin }) => {
|
const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onLogin, onOpenLogin }) => {
|
||||||
const [step, setStep] = useState<'key' | 'keygen'>('key');
|
const [step, setStep] = useState<'key' | 'keygen' | 'download'>('key');
|
||||||
|
const [nsec, setNsec] = useState('');
|
||||||
|
const login = useLoginActions();
|
||||||
|
|
||||||
const handleGenerateKey = () => {
|
const handleGenerateKey = () => {
|
||||||
setStep('keygen');
|
setStep('keygen');
|
||||||
|
|
||||||
|
// Add a dramatic pause for the key generation effect
|
||||||
|
setTimeout(() => {
|
||||||
|
try {
|
||||||
|
// Generate a new secret key
|
||||||
|
const sk = generateSecretKey();
|
||||||
|
|
||||||
|
// Convert to nsec format
|
||||||
|
setNsec(nip19.nsecEncode(sk));
|
||||||
|
setStep('download');
|
||||||
|
|
||||||
|
toast({
|
||||||
|
title: 'Your Secret Key is Ready!',
|
||||||
|
description: 'A new secret key has been generated for you.',
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: 'Error',
|
||||||
|
description: 'Failed to generate key. Please try again.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
|
setStep('key');
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleHasKey = () => {
|
const handleHasKey = () => {
|
||||||
onClose();
|
onClose();
|
||||||
// This would trigger the login dialog to open with the key-add step
|
if (onOpenLogin) {
|
||||||
// For now, we'll just show a toast message
|
onOpenLogin();
|
||||||
toast({
|
}
|
||||||
title: 'Use Login Instead',
|
|
||||||
description: 'Please use the login dialog to enter your existing key.',
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleExtensionLogin = () => {
|
const handleUseKey = () => {
|
||||||
if (onLogin) {
|
try {
|
||||||
onLogin();
|
login.nsec(nsec);
|
||||||
|
if (onLogin) {
|
||||||
|
onLogin();
|
||||||
|
}
|
||||||
|
onClose();
|
||||||
|
toast({
|
||||||
|
title: 'Welcome!',
|
||||||
|
description: 'Your account is ready.',
|
||||||
|
});
|
||||||
|
} catch {
|
||||||
|
toast({
|
||||||
|
title: 'Login Failed',
|
||||||
|
description: 'Failed to login with the generated key. Please try again.',
|
||||||
|
variant: 'destructive',
|
||||||
|
});
|
||||||
}
|
}
|
||||||
onClose();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Reset state when dialog opens
|
// Reset state when dialog opens
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isOpen) {
|
if (isOpen) {
|
||||||
setStep('key');
|
setStep('key');
|
||||||
|
setNsec('');
|
||||||
}
|
}
|
||||||
}, [isOpen]);
|
}, [isOpen]);
|
||||||
|
|
||||||
@ -73,7 +112,7 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onLogin })
|
|||||||
|
|
||||||
<div className='flex flex-col items-center space-y-3 w-full'>
|
<div className='flex flex-col items-center space-y-3 w-full'>
|
||||||
<Button
|
<Button
|
||||||
className='w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg'
|
className='bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg'
|
||||||
size='lg'
|
size='lg'
|
||||||
onClick={handleGenerateKey}
|
onClick={handleGenerateKey}
|
||||||
>
|
>
|
||||||
@ -95,13 +134,41 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onLogin })
|
|||||||
<div className='text-center space-y-4'>
|
<div className='text-center space-y-4'>
|
||||||
<div className='space-y-2'>
|
<div className='space-y-2'>
|
||||||
<p className='text-lg font-semibold'>
|
<p className='text-lg font-semibold'>
|
||||||
Key Generation Coming Soon
|
Generating your secret key...
|
||||||
</p>
|
</p>
|
||||||
<p className='text-sm text-muted-foreground'>
|
<p className='text-sm text-muted-foreground'>
|
||||||
Key generation feature is being developed. For now, please use an existing key or browser extension.
|
Creating your secure key
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className='flex justify-center'>
|
||||||
|
<div className='w-8 h-8 border-4 border-blue-600 border-t-transparent rounded-full animate-spin'></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
|
||||||
|
{step === 'download' && (
|
||||||
|
<div className='text-center space-y-4'>
|
||||||
|
<div className='space-y-2'>
|
||||||
|
<p className='text-lg font-semibold'>
|
||||||
|
Your secret key has been generated!
|
||||||
|
</p>
|
||||||
|
<p className='text-sm text-muted-foreground'>
|
||||||
|
Your key is ready to use.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className='text-6xl'>
|
||||||
|
✅
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
className='w-full bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-6 rounded-lg'
|
||||||
|
onClick={handleUseKey}
|
||||||
|
>
|
||||||
|
Continue with generated key
|
||||||
|
</Button>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
variant='outline'
|
variant='outline'
|
||||||
onClick={() => setStep('key')}
|
onClick={() => setStep('key')}
|
||||||
@ -112,23 +179,7 @@ const SignupDialog: React.FC<SignupDialogProps> = ({ isOpen, onClose, onLogin })
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className='space-y-4'>
|
|
||||||
<div className='relative'>
|
|
||||||
<div className='absolute inset-0 flex items-center'>
|
|
||||||
<div className='w-full border-t border-gray-300 dark:border-gray-600'></div>
|
|
||||||
</div>
|
|
||||||
<div className='relative flex justify-center text-sm'>
|
|
||||||
<span className='px-3 bg-background text-muted-foreground'>
|
|
||||||
or
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<NostrExtensionIndicator
|
|
||||||
onLogin={handleExtensionLogin}
|
|
||||||
onClose={onClose}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
</Dialog>
|
</Dialog>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user