diff --git a/app/setup/page.tsx b/app/setup/page.tsx
index 6f2d199..a13a4a4 100644
--- a/app/setup/page.tsx
+++ b/app/setup/page.tsx
@@ -1,16 +1,10 @@
"use client"
-import { useState, useEffect } from "react"
+import { useEffect } from "react"
import { useRouter } from "next/navigation"
-import { Server, ArrowRight } from "lucide-react"
-import { Button } from "@/components/ui/button"
-import { Input } from "@/components/ui/input"
-import { Card, CardContent, CardDescription, CardFooter, CardHeader, CardTitle } from "@/components/ui/card"
+import Connector from "@/components/connector"
export default function SetupPage() {
- const [relayUrl, setRelayUrl] = useState("")
- const [isLoading, setIsLoading] = useState(false)
- const [error, setError] = useState("")
const router = useRouter()
// Check if user already has a relay configured - if yes, redirect to dashboard
@@ -21,96 +15,7 @@ export default function SetupPage() {
}
}, [router])
- const handleSubmit = (e: React.FormEvent) => {
- e.preventDefault()
- setIsLoading(true)
- setError("")
-
- // Validate the URL format
- try {
- // Simple validation for websocket URL format
- if (!relayUrl.trim()) {
- throw new Error("Please enter a relay URL")
- }
-
- // Check if it's a valid URL
- const url = new URL(relayUrl)
-
- // Check if it's a ws:// or wss:// protocol
- if (!url.protocol.match(/^wss?:$/)) {
- throw new Error("Relay URL must use WebSocket protocol (ws:// or wss://)")
- }
-
- // Save to localStorage
- localStorage.setItem("relayUrl", relayUrl.trim())
-
- // Redirect to dashboard
- router.push("/dashboard")
- } catch (err: any) {
- setError(err.message || "Invalid relay URL format")
- setIsLoading(false)
- }
- }
-
return (
-
-
-
-
-
-
- Connect to Your Relay
-
- Enter your relay's WebSocket URL to connect to your NOSTR relay
-
-
-
-
-
-
-
-
+
)
}
\ No newline at end of file
diff --git a/components/connector.tsx b/components/connector.tsx
new file mode 100644
index 0000000..81c3b69
--- /dev/null
+++ b/components/connector.tsx
@@ -0,0 +1,170 @@
+"use client"
+
+import { useState, useEffect } from "react"
+import { AlertCircle, CheckCircle2 } from "lucide-react"
+import { Input } from "@/components/ui/input"
+import { Button } from "@/components/ui/button"
+import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
+import { Alert, AlertDescription } from "@/components/ui/alert"
+import { useRouter } from "next/navigation"
+
+interface RelayInfo {
+ name?: string
+ description?: string
+ pubkey?: string
+ contact?: string
+ supported_nips?: number[]
+ software?: string
+ version?: string
+ [key: string]: any
+}
+
+export default function Connector() {
+ const [relayUrl, setRelayUrl] = useState("")
+ // const [url, setUrl] = useState("")
+ const [isLoading, setIsLoading] = useState(false)
+ const [error, setError] = useState(null)
+ const [relayInfo, setRelayInfo] = useState(null)
+ const router = useRouter()
+
+ useEffect(() => {
+ const fetchRelayInfo = async () => {
+ if (!relayUrl) {
+ setRelayInfo(null)
+ setError(null)
+ return
+ }
+
+ setIsLoading(true)
+ setError(null)
+
+ try {
+ // Convert WebSocket URL to HTTP/HTTPS
+ const httpUrl = relayUrl.replace(/^wss?:\/\//i, (match) => (match === "ws://" ? "http://" : "https://"))
+
+ const response = await fetch(httpUrl, {
+ headers: {
+ Accept: "application/nostr+json",
+ },
+ })
+
+ if (!response.ok) {
+ throw new Error(`Failed to fetch relay info: ${response.status}`)
+ }
+
+ const data = await response.json()
+ setRelayInfo(data)
+ } catch (err) {
+ setError(err instanceof Error ? err.message : "Failed to fetch relay information")
+ setRelayInfo(null)
+ } finally {
+ setIsLoading(false)
+ }
+ }
+
+ // Debounce the fetch to avoid too many requests
+ const timeoutId = setTimeout(() => {
+ fetchRelayInfo()
+ }, 500)
+
+ return () => clearTimeout(timeoutId)
+ }, [relayUrl])
+
+ const handleConnect = (e: React.FormEvent) => {
+ e.preventDefault()
+ setIsLoading(true)
+ setError("")
+
+ // Validate the URL format
+ try {
+ // Simple validation for websocket URL format
+ if (!relayUrl.trim()) {
+ throw new Error("Please enter a relay URL")
+ }
+
+ // Check if it's a valid URL
+ const url = new URL(relayUrl)
+
+ // Check if it's a ws:// or wss:// protocol
+ if (!url.protocol.match(/^wss?:$/)) {
+ throw new Error("Relay URL must use WebSocket protocol (ws:// or wss://)")
+ }
+
+ // Save to localStorage
+ localStorage.setItem("relayUrl", relayUrl.trim())
+
+ // Redirect to dashboard
+ router.push("/dashboard")
+ } catch (err: any) {
+ setError(err.message || "Invalid relay URL format")
+ setIsLoading(false)
+ }
+ }
+
+ return (
+
+
+ Nostr Relay Connector
+ Enter a WebSocket URL to connect to a Nostr relay
+
+
+
+
+
setRelayUrl(e.target.value)}
+ className="w-full"
+ />
+
Example: wss://relay.damus.io
+
+
+ {isLoading && (
+
+ )}
+
+ {error && (
+
+
+ {error}
+
+ )}
+
+ {relayInfo && (
+
+
+
+ Successfully connected to relay
+
+
+
+
+ Name: {relayInfo.name || "N/A"}
+
+ {relayInfo.description && (
+
+ Description: {relayInfo.description}
+
+ )}
+ {relayInfo.supported_nips && (
+
+ Supported NIPs: {relayInfo.supported_nips.join(", ")}
+
+ )}
+
+
+
+
+ )}
+
+
+ )
+}
diff --git a/components/ui/alert.tsx b/components/ui/alert.tsx
new file mode 100644
index 0000000..1421354
--- /dev/null
+++ b/components/ui/alert.tsx
@@ -0,0 +1,66 @@
+import * as React from "react"
+import { cva, type VariantProps } from "class-variance-authority"
+
+import { cn } from "@/lib/utils"
+
+const alertVariants = cva(
+ "relative w-full rounded-lg border px-4 py-3 text-sm grid has-[>svg]:grid-cols-[calc(var(--spacing)*4)_1fr] grid-cols-[0_1fr] has-[>svg]:gap-x-3 gap-y-0.5 items-start [&>svg]:size-4 [&>svg]:translate-y-0.5 [&>svg]:text-current",
+ {
+ variants: {
+ variant: {
+ default: "bg-card text-card-foreground",
+ destructive:
+ "text-destructive bg-card [&>svg]:text-current *:data-[slot=alert-description]:text-destructive/90",
+ },
+ },
+ defaultVariants: {
+ variant: "default",
+ },
+ }
+)
+
+function Alert({
+ className,
+ variant,
+ ...props
+}: React.ComponentProps<"div"> & VariantProps) {
+ return (
+
+ )
+}
+
+function AlertTitle({ className, ...props }: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+function AlertDescription({
+ className,
+ ...props
+}: React.ComponentProps<"div">) {
+ return (
+
+ )
+}
+
+export { Alert, AlertTitle, AlertDescription }