Write on the margins of the internet. Powered by the AT Protocol.
margin.at
extension
web
atproto
comments
1import { useStore } from "@nanostores/react";
2import {
3 Bell,
4 Bookmark,
5 Folder,
6 Highlighter,
7 Home,
8 LogOut,
9 MessageSquareText,
10 MoreHorizontal,
11 PenSquare,
12 Search,
13 Settings,
14 User,
15 X,
16} from "lucide-react";
17import React, { useEffect, useState } from "react";
18import { Link, useLocation } from "react-router-dom";
19import { getUnreadNotificationCount } from "../../api/client";
20import { $user, logout } from "../../store/auth";
21import { AppleIcon } from "../common/Icons";
22
23export default function MobileNav() {
24 const user = useStore($user);
25 const location = useLocation();
26 const [isMenuOpen, setIsMenuOpen] = useState(false);
27 const [unreadCount, setUnreadCount] = useState(0);
28
29 const isAuthenticated = !!user;
30
31 const isActive = (path: string) => {
32 if (path === "/") return location.pathname === "/";
33 return location.pathname.startsWith(path);
34 };
35
36 useEffect(() => {
37 if (isAuthenticated) {
38 getUnreadNotificationCount()
39 .then((count) => setUnreadCount(count || 0))
40 .catch(() => {});
41 }
42 }, [isAuthenticated]);
43
44 const closeMenu = () => setIsMenuOpen(false);
45
46 return (
47 <>
48 {isMenuOpen && (
49 <div
50 className="fixed inset-0 bg-black/50 z-40 md:hidden"
51 onClick={closeMenu}
52 />
53 )}
54
55 {isMenuOpen && (
56 <div className="fixed bottom-16 left-0 right-0 bg-white dark:bg-surface-900 rounded-t-2xl shadow-2xl z-50 md:hidden animate-slide-up">
57 <div className="p-4 space-y-1">
58 {isAuthenticated && user ? (
59 <>
60 <Link
61 to={`/profile/${user.did}`}
62 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors"
63 onClick={closeMenu}
64 >
65 {user.avatar ? (
66 <img
67 src={user.avatar}
68 alt=""
69 className="w-10 h-10 rounded-full object-cover"
70 />
71 ) : (
72 <div className="w-10 h-10 rounded-full bg-surface-200 dark:bg-surface-700 flex items-center justify-center">
73 <User size={18} className="text-surface-500" />
74 </div>
75 )}
76 <div className="flex flex-col">
77 <span className="font-semibold text-surface-900 dark:text-white">
78 {user.displayName || user.handle}
79 </span>
80 <span className="text-sm text-surface-500">
81 @{user.handle}
82 </span>
83 </div>
84 </Link>
85
86 <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
87
88 <Link
89 to="/annotations"
90 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
91 onClick={closeMenu}
92 >
93 <MessageSquareText size={20} />
94 <span>Annotations</span>
95 </Link>
96
97 <Link
98 to="/highlights"
99 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
100 onClick={closeMenu}
101 >
102 <Highlighter size={20} />
103 <span>Highlights</span>
104 </Link>
105
106 <Link
107 to="/bookmarks"
108 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
109 onClick={closeMenu}
110 >
111 <Bookmark size={20} />
112 <span>Bookmarks</span>
113 </Link>
114
115 <Link
116 to="/collections"
117 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
118 onClick={closeMenu}
119 >
120 <Folder size={20} />
121 <span>Collections</span>
122 </Link>
123
124 <Link
125 to="/settings"
126 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
127 onClick={closeMenu}
128 >
129 <Settings size={20} />
130 <span>Settings</span>
131 </Link>
132
133 <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
134
135 <a
136 href="https://www.icloud.com/shortcuts/1e33ebf52f55431fae1e187cfe9738c3"
137 target="_blank"
138 rel="noopener noreferrer"
139 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
140 onClick={closeMenu}
141 >
142 <AppleIcon size={20} />
143 <span>iOS Shortcut</span>
144 </a>
145
146 <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
147
148 <button
149 className="flex items-center gap-3 p-3 rounded-xl hover:bg-red-50 dark:hover:bg-red-900/20 transition-colors text-red-600 w-full"
150 onClick={() => {
151 logout();
152 closeMenu();
153 }}
154 >
155 <LogOut size={20} />
156 <span>Log Out</span>
157 </button>
158 </>
159 ) : (
160 <>
161 <Link
162 to="/login"
163 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
164 onClick={closeMenu}
165 >
166 <User size={20} />
167 <span>Sign In</span>
168 </Link>
169 <Link
170 to="/collections"
171 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
172 onClick={closeMenu}
173 >
174 <Folder size={20} />
175 <span>Collections</span>
176 </Link>
177 <Link
178 to="/settings"
179 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
180 onClick={closeMenu}
181 >
182 <Settings size={20} />
183 <span>Settings</span>
184 </Link>
185
186 <div className="h-px bg-surface-200 dark:bg-surface-700 my-2" />
187
188 <a
189 href="https://www.icloud.com/shortcuts/1e33ebf52f55431fae1e187cfe9738c3"
190 target="_blank"
191 rel="noopener noreferrer"
192 className="flex items-center gap-3 p-3 rounded-xl hover:bg-surface-100 dark:hover:bg-surface-800 transition-colors text-surface-700 dark:text-surface-200"
193 onClick={closeMenu}
194 >
195 <AppleIcon size={20} />
196 <span>iOS Shortcut</span>
197 </a>
198 </>
199 )}
200 </div>
201 </div>
202 )}
203
204 <nav className="fixed bottom-0 left-0 right-0 h-14 bg-white dark:bg-surface-900 border-t border-surface-200 dark:border-surface-700 flex items-center justify-around px-2 z-50 md:hidden safe-area-bottom">
205 <Link
206 to="/home"
207 className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${
208 isActive("/home")
209 ? "text-primary-600"
210 : "text-surface-500 hover:text-surface-700"
211 }`}
212 onClick={closeMenu}
213 >
214 <Home size={24} strokeWidth={1.5} />
215 </Link>
216
217 <Link
218 to="/search"
219 className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${
220 isActive("/search")
221 ? "text-primary-600"
222 : "text-surface-500 hover:text-surface-700"
223 }`}
224 onClick={closeMenu}
225 >
226 <Search size={24} strokeWidth={1.5} />
227 </Link>
228
229 {isAuthenticated ? (
230 <>
231 <Link
232 to="/new"
233 className="flex items-center justify-center w-12 h-12 rounded-full bg-primary-600 text-white shadow-lg hover:bg-primary-500 transition-colors -mt-4"
234 onClick={closeMenu}
235 >
236 <PenSquare size={20} strokeWidth={2} />
237 </Link>
238
239 <Link
240 to="/notifications"
241 className={`relative flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${
242 isActive("/notifications")
243 ? "text-primary-600"
244 : "text-surface-500 hover:text-surface-700"
245 }`}
246 onClick={closeMenu}
247 >
248 <Bell size={24} strokeWidth={1.5} />
249 {unreadCount > 0 && (
250 <span className="absolute top-2 right-2 w-2 h-2 bg-red-500 rounded-full" />
251 )}
252 </Link>
253 </>
254 ) : (
255 <Link
256 to="/login"
257 className="flex items-center justify-center w-12 h-12 rounded-full bg-primary-600 text-white shadow-lg hover:bg-primary-500 transition-colors -mt-4"
258 onClick={closeMenu}
259 >
260 <User size={20} strokeWidth={2} />
261 </Link>
262 )}
263
264 <button
265 className={`flex flex-col items-center justify-center w-14 h-14 rounded-xl transition-colors ${
266 isMenuOpen
267 ? "text-primary-600"
268 : "text-surface-500 hover:text-surface-700"
269 }`}
270 onClick={() => setIsMenuOpen(!isMenuOpen)}
271 >
272 {isMenuOpen ? (
273 <X size={24} strokeWidth={1.5} />
274 ) : (
275 <MoreHorizontal size={24} strokeWidth={1.5} />
276 )}
277 </button>
278 </nav>
279 </>
280 );
281}