page.tsx (9717B)
1 "use client" 2 3 import { useCallback, useEffect, useState } from "react" 4 import Link from "next/link" 5 import { Shield, ArrowRight, Loader2, X } from "lucide-react" 6 7 import { Button } from "@/components/ui/button" 8 import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card" 9 import { Input } from "@/components/ui/input" 10 import { Label } from "@/components/ui/label" 11 import { useSession } from "@/contexts/session-context" 12 import RedirectionPageClient from "@/components/redirect-workaround" 13 14 export default function LoginPage() { 15 const [matrixId, setMatrixId] = useState("") 16 const [step, setStep] = useState<"initial" | "login" | "error">("initial") 17 const [password, setPassword] = useState("") 18 const { isLoading, login, discoveryStatus, homeserverUrl, discoverHomeserver, user } = useSession(); 19 const [loginLoading, setLoginLoading] = useState(isLoading) 20 const [errorMessage, setErrorMessage] = useState("") 21 22 useEffect(() => { 23 console.log("Discovery status changed:", discoveryStatus) 24 if (discoveryStatus === "loading") { 25 setLoginLoading(true) 26 } else if (discoveryStatus === "error") { 27 setErrorMessage("Failed to discover homeserver. Please check your Matrix ID format.") 28 setLoginLoading(false) 29 } else if (discoveryStatus === "success") { 30 setErrorMessage("") 31 setLoginLoading(false) 32 } 33 }, [discoveryStatus]) 34 35 const startMatrixAuth = useCallback(() => { 36 if (!matrixId) return 37 setLoginLoading(true) 38 discoverHomeserver(matrixId).then(() => { 39 setStep("login") 40 }).catch((err) => { 41 console.error("Error discovering homeserver:", err) 42 setErrorMessage("Please check your Matrix ID format. It should be like @username:matrix.org") 43 setStep("error") 44 setLoginLoading(false) 45 }); 46 }, [matrixId, discoverHomeserver]); 47 48 const handleSubmit = useCallback(async (e: React.FormEvent) => { 49 e.preventDefault() 50 setErrorMessage("") 51 52 try { 53 await login(matrixId, password) 54 } catch (err) { 55 console.error("Login failed:", err) 56 setErrorMessage("Login failed. Please check your credentials.") 57 setStep("error") 58 } 59 }, [login, matrixId, password]) 60 61 if (user) { 62 return <RedirectionPageClient redirectUrl="/dashboard" replace /> 63 } 64 65 return ( 66 <div className="flex min-h-screen flex-col bg-black text-white"> 67 <header className="border-b border-gray-800 bg-black py-4"> 68 <div className="container flex items-center justify-between px-4 md:px-6"> 69 <Link href="/" className="flex items-center gap-2"> 70 <Shield className="h-6 w-6 text-purple-400" /> 71 <span className="text-xl font-bold tracking-tighter">Draupnir4All</span> 72 </Link> 73 </div> 74 </header> 75 <main className="flex-1 flex items-center justify-center p-4"> 76 <Card className="w-full max-w-md border-gray-800 bg-gray-950"> 77 <CardHeader> 78 <CardTitle className="text-2xl">Log in with Matrix</CardTitle> 79 <CardDescription className="text-gray-400">Access your Draupnir moderation dashboard</CardDescription> 80 </CardHeader> 81 <CardContent className="space-y-4"> 82 {step === "initial" && ( 83 <div className="space-y-4"> 84 <div className="space-y-2"> 85 <Label htmlFor="matrix-id">Matrix ID</Label> 86 <Input 87 id="matrix-id" 88 placeholder="@username:matrix.org" 89 className="bg-gray-900 border-gray-800" 90 value={matrixId} 91 onChange={(e) => setMatrixId(e.target.value)} 92 /> 93 <p className="text-xs text-gray-400">Enter your Matrix ID to begin the authentication process</p> 94 </div> 95 </div> 96 )} 97 98 {step === "login" && ( 99 <form onSubmit={handleSubmit} className="space-y-4"> 100 <div className="space-y-2"> 101 <div className="flex items-center justify-between"> 102 <Label htmlFor="matrix-username">Matrix ID</Label> 103 <Button type="button" variant="ghost" size="sm" className="h-6 px-2 text-gray-400" onClick={() => setStep("initial")}> 104 <X className="h-4 w-4" /> 105 </Button> 106 </div> 107 <div className="flex items-center gap-2 rounded-md bg-gray-900 px-3 py-2 text-sm"> 108 <span>{matrixId}</span> 109 </div> 110 111 {discoveryStatus === "loading" && ( 112 <div className="flex items-center gap-2 text-xs text-gray-400"> 113 <Loader2 className="h-3 w-3 animate-spin" /> 114 Discovering homeserver... 115 </div> 116 )} 117 118 {discoveryStatus === "success" && homeserverUrl && ( 119 <p className="text-xs text-gray-400">Authenticating with {homeserverUrl.replace(/^https?:\/\//, "")}</p> 120 )} 121 122 {discoveryStatus === "error" && ( 123 <p className="text-xs text-red-400">Failed to discover homeserver. Please check your Matrix ID and ensure it's format is "@localpart:example.com".</p> 124 )} 125 </div> 126 127 {discoveryStatus === "success" && ( 128 <div className="space-y-2"> 129 <Label htmlFor="matrix-password">Password</Label> 130 <Input 131 id="matrix-password" 132 type="password" 133 placeholder="Enter your Matrix password" 134 className="bg-gray-900 border-gray-800" 135 value={password} 136 onChange={(e) => setPassword(e.target.value)} 137 required 138 /> 139 </div> 140 )} 141 142 <div className="flex gap-2"> 143 <Button 144 type="submit" 145 className="flex-1 bg-purple-600 text-white hover:bg-purple-700" 146 disabled={isLoading || !password || discoveryStatus !== "success"} 147 > 148 {isLoading ? ( 149 <> 150 <Loader2 className="mr-2 h-4 w-4 animate-spin" /> 151 Authenticating... 152 </> 153 ) : ( 154 "Log in" 155 )} 156 </Button> 157 <Button 158 type="button" 159 variant="outline" 160 className="border-gray-700 text-gray-400 hover:bg-gray-900 hover:text-gray-300" 161 onClick={() => { 162 setStep("initial") 163 setMatrixId("") 164 setPassword("") 165 }} 166 disabled={isLoading || loginLoading} 167 > 168 Cancel 169 </Button> 170 </div> 171 172 <p className="text-xs text-gray-400 text-center"> 173 Your credentials are sent directly to your Matrix homeserver. Draupnir4All only receives a verification token. 174 </p> 175 </form> 176 )} 177 178 {((isLoading || loginLoading) && discoveryStatus !== "loading") && ( 179 <div className="flex flex-col items-center justify-center py-6 space-y-4"> 180 <Loader2 className="h-8 w-8 animate-spin text-purple-400" /> 181 <p className="text-gray-300">Logging in...</p> 182 <p className="text-xs text-gray-400">Accessing your Draupnir4All account</p> 183 </div> 184 )} 185 186 {((isLoading || loginLoading) && discoveryStatus === "loading") && ( 187 <div className="flex flex-col items-center justify-center py-6 space-y-4"> 188 <Loader2 className="h-8 w-8 animate-spin text-purple-400" /> 189 <p className="text-gray-300">Discovering the Homeserver URL...</p> 190 </div> 191 )} 192 {step === "error" && ( 193 <div className="rounded-md bg-red-900/20 border border-red-800 p-4 text-center"> 194 <p className="text-red-300 font-medium">Login Failed</p> 195 <p className="text-sm text-red-200 mt-1">{errorMessage}</p> 196 <Button 197 variant="outline" 198 className="mt-4 border-red-500 text-red-400 hover:bg-red-950 hover:text-red-300" 199 onClick={() => { setStep("initial"); setMatrixId(""); setPassword(""); }} 200 > 201 Try Again 202 </Button> 203 </div> 204 )} 205 </CardContent> 206 <CardFooter className="flex flex-col space-y-4"> 207 {step === "initial" && ( 208 <> 209 <Button 210 className="w-full bg-purple-600 text-white hover:bg-purple-700" 211 onClick={startMatrixAuth} 212 disabled={!matrixId || isLoading || loginLoading} 213 > 214 Continue 215 <ArrowRight className="ml-2 h-4 w-4" /> 216 </Button> 217 <p className="text-center text-sm text-gray-400"> 218 Don't have a Draupnir bot yet?{" "} 219 <Link href="/register" className="text-purple-400 hover:text-purple-300"> 220 Register here 221 </Link> 222 </p> 223 </> 224 )} 225 </CardFooter> 226 </Card> 227 </main> 228 </div > 229 ) 230 }