Write on the margins of the internet. Powered by the AT Protocol.
at main 226 lines 6.9 kB view raw
1import { useState, useEffect } from "react"; 2import { Link } from "react-router-dom"; 3import { ExternalLink, Sun, Moon, Monitor } from "lucide-react"; 4import { 5 SiFirefox, 6 SiGooglechrome, 7 SiGithub, 8 SiBluesky, 9 SiApple, 10 SiKofi, 11 SiDiscord, 12} from "react-icons/si"; 13import { FaEdge } from "react-icons/fa"; 14import { useAuth } from "../context/AuthContext"; 15import { useTheme } from "../context/ThemeContext"; 16import { getTrendingTags } from "../api/client"; 17 18const isFirefox = 19 typeof navigator !== "undefined" && /Firefox/i.test(navigator.userAgent); 20const isEdge = 21 typeof navigator !== "undefined" && /Edg/i.test(navigator.userAgent); 22const isMobileSafari = 23 typeof navigator !== "undefined" && 24 /iPhone|iPad|iPod/.test(navigator.userAgent) && 25 /Safari/.test(navigator.userAgent) && 26 !/CriOS|FxiOS|OPiOS|EdgiOS/.test(navigator.userAgent); 27 28function getExtensionInfo() { 29 if (isMobileSafari) { 30 return { 31 url: "https://margin.at/soon", 32 icon: SiApple, 33 name: "iOS", 34 label: "Coming Soon", 35 }; 36 } 37 if (isFirefox) { 38 return { 39 url: "https://addons.mozilla.org/en-US/firefox/addon/margin/", 40 icon: SiFirefox, 41 name: "Firefox", 42 label: "Install for Firefox", 43 }; 44 } 45 if (isEdge) { 46 return { 47 url: "https://microsoftedge.microsoft.com/addons/detail/margin/nfjnmllpdgcdnhmmggjihjbidmeadddn", 48 icon: FaEdge, 49 name: "Edge", 50 label: "Install for Edge", 51 }; 52 } 53 return { 54 url: "https://chromewebstore.google.com/detail/margin/cgpmbiiagnehkikhcbnhiagfomajncpa/", 55 icon: SiGooglechrome, 56 name: "Chrome", 57 label: "Install for Chrome", 58 }; 59} 60 61export default function RightSidebar() { 62 const { theme, setTheme } = useTheme(); 63 const { isAuthenticated } = useAuth(); 64 const ext = getExtensionInfo(); 65 const ExtIcon = ext.icon; 66 const [trendingTags, setTrendingTags] = useState([]); 67 const [loading, setLoading] = useState(true); 68 69 useEffect(() => { 70 getTrendingTags() 71 .then((tags) => setTrendingTags(tags)) 72 .catch((err) => console.error("Failed to fetch trending tags:", err)) 73 .finally(() => setLoading(false)); 74 }, []); 75 76 return ( 77 <aside className="right-sidebar"> 78 <div className="right-section"> 79 <h3 className="right-section-title"> 80 {isMobileSafari ? "Save from Safari" : "Get the Extension"} 81 </h3> 82 <p className="right-section-desc"> 83 {isMobileSafari 84 ? "Bookmark pages using Safari's share sheet" 85 : "Annotate, highlight, and bookmark any webpage"} 86 </p> 87 <a 88 href={ext.url} 89 target="_blank" 90 rel="noopener noreferrer" 91 className="right-extension-btn" 92 > 93 <ExtIcon size={18} /> 94 {ext.label} 95 <ExternalLink size={14} /> 96 </a> 97 </div> 98 99 {isAuthenticated ? ( 100 <div className="right-section"> 101 <h3 className="right-section-title">Trending Tags</h3> 102 <div className="right-links"> 103 {loading ? ( 104 <span className="right-section-desc">Loading...</span> 105 ) : trendingTags.length > 0 ? ( 106 trendingTags.map(({ tag, count }) => ( 107 <Link 108 key={tag} 109 to={`/?tag=${encodeURIComponent(tag)}`} 110 className="right-link" 111 > 112 <span>#{tag}</span> 113 <span style={{ fontSize: "0.75rem", opacity: 0.6 }}> 114 {count} 115 </span> 116 </Link> 117 )) 118 ) : ( 119 <span className="right-section-desc">No trending tags yet</span> 120 )} 121 </div> 122 </div> 123 ) : ( 124 <div className="right-section"> 125 <h3 className="right-section-title">Explore</h3> 126 <nav className="right-links"> 127 <Link to="/url" className="right-link"> 128 Browse by URL 129 </Link> 130 </nav> 131 </div> 132 )} 133 134 <div className="right-section"> 135 <h3 className="right-section-title">Resources</h3> 136 <nav className="right-links"> 137 <a 138 href="https://github.com/margin-at/margin" 139 target="_blank" 140 rel="noopener noreferrer" 141 className="right-link" 142 > 143 <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> 144 <SiGithub size={16} /> 145 GitHub 146 </div> 147 <ExternalLink size={12} /> 148 </a> 149 <a 150 href="https://tangled.org/margin.at/margin" 151 target="_blank" 152 rel="noopener noreferrer" 153 className="right-link" 154 > 155 <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> 156 <div className="tangled-icon" /> 157 Tangled 158 </div> 159 <ExternalLink size={12} /> 160 </a> 161 <a 162 href="https://bsky.app/profile/margin.at" 163 target="_blank" 164 rel="noopener noreferrer" 165 className="right-link" 166 > 167 <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> 168 <SiBluesky size={16} /> 169 Bluesky 170 </div> 171 <ExternalLink size={12} /> 172 </a> 173 <a 174 href="https://discord.gg/ZQbkGqwzBH" 175 target="_blank" 176 rel="noopener noreferrer" 177 className="right-link" 178 > 179 <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> 180 <SiDiscord size={16} /> 181 Discord 182 </div> 183 <ExternalLink size={12} /> 184 </a> 185 <a 186 href="https://ko-fi.com/scan" 187 target="_blank" 188 rel="noopener noreferrer" 189 className="right-link" 190 > 191 <div style={{ display: "flex", alignItems: "center", gap: "8px" }}> 192 <SiKofi size={16} /> 193 Donate 194 </div> 195 <ExternalLink size={12} /> 196 </a> 197 </nav> 198 </div> 199 200 <div className="right-footer"> 201 <div className="footer-links"> 202 <Link to="/privacy">Privacy</Link> 203 <span>·</span> 204 <Link to="/terms">Terms</Link> 205 </div> 206 <button 207 onClick={() => { 208 const next = 209 theme === "system" 210 ? "light" 211 : theme === "light" 212 ? "dark" 213 : "system"; 214 setTheme(next); 215 }} 216 className="theme-toggle-mini" 217 title={`Theme: ${theme}`} 218 > 219 {theme === "system" && <Monitor size={14} />} 220 {theme === "light" && <Sun size={14} />} 221 {theme === "dark" && <Moon size={14} />} 222 </button> 223 </div> 224 </aside> 225 ); 226}