Fork of atp.tools as a universal profile for people on the ATmosphere

minimum prototype of simplified handle page

Changed files
+52 -27
src
components
routes
+19 -6
src/components/repoIcons.tsx
··· 68 68 return matchingKey ? iconMappings[matchingKey] : null; 69 69 } 70 70 71 + function reverseDomain(collection: string) { 72 + // Split by dot, reverse, join with dot 73 + return collection.split(".").reverse().join("."); 74 + } 75 + 71 76 function RepoIcons({ 72 77 collections, 73 78 did, ··· 79 84 }) { 80 85 let uniqueTypes = Array.from( 81 86 collections 82 - .map((collection) => ({ 83 - id: collection, 84 - Icon: getIconForCollection(collection)?.icon ?? <ShieldQuestionIcon />, 85 - displayName: getIconForCollection(collection)?.label ?? "Unknown", 86 - linkTemplate: getIconForCollection(collection)?.linkTemplate, 87 - })) 87 + .map((collection) => { 88 + const iconObj = getIconForCollection(collection); 89 + return { 90 + id: collection, 91 + Icon: iconObj?.icon ?? <ShieldQuestionIcon />, 92 + displayName: iconObj?.label ?? "Unknown", 93 + // If linkTemplate exists, use it; else generate URL from reversed domain 94 + linkTemplate: 95 + iconObj?.linkTemplate ?? 96 + `https://${reverseDomain(collection)}/{did}`, 97 + }; 98 + }) 88 99 .reduce((acc, current) => { 89 100 if ( 90 101 !Array.from(acc.values()).some( ··· 107 118 <a 108 119 href={linkTemplate?.replace("{handle}", handle).replace("{did}", did)} 109 120 key={id} 121 + target="_blank" 122 + rel="noopener noreferrer" 110 123 > 111 124 <div className="w-8 h-8 p-1 mr-2 rounded-full dark:bg-neutral-800 border border-neutral-500/50 text-white"> 112 125 {typeof Icon === "string" ? (
+33 -21
src/routes/at:/$handle.index.tsx
··· 252 252 <div className="pt-2"> 253 253 <h2 className="text-xl font-bold mb-1">Collections</h2> 254 254 <ul className="list-inside space-y-1"> 255 - {data.collections.map((c, i) => ( 256 - <Fragment key={c}> 257 - {c.split(".").slice(0, 2).join(".") != 258 - (i > 0 && 259 - data.collections[i - 1] 260 - .split(".") 261 - .slice(0, 2) 262 - .join(".")) && ( 263 - <div className="w-min pt-2"> 264 - {c.split(".").slice(0, 2).join(".")}{" "} 265 - </div> 266 - )} 267 - <li> 255 + {Array.from( 256 + data.collections 257 + .reduce((map, c) => { 258 + const prefix = c.split(".").slice(0, 2).join("."); 259 + if (!map.has(prefix)) { 260 + map.set(prefix, prefix); 261 + } 262 + return map; 263 + }, new Map<string, string>()) 264 + .values(), 265 + ).map((prefix) => { 266 + const domain = prefix.split(".").reverse().join("."); 267 + const faviconUrl = `https://www.google.com/s2/favicons?sz=64&domain=${domain}`; 268 + return ( 269 + <li key={prefix} className="flex items-center gap-2"> 270 + <img 271 + src={faviconUrl} 272 + alt={`${domain} favicon`} 273 + className="w-4 h-4 rounded-sm flex-shrink-0" 274 + onError={(e) => { 275 + // Hide image if favicon not found 276 + (e.target as HTMLImageElement).style.display = "none"; 277 + }} 278 + /> 268 279 <Link 269 - className="ml-4 text-blue-600 dark:text-blue-400 hover:no-underline border-b hover:border-border border-transparent w-min" 280 + className="text-blue-600 dark:text-blue-400 hover:no-underline border-b hover:border-border border-transparent flex-1 break-all" 270 281 to="/at:/$handle/$collection" 271 282 params={{ 272 - handle: handle, // Use original handle for navigation consistency 273 - collection: c, 283 + handle: handle, 284 + collection: prefix, 274 285 }} 286 + title={`https://${domain}`} 275 287 > 276 - {c} 288 + {`https://${domain}`} 277 289 </Link> 278 290 </li> 279 - </Fragment> 280 - ))} 291 + ); 292 + })} 281 293 </ul> 282 294 </div> 283 295 )} ··· 309 321 </Accordion> 310 322 )} 311 323 {/* Backlinks Section */} 312 - {data?.did && ( 324 + {/* {data?.did && ( 313 325 <div className="pt-4 pb-8 flex flex-col gap-2"> 314 326 <AllBacklinksViewer aturi={data.did} /> 315 327 </div> 316 - )} 328 + )} */} 317 329 </div> 318 330 </div> 319 331 );