forked from
atpota.to/flushes.app
The 1st decentralized social network for sharing when you're on the toilet. Post a "flush" today! Powered by the AT Protocol.
1'use client';
2
3import React, { useState, useEffect } from 'react';
4import Link from 'next/link';
5import Image from 'next/image';
6import { usePathname } from 'next/navigation';
7import styles from './NavigationBar.module.css';
8import ProfileSearch from './ProfileSearch';
9import ThemeToggle from './ThemeToggle';
10import { useAuth } from '@/lib/auth-context';
11
12export default function NavigationBar() {
13 const pathname = usePathname();
14 const { isAuthenticated, signOut, session } = useAuth();
15 const [handle, setHandle] = useState<string | null>(null);
16
17 // Fetch user's handle when authenticated
18 useEffect(() => {
19 if (isAuthenticated && session?.sub && !handle) {
20 fetchUserHandle(session.sub);
21 }
22 }, [isAuthenticated, session?.sub, handle]);
23
24 const fetchUserHandle = async (did: string) => {
25 try {
26 // Try to resolve DID to handle using PLC directory
27 const plcResponse = await fetch(`https://plc.directory/${did}/data`);
28
29 if (plcResponse.ok) {
30 const plcData = await plcResponse.json();
31 if (plcData.alsoKnownAs && plcData.alsoKnownAs.length > 0) {
32 const handleUrl = plcData.alsoKnownAs[0];
33 if (handleUrl.startsWith('at://')) {
34 const userHandle = handleUrl.substring(5); // Remove 'at://'
35 console.log(`Resolved DID ${did} to handle ${userHandle}`);
36 setHandle(userHandle);
37 return;
38 }
39 }
40 }
41 } catch (error) {
42 console.warn('Failed to resolve handle from PLC directory:', error);
43 }
44
45 // Fallback: try using the profile API
46 try {
47 const response = await fetch('/api/bluesky/profile', {
48 method: 'POST',
49 headers: {
50 'Content-Type': 'application/json'
51 },
52 body: JSON.stringify({
53 accessToken: 'placeholder', // OAuth session handles auth internally
54 dpopToken: 'placeholder', // OAuth session handles auth internally
55 handle: did,
56 pdsEndpoint: null
57 })
58 });
59
60 if (response.ok) {
61 const data = await response.json();
62 if (data.handle && data.handle !== 'unknown') {
63 setHandle(data.handle);
64 }
65 }
66 } catch (error) {
67 console.warn('Failed to fetch handle from profile API:', error);
68 }
69 };
70
71 const handleLogout = async () => {
72 await signOut();
73 setHandle(null); // Clear handle on logout
74 };
75
76 // Check if a link is active
77 const isActive = (path: string) => {
78 return pathname === path;
79 };
80
81 return (
82 <nav className={styles.navbar}>
83 <div className={styles.navStart}>
84 <Link href="/" className={styles.logo}>
85 <Image
86 src="/flushes-logo-horizontal.png"
87 alt="Flushes Logo"
88 width={200}
89 height={53}
90 priority
91 className={styles.logoImage}
92 />
93 </Link>
94
95 <div className={styles.navLinks}>
96 <Link href="/" className={`${styles.navLink} font-medium ${isActive('/') ? styles.active : ''}`}>
97 Feed
98 </Link>
99 <Link href="/stats" className={`${styles.navLink} font-medium ${isActive('/stats') ? styles.active : ''}`}>
100 Stats
101 </Link>
102 <Link href="/shortcut" className={`${styles.navLink} font-medium ${isActive('/shortcut') ? styles.active : ''}`}>
103 Shortcut
104 </Link>
105 <Link href="/about" className={`${styles.navLink} font-medium ${isActive('/about') ? styles.active : ''}`}>
106 About
107 </Link>
108 {isAuthenticated && handle && (
109 <Link
110 href={`/profile/${handle}`}
111 className={`${styles.navLink} font-medium ${pathname.startsWith('/profile/') ? styles.active : ''}`}
112 >
113 Profile
114 </Link>
115 )}
116 </div>
117 </div>
118
119 <div className={styles.secondRow}>
120 <div className={styles.navSearch}>
121 <ProfileSearch />
122 </div>
123
124 <div className={styles.navEnd}>
125 <ThemeToggle />
126
127 {isAuthenticated ? (
128 <button onClick={handleLogout} className={`${styles.authButton} font-medium`}>
129 Logout
130 </button>
131 ) : (
132 <Link href="/auth/login" className={`${styles.authButton} font-medium`}>
133 Login
134 </Link>
135 )}
136 </div>
137 </div>
138 </nav>
139 );
140}