This repository has no description
Something went wrong. Try again.
4.4 kB · 91 lines
TSX
at main
1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192import React, { useState } from 'react';import { Calendar, Loader2, Server, User } from 'lucide-react';import { atprotoService } from '../services/atproto';import { useModalFocus } from '../hooks/useModalFocus';
interface AuthModalProps { isOpen: boolean; onSuccess: () => void;}
export const AuthModal: React.FC<AuthModalProps> = ({ isOpen, onSuccess }) => { const dialogRef = useModalFocus(isOpen, () => {}); const [identifier, setIdentifier] = useState(''); const [loading, setLoading] = useState(false); const [error, setError] = useState<string | null>(() => atprotoService.getAuthenticationError());
if (!isOpen) return null;
const handleSubmit = async (event: React.FormEvent) => { event.preventDefault(); setError(null); setLoading(true); try { await atprotoService.startOAuthLogin(identifier); onSuccess(); } catch (reason) { setError(reason instanceof Error ? reason.message : 'Could not start OAuth sign-in.'); setLoading(false); } };
return ( <div className="fixed inset-0 z-50 flex items-center justify-center p-4 bg-slate-900/70 backdrop-blur-xs animate-fade-in"> <div ref={dialogRef} role="dialog" aria-modal="true" aria-labelledby="auth-modal-title" tabIndex={-1} className="w-full max-w-md bg-white dark:bg-slate-900 rounded-2xl shadow-2xl border border-slate-200 dark:border-slate-800 p-6 md:p-8 overflow-hidden relative"> <div className="absolute -top-24 -right-24 w-48 h-48 bg-primary-500/10 rounded-full blur-3xl pointer-events-none" /> <div className="absolute -bottom-24 -left-24 w-48 h-48 bg-emerald-500/10 rounded-full blur-3xl pointer-events-none" />
<div className="flex items-center gap-3 mb-6"> <div className="w-12 h-12 rounded-xl bg-primary-500/10 dark:bg-primary-500/20 text-primary-600 dark:text-primary-400 flex items-center justify-center"> <Calendar className="w-6 h-6" /> </div> <div> <h2 id="auth-modal-title" className="text-xl font-bold tracking-tight text-slate-900 dark:text-white"> Calend<span className="text-primary-600 dark:text-primary-400">AT</span> </h2> <p className="text-xs text-slate-500 dark:text-slate-400">Sign in through your AT Protocol provider</p> </div> </div>
{error && ( <div role="alert" className="mb-4 p-3 text-xs text-red-600 dark:text-red-400 bg-red-50 dark:bg-red-950/30 border border-red-200 dark:border-red-900/50 rounded-lg"> {error} </div> )}
<form onSubmit={handleSubmit} className="space-y-4"> <div> <label htmlFor="oauth-identifier" className="block text-xs font-medium text-slate-700 dark:text-slate-300 mb-1"> Handle, DID, or PDS URL </label> <div className="relative"> {identifier.startsWith('http') ? <Server className="w-4 h-4 text-slate-400 absolute left-3 top-2.5" /> : <User className="w-4 h-4 text-slate-400 absolute left-3 top-2.5" />} <input id="oauth-identifier" type="text" value={identifier} onChange={(event) => setIdentifier(event.target.value)} required autoComplete="username" placeholder="you.example.com or https://your-pds.example" className="w-full pl-9 pr-3 py-2 text-xs rounded-lg border border-slate-300 dark:border-slate-700 bg-white dark:bg-slate-800 text-slate-900 dark:text-white focus:ring-2 focus:ring-primary-500 focus:border-transparent outline-none transition" /> </div> </div>
<p className="text-xs text-slate-500 dark:text-slate-400"> You will continue securely with your provider. CalendAT never receives your password. </p>
<button type="submit" disabled={loading} className="w-full inline-flex items-center justify-center gap-2 py-2.5 px-4 text-xs font-semibold text-white bg-slate-900 dark:bg-white dark:text-slate-900 hover:bg-slate-800 dark:hover:bg-slate-100 rounded-lg shadow-xs transition-colors disabled:opacity-50" > {loading ? <Loader2 className="w-4 h-4 animate-spin" /> : 'Continue with AT Protocol'} </button> </form> </div> </div> );};