feat: Show number of connected relays in Header Bar (#127)

* feat: add ConnectedRelaysButton component and integrate into TopNavigation
fix: improve loading state management in RelaysPage with timeout
refactor: sanitize relay URLs in RootLayout

* fix: disable debug mode in NostrProvider for production

* feat: add AuthButton component and integrate into TopNavigation

* fix: update header text in RelaysPage and remove 'Add Relay' button label in AddRelaySheet

---------

Co-authored-by: highperfocused <highperfocused@pm.me>
This commit is contained in:
mroxso
2025-05-26 22:43:58 +02:00
committed by GitHub
parent cc94ec5ce6
commit a304032334
6 changed files with 111 additions and 15 deletions

View File

@@ -0,0 +1,37 @@
"use client";
import { Button } from "@/components/ui/button";
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import Link from "next/link";
import { UserIcon } from "lucide-react";
export default function AuthButton() {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="outline" className="flex items-center gap-2">
<UserIcon className="h-[1.2rem] w-[1.2rem]" />
<span className="hidden sm:inline">Account</span>
<span className="sr-only">Account options</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuItem asChild>
<Link href="/login" className="w-full cursor-pointer">
Sign In
</Link>
</DropdownMenuItem>
<DropdownMenuItem asChild>
<Link href="/onboarding" className="w-full cursor-pointer">
Register
</Link>
</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
);
}

View File

@@ -0,0 +1,43 @@
"use client";
import { Button } from "@/components/ui/button";
import React, { useEffect, useState } from 'react';
import Link from "next/link";
import { useNostr } from "nostr-react";
import { Wifi } from "lucide-react";
export default function ConnectedRelaysButton() {
const { connectedRelays } = useNostr();
const [relayCount, setRelayCount] = useState<number>(0);
const [isConnecting, setIsConnecting] = useState<boolean>(true);
useEffect(() => {
// Update relay count when connectedRelays changes
if (connectedRelays && connectedRelays.length > 0) {
// Count only connected relays (status === 1)
const activeRelays = connectedRelays.filter(relay => relay.status === 1).length;
setRelayCount(activeRelays);
// If at least one relay is connected, we're no longer in connecting state
if (activeRelays > 0) {
setIsConnecting(false);
}
}
// Set a timeout to stop showing "Connecting..." after a reasonable time
const timer = setTimeout(() => {
setIsConnecting(false);
}, 5000);
return () => clearTimeout(timer);
}, [connectedRelays]);
return (
<Link href={"/relays"}>
<Button variant={"outline"} className="gap-2">
<Wifi className={`h-4 w-4 ${isConnecting ? 'animate-pulse' : ''}`} />
{isConnecting && relayCount === 0 ? 'Connecting...' : `${relayCount}`}
</Button>
</Link>
);
}

View File

@@ -4,10 +4,11 @@ import { siteConfig } from "@/config/site"
import { useEffect, useState } from "react"
import { TopNavigationItems } from "./TopNavigationItems"
import { DropdownThemeMode } from "./DropdownThemeMode"
import LoginButton from "./LoginButton"
import { AvatarDropdown } from "./AvatarDropdown"
import RegisterButton from "./RegisterButton"
import GitHubButton from "@/components/headerComponents/GitHubButton"
import ConnectedRelaysButton from "@/components/headerComponents/ConnectedRelaysButton"
import AuthButton from "./AuthButton"
import { Button } from "@/components/ui/button"
import { UserIcon } from "lucide-react"
export function TopNavigation() {
const [pubkey, setPubkey] = useState<string | null>(null)
@@ -26,8 +27,13 @@ export function TopNavigation() {
<TopNavigationItems items={siteConfig.mainNav} />
<div className="flex flex-1 items-center justify-end space-x-4">
<nav className="flex items-center space-x-2">
<GitHubButton />
<ConnectedRelaysButton />
<DropdownThemeMode />
{/* Placeholder for auth button to prevent layout shift */}
<Button variant="outline" className="flex items-center gap-2" disabled>
<UserIcon className="h-[1.2rem] w-[1.2rem]" />
<span className="hidden sm:inline">Account</span>
</Button>
</nav>
</div>
</div>
@@ -41,11 +47,9 @@ export function TopNavigation() {
<TopNavigationItems items={siteConfig.mainNav} />
<div className="flex flex-1 items-center justify-end space-x-4">
<nav className="flex items-center space-x-2">
<GitHubButton />
<ConnectedRelaysButton />
<DropdownThemeMode />
{pubkey === null ? <RegisterButton /> : null}
{pubkey === null ? <LoginButton /> : null}
{pubkey !== null ? <AvatarDropdown /> : null}
{pubkey !== null ? <AvatarDropdown /> : <AuthButton />}
</nav>
</div>
</div>