+19
-6
src/components/repoIcons.tsx
+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
+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
);