Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import {
2 BellIcon,
3 GlobeAltIcon as GlobeOutline,
4 HomeIcon,
5 MagnifyingGlassIcon
6} from "@heroicons/react/24/outline";
7import {
8 BellIcon as BellIconSolid,
9 GlobeAltIcon as GlobeSolid,
10 HomeIcon as HomeIconSolid
11} from "@heroicons/react/24/solid";
12import getAvatar from "@hey/helpers/getAvatar";
13import type { MouseEvent, ReactNode } from "react";
14import { Link, useLocation } from "react-router";
15import { Image } from "@/components/Shared/UI";
16import useHasNewNotifications from "@/hooks/useHasNewNotifications";
17import { useMobileDrawerModalStore } from "@/store/non-persisted/modal/useMobileDrawerModalStore";
18import { useAccountStore } from "@/store/persisted/useAccountStore";
19import MobileDrawerMenu from "./MobileDrawerMenu";
20
21interface NavigationItemProps {
22 path: string;
23 label: string;
24 outline: ReactNode;
25 solid: ReactNode;
26 isActive: boolean;
27 onClick?: (e: MouseEvent) => void;
28 showIndicator?: boolean;
29}
30
31const NavigationItem = ({
32 path,
33 label,
34 outline,
35 solid,
36 isActive,
37 onClick,
38 showIndicator
39}: NavigationItemProps) => (
40 <Link
41 aria-label={label}
42 className="relative mx-auto my-3"
43 onClick={onClick}
44 to={path}
45 >
46 {isActive ? solid : outline}
47 {showIndicator && (
48 <span className="-right-1 -top-1 absolute size-2 rounded-full bg-red-500" />
49 )}
50 </Link>
51);
52
53const BottomNavigation = () => {
54 const { pathname } = useLocation();
55 const { currentAccount } = useAccountStore();
56 const { show: showMobileDrawer, setShow: setShowMobileDrawer } =
57 useMobileDrawerModalStore();
58 const hasNewNotifications = useHasNewNotifications();
59
60 const handleAccountClick = () => setShowMobileDrawer(true);
61
62 const handleHomClick = (path: string, e: MouseEvent) => {
63 if (path === "/" && pathname === "/") {
64 e.preventDefault();
65 window.scrollTo(0, 0);
66 }
67 };
68
69 const navigationItems = [
70 {
71 label: "Home",
72 outline: <HomeIcon className="size-6" />,
73 path: "/",
74 solid: <HomeIconSolid className="size-6" />
75 },
76 {
77 label: "Search",
78 outline: <MagnifyingGlassIcon className="size-6" />,
79 path: "/search",
80 solid: <MagnifyingGlassIcon className="size-6" />
81 },
82 {
83 label: "Explore",
84 outline: <GlobeOutline className="size-6" />,
85 path: "/explore",
86 solid: <GlobeSolid className="size-6" />
87 },
88 {
89 label: "Notifications",
90 outline: <BellIcon className="size-6" />,
91 path: "/notifications",
92 solid: <BellIconSolid className="size-6" />
93 }
94 ];
95
96 return (
97 <nav className="fixed inset-x-0 bottom-0 z-[5] border-gray-200 border-t bg-white pb-safe md:hidden dark:border-gray-800 dark:bg-black">
98 {showMobileDrawer && <MobileDrawerMenu />}
99 <div className="flex justify-between">
100 {navigationItems.map(({ path, label, outline, solid }) => (
101 <NavigationItem
102 isActive={pathname === path}
103 key={path}
104 label={label}
105 onClick={(e) => handleHomClick(path, e)}
106 outline={outline}
107 path={path}
108 showIndicator={hasNewNotifications && path === "/notifications"}
109 solid={solid}
110 />
111 ))}
112 {currentAccount && (
113 <button
114 aria-label="Your account"
115 className="m-auto h-fit"
116 onClick={handleAccountClick}
117 type="button"
118 >
119 <Image
120 alt={currentAccount.address}
121 className="m-0.5 size-6 rounded-full border border-gray-200 dark:border-gray-700"
122 src={getAvatar(currentAccount)}
123 />
124 </button>
125 )}
126 </div>
127 </nav>
128 );
129};
130
131export default BottomNavigation;