ATProto forum built with ESAV
at main 7.9 kB view raw
1import React, { useEffect, useState, useRef } from 'react'; 2import { useAuth } from '@/providers/PassAuthProvider'; 3 4interface LoginProps { 5 compact?: boolean; 6} 7 8export default function Login({ compact = false }: LoginProps) { 9 const { loginStatus, login, logout, loading, authed } = useAuth(); 10 const [user, setUser] = useState(''); 11 const [password, setPassword] = useState(''); 12 const [serviceURL, setServiceURL] = useState('bsky.social'); 13 const [showLoginForm, setShowLoginForm] = useState(false); 14 const formRef = useRef<HTMLDivElement>(null); 15 16 useEffect(() => { 17 function handleClickOutside(event: MouseEvent) { 18 if (formRef.current && !formRef.current.contains(event.target as Node)) { 19 setShowLoginForm(false); 20 } 21 } 22 23 if (showLoginForm) { 24 document.addEventListener('mousedown', handleClickOutside); 25 } 26 27 return () => { 28 document.removeEventListener('mousedown', handleClickOutside); 29 }; 30 }, [showLoginForm]); 31 32 if (loading) { 33 return ( 34 <div className="flex items-center justify-center p-6 text-gray-500 dark:text-gray-400"> 35 Loading... 36 </div> 37 ); 38 } 39 40 if (compact) { 41 if (authed) { 42 return ( 43 <button 44 onClick={logout} 45 className="text-sm bg-gray-600 hover:bg-gray-700 text-white rounded px-3 py-1 font-medium transition-colors" 46 > 47 Log out 48 </button> 49 ); 50 } else { 51 return ( 52 <div className="relative" ref={formRef}> 53 <button 54 onClick={() => setShowLoginForm(!showLoginForm)} 55 className="text-sm bg-gray-600 hover:bg-gray-700 text-white rounded px-3 py-1 font-medium transition-colors" 56 > 57 Log in 58 </button> 59 {showLoginForm && ( 60 <div className="absolute top-full right-0 mt-2 w-80 bg-white dark:bg-gray-900 rounded-lg shadow-lg border border-gray-200 dark:border-gray-700 p-4 z-50"> 61 <form 62 onSubmit={e => { 63 e.preventDefault(); 64 login(user, password, `https://${serviceURL}`); 65 setShowLoginForm(false); 66 }} 67 className="flex flex-col gap-3" 68 > 69 <p className="text-xs text-gray-500 dark:text-gray-400">sorry for the temporary login,<br />oauth will come soon enough i swear</p> 70 <input 71 type="text" 72 placeholder="Username" 73 value={user} 74 onChange={e => setUser(e.target.value)} 75 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 76 autoComplete="username" 77 /> 78 <input 79 type="password" 80 placeholder="Password" 81 value={password} 82 onChange={e => setPassword(e.target.value)} 83 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 84 autoComplete="current-password" 85 /> 86 <input 87 type="text" 88 placeholder="bsky.social" 89 value={serviceURL} 90 onChange={e => setServiceURL(e.target.value)} 91 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-sm focus:outline-none focus:ring-2 focus:ring-blue-500" 92 /> 93 <button 94 type="submit" 95 className="bg-gray-600 hover:bg-gray-700 text-white rounded px-4 py-2 font-medium text-sm transition-colors" 96 > 97 Log in 98 </button> 99 </form> 100 </div> 101 )} 102 </div> 103 ); 104 } 105 } 106 107 return ( 108 <div className="p-6 bg-gray-100 dark:bg-gray-900 rounded-xl shadow border border-gray-200 dark:border-gray-800 mt-6 mx-4"> 109 {authed ? ( 110 <div className="flex flex-col items-center justify-center text-center"> 111 <p className="text-lg font-semibold mb-6 text-gray-800 dark:text-gray-100">You are logged in!</p> 112 <button 113 onClick={logout} 114 className="bg-gray-600 hover:bg-gray-700 text-white rounded px-6 py-2 font-semibold text-base transition-colors" 115 > 116 Log out 117 </button> 118 </div> 119 ) : ( 120 <form 121 onSubmit={e => { 122 e.preventDefault(); 123 login(user, password, `https://${serviceURL}`); 124 }} 125 className="flex flex-col gap-4" 126 > 127 <p className="text-sm text-gray-500 dark:text-gray-400 mb-2">sorry for the temporary login,<br />oauth will come soon enough i swear</p> 128 <input 129 type="text" 130 placeholder="Username" 131 value={user} 132 onChange={e => setUser(e.target.value)} 133 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 134 autoComplete="username" 135 /> 136 <input 137 type="password" 138 placeholder="Password" 139 value={password} 140 onChange={e => setPassword(e.target.value)} 141 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 142 autoComplete="current-password" 143 /> 144 <input 145 type="text" 146 placeholder="bsky.social" 147 value={serviceURL} 148 onChange={e => setServiceURL(e.target.value)} 149 className="px-3 py-2 rounded border border-gray-300 dark:border-gray-700 bg-white dark:bg-gray-800 text-gray-900 dark:text-gray-100 text-base focus:outline-none focus:ring-2 focus:ring-blue-500" 150 /> 151 <button 152 type="submit" 153 className="bg-gray-600 hover:bg-gray-700 text-white rounded px-6 py-2 font-semibold text-base transition-colors mt-2" 154 > 155 Log in 156 </button> 157 </form> 158 )} 159 </div> 160 ); 161} 162 163export const ProfileThing = () => { 164 const { agent, loading, loginStatus, authed } = useAuth(); 165 const [response, setResponse] = useState<any>(null); 166 167 useEffect(() => { 168 if (loginStatus && agent && !loading && authed) { 169 fetchUser(); 170 } 171 }, [loginStatus, agent, loading, authed]); 172 173 const fetchUser = async () => { 174 if (!agent) { 175 console.error("Agent is null or undefined"); 176 return; 177 } 178 const res = await agent.app.bsky.actor.getProfile({ 179 actor: agent.assertDid, 180 }); 181 setResponse(res.data); 182 }; 183 184 if (!authed) { 185 return ( 186 <div className="inline-block"> 187 <span className="text-gray-100 text-base font-medium px-1.5">Login</span> 188 </div> 189 ); 190 } 191 192 if (!response) { 193 return ( 194 <div className="flex flex-col items-start gap-1.5"> 195 <span className="w-5 h-5 border-2 border-gray-200 dark:border-gray-600 border-t-transparent rounded-full animate-spin inline-block" /> 196 <span className="text-gray-100">Loading... </span> 197 </div> 198 ); 199 } 200 201 return ( 202 <div className="flex flex-row items-start gap-1.5"> 203 <img 204 src={response?.avatar} 205 alt="avatar" 206 className="w-[30px] h-[30px] rounded-full object-cover" 207 /> 208 <div> 209 <div className="text-gray-100 text-xs">{response?.displayName}</div> 210 <div className="text-gray-100 text-xs">@{response?.handle}</div> 211 </div> 212 </div> 213 ); 214};