// NOTE: This file is stable and usually should not be modified. // It is important that all functionality in this file is preserved, and should only be modified if explicitly requested. import React, { useRef, useState } from 'react'; import { Shield, Upload, AlertTriangle, Sparkles, UserPlus, KeyRound, Lock } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription } from "@/components/ui/dialog"; import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs'; import { Alert, AlertDescription } from '@/components/ui/alert'; import { useLoginActions } from '@/hooks/useLoginActions'; import { cn } from '@/lib/utils'; interface LoginDialogProps { isOpen: boolean; onClose: () => void; onLogin: () => void; onSignup?: () => void; } const validateNsec = (nsec: string) => { return /^nsec1[a-zA-Z0-9]{58}$/.test(nsec); }; const validateBunkerUri = (uri: string) => { return uri.startsWith('bunker://'); }; const LoginDialog: React.FC = ({ isOpen, onClose, onLogin, onSignup }) => { const [isLoading, setIsLoading] = useState(false); const [isFileLoading, setIsFileLoading] = useState(false); const [nsec, setNsec] = useState(''); const [bunkerUri, setBunkerUri] = useState(''); const [errors, setErrors] = useState<{ nsec?: string; bunker?: string; file?: string; extension?: string; }>({}); const fileInputRef = useRef(null); const login = useLoginActions(); const handleExtensionLogin = async () => { setIsLoading(true); setErrors(prev => ({ ...prev, extension: undefined })); try { if (!('nostr' in window)) { throw new Error('Nostr extension not found. Please install a NIP-07 extension.'); } await login.extension(); onLogin(); onClose(); } catch (e: unknown) { const error = e as Error; console.error('Bunker login failed:', error); console.error('Nsec login failed:', error); console.error('Extension login failed:', error); setErrors(prev => ({ ...prev, extension: error instanceof Error ? error.message : 'Extension login failed' })); } finally { setIsLoading(false); } }; const executeLogin = (key: string) => { setIsLoading(true); setErrors({}); // Use a timeout to allow the UI to update before the synchronous login call setTimeout(() => { try { login.nsec(key); onLogin(); onClose(); } catch { setErrors({ nsec: "Failed to login with this key. Please check that it's correct." }); setIsLoading(false); } }, 50); }; const handleKeyLogin = () => { if (!nsec.trim()) { setErrors(prev => ({ ...prev, nsec: 'Please enter your secret key' })); return; } if (!validateNsec(nsec)) { setErrors(prev => ({ ...prev, nsec: 'Invalid secret key format. Must be a valid nsec starting with nsec1.' })); return; } executeLogin(nsec); }; const handleBunkerLogin = async () => { if (!bunkerUri.trim()) { setErrors(prev => ({ ...prev, bunker: 'Please enter a bunker URI' })); return; } if (!validateBunkerUri(bunkerUri)) { setErrors(prev => ({ ...prev, bunker: 'Invalid bunker URI format. Must start with bunker://' })); return; } setIsLoading(true); setErrors(prev => ({ ...prev, bunker: undefined })); try { await login.bunker(bunkerUri); onLogin(); onClose(); // Clear the URI from memory setBunkerUri(''); } catch { setErrors(prev => ({ ...prev, bunker: 'Failed to connect to bunker. Please check the URI.' })); } finally { setIsLoading(false); } }; const handleFileUpload = (e: React.ChangeEvent) => { const file = e.target.files?.[0]; if (!file) return; setIsFileLoading(true); setErrors({}); const reader = new FileReader(); reader.onload = (event) => { setIsFileLoading(false); const content = event.target?.result as string; if (content) { const trimmedContent = content.trim(); if (validateNsec(trimmedContent)) { executeLogin(trimmedContent); } else { setErrors({ file: 'File does not contain a valid secret key.' }); } } else { setErrors({ file: 'Could not read file content.' }); } }; reader.onerror = () => { setIsFileLoading(false); setErrors({ file: 'Failed to read file.' }); }; reader.readAsText(file); }; const handleSignupClick = () => { onClose(); if (onSignup) { onSignup(); } }; const defaultTab = 'nostr' in window ? 'extension' : 'key'; return ( Welcome! Sign up or log in to continue
{/* Prominent Sign Up Section */}
New to Nostr?

Create a new account to join the network.

{/* Divider */}
Or log in
{/* Login Methods */} Extension Key Bunker {errors.extension && ( {errors.extension} )}

Login with one click using the browser extension

{ setNsec(e.target.value); if (errors.nsec) setErrors(prev => ({ ...prev, nsec: undefined })); }} className={`rounded-lg ${ errors.nsec ? 'border-red-500 focus-visible:ring-red-500' : '' }`} placeholder='nsec1...' autoComplete="off" /> {errors.nsec && (

{errors.nsec}

)}
or
{errors.file && (

{errors.file}

)}
{ setBunkerUri(e.target.value); if (errors.bunker) setErrors(prev => ({ ...prev, bunker: undefined })); }} className={`rounded-lg border-gray-300 dark:border-gray-700 focus-visible:ring-primary ${ errors.bunker ? 'border-red-500' : '' }`} placeholder='bunker://' autoComplete="off" /> {errors.bunker && (

{errors.bunker}

)}
); }; export default LoginDialog;