This repository has no description
0

Configure Feed

Select the types of activity you want to include in your feed.

web/lib/components/profile: dedup repos with same repoKey

Signed-off-by: dawn <dawn@tangled.org>

author
dawn
date (Aug 4, 2026, 12:11 AM +0300) commit 64a7400f parent 2434d832 change-id tuqqnmwz
+55 -1
+48
web/src/lib/components/profile/pages.test.ts
··· 144 144 ) 145 145 ).toBe(true); 146 146 }); 147 + 148 + it("deduplicates repos returned by fetchReposPage by repoKey", async () => { 149 + const fetchMock = vi.fn<typeof globalThis.fetch>().mockResolvedValue( 150 + Response.json({ 151 + output: { 152 + items: [ 153 + { 154 + uri: "at://did:plc:alice/sh.tangled.repo/repo-one", 155 + value: { 156 + repoDid: "did:plc:repo-one", 157 + name: "repo-one", 158 + createdAt: "2026-08-01T00:00:00Z" 159 + } 160 + }, 161 + { 162 + uri: "at://did:plc:alice/sh.tangled.repo/repo-one-dup", 163 + value: { 164 + repoDid: "did:plc:repo-one", 165 + name: "repo-one-dup", 166 + createdAt: "2026-08-02T00:00:00Z" 167 + } 168 + }, 169 + { 170 + uri: "at://did:plc:alice/sh.tangled.repo/repo-two", 171 + value: { 172 + repoDid: "did:plc:repo-two", 173 + name: "repo-two", 174 + createdAt: "2026-08-03T00:00:00Z" 175 + } 176 + } 177 + ] 178 + }, 179 + data: {} 180 + }) 181 + ); 182 + const ctx = createBobbinClient({ serviceUrl: "https://bobbin.test", fetch: fetchMock }); 183 + 184 + const result = await fetchReposPage(ctx, { 185 + did: "did:plc:alice", 186 + handle: "alice.test", 187 + viewerDid: "did:plc:viewer" 188 + }); 189 + 190 + expect(result.items).toHaveLength(2); 191 + expect(result.items[0].repoDid).toBe("did:plc:repo-one"); 192 + expect(result.items[0].name).toBe("repo-one-dup"); 193 + expect(result.items[1].repoDid).toBe("did:plc:repo-two"); 194 + }); 147 195 });
+7 -1
web/src/lib/components/profile/pages.ts
··· 26 26 import type * as ShTangledString from "$lib/api/lexicons/types/sh/tangled/string"; 27 27 import type * as ShTangledGraphFollow from "$lib/api/lexicons/types/sh/tangled/graph/follow"; 28 28 import type { RepoCardData, StringCardData, PersonData, VouchData, StarData } from "./types"; 29 + import { repoKey } from "./types"; 29 30 30 31 // same page size as the appview's lists 31 32 export const PROFILE_PAGE_LIMIT = 30; ··· 277 278 enrich: targetAll(descriptors, ["items[].value.repoDid"]), 278 279 ...(viewerDid ? { viewer: viewerDid } : {}) 279 280 }); 281 + const cards = new Map<string, RepoCardData>(); 282 + for (const item of enriched.output.items) { 283 + const card = resolveRepoCard(item, handle, enriched.data); 284 + cards.set(repoKey(card), card); 285 + } 280 286 return { 281 - items: enriched.output.items.map((item) => resolveRepoCard(item, handle, enriched.data)), 287 + items: Array.from(cards.values()), 282 288 cursor: enriched.output.cursor 283 289 }; 284 290 }