your personal website on atproto - mirror blento.app
at fix-cached-posts 133 lines 4.1 kB view raw
1import type { CardDefinition } from '../../types'; 2import { listRecords, getRecord, resolveHandle } from '$lib/atproto'; 3import SembleCollectionCard from './SembleCollectionCard.svelte'; 4import CreateSembleCollectionCardModal from './CreateSembleCollectionCardModal.svelte'; 5 6export type SembleCard = { 7 uri: string; 8 type: 'URL' | 'NOTE'; 9 url?: string; 10 title?: string; 11 description?: string; 12 imageUrl?: string; 13 siteName?: string; 14 text?: string; 15 createdAt?: string; 16}; 17 18export type SembleCollectionData = { 19 name: string; 20 description?: string; 21 cards: SembleCard[]; 22}; 23 24function parseSembleUrl(url: string) { 25 const match = url.match(/^https?:\/\/semble\.so\/profile\/([^/]+)\/collections\/([a-z0-9]+)$/); 26 if (!match) return null; 27 return { handle: match[1], rkey: match[2] }; 28} 29 30async function loadCollectionData( 31 handle: string, 32 collectionRkey: string 33): Promise<SembleCollectionData | undefined> { 34 const did = await resolveHandle({ handle: handle as `${string}.${string}` }); 35 if (!did) return undefined; 36 37 const collectionUri = `at://${did}/network.cosmik.collection/${collectionRkey}`; 38 39 const [collection, allLinks, allCards] = await Promise.all([ 40 getRecord({ 41 did, 42 collection: 'network.cosmik.collection', 43 rkey: collectionRkey 44 }).catch(() => undefined), 45 listRecords({ did, collection: 'network.cosmik.collectionLink', limit: 0 }).catch(() => []), 46 listRecords({ did, collection: 'network.cosmik.card', limit: 0 }).catch(() => []) 47 ]); 48 49 if (!collection) return undefined; 50 51 const linkedCardUris = new Set( 52 allLinks 53 .filter((link: any) => link.value.collection?.uri === collectionUri) 54 .map((link: any) => link.value.card?.uri) 55 ); 56 57 const cards: SembleCard[] = allCards 58 .filter((card: any) => linkedCardUris.has(card.uri)) 59 .map((card: any) => { 60 const v = card.value; 61 const content = v.content; 62 if (v.type === 'URL') { 63 return { 64 uri: card.uri, 65 type: 'URL' as const, 66 url: content?.url, 67 title: content?.metadata?.title, 68 description: content?.metadata?.description, 69 imageUrl: content?.metadata?.imageUrl, 70 siteName: content?.metadata?.siteName, 71 createdAt: v.createdAt 72 }; 73 } 74 return { 75 uri: card.uri, 76 type: 'NOTE' as const, 77 text: content?.text, 78 createdAt: v.createdAt 79 }; 80 }); 81 82 return { 83 name: collection.value.name as string, 84 description: collection.value.description as string | undefined, 85 cards 86 }; 87} 88 89export const SembleCollectionCardDefinition = { 90 type: 'sembleCollection', 91 contentComponent: SembleCollectionCard, 92 creationModalComponent: CreateSembleCollectionCardModal, 93 createNew: (card) => { 94 card.w = 4; 95 card.mobileW = 8; 96 card.h = 4; 97 card.mobileH = 6; 98 }, 99 loadData: async (items) => { 100 const results: Record<string, SembleCollectionData> = {}; 101 for (const item of items) { 102 const handle = item.cardData.handle; 103 const rkey = item.cardData.collectionRkey; 104 if (!handle || !rkey) continue; 105 try { 106 const data = await loadCollectionData(handle, rkey); 107 if (data) results[`${handle}/${rkey}`] = data; 108 } catch { 109 // skip failed fetches 110 } 111 } 112 return results; 113 }, 114 onUrlHandler: (url, item) => { 115 const parsed = parseSembleUrl(url); 116 if (!parsed) return null; 117 item.cardData.handle = parsed.handle; 118 item.cardData.collectionRkey = parsed.rkey; 119 item.cardData.href = url; 120 item.w = 4; 121 item.mobileW = 8; 122 item.h = 4; 123 item.mobileH = 6; 124 return item; 125 }, 126 urlHandlerPriority: 5, 127 minH: 2, 128 129 keywords: ['semble', 'collection', 'bookmarks', 'links', 'cards', 'cosmik'], 130 groups: ['Social'], 131 name: 'Semble Collection', 132 icon: `<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor" class="size-4"><path stroke-linecap="round" stroke-linejoin="round" d="M2.25 12.75V12A2.25 2.25 0 0 1 4.5 9.75h15A2.25 2.25 0 0 1 21.75 12v.75m-8.69-6.44-2.12-2.12a1.5 1.5 0 0 0-1.061-.44H4.5A2.25 2.25 0 0 0 2.25 6v12a2.25 2.25 0 0 0 2.25 2.25h15A2.25 2.25 0 0 0 21.75 18V9a2.25 2.25 0 0 0-2.25-2.25h-5.379a1.5 1.5 0 0 1-1.06-.44Z" /></svg>` 133} as CardDefinition & { type: 'sembleCollection' };