Something went wrong. Try again.
A fork of @slices.network/slices forked from slices.network/slices
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284import { AtProtoClient, NetworkSlicesActorDefsProfileViewBasic, NetworkSlicesActorProfile, NetworkSlicesSlice, NetworkSlicesSliceDefsSliceView, NetworkSlicesSliceDefsSparklinePoint, NetworkSlicesSliceStatsOutput,} from "../client.ts";import { recordBlobToCdnUrl, RecordResponse } from "@slices/client";
async function fetchSparklinesForSlices( client: AtProtoClient, sliceUris: string[]): Promise<Record<string, NetworkSlicesSliceDefsSparklinePoint[]>> { if (sliceUris.length === 0) return {};
try { const sparklinesResponse = await client.network.slices.slice.getSparklines({ slices: sliceUris, duration: "24h", });
// Convert array of sparklineEntry objects to a map const sparklinesMap: Record<string, NetworkSlicesSliceDefsSparklinePoint[]> = {}; for (const entry of sparklinesResponse.sparklines) { sparklinesMap[entry.sliceUri] = entry.points; } return sparklinesMap; } catch (error) { console.warn("Failed to fetch batch sparkline data:", error); } return {};}
async function fetchStatsForSlice( client: AtProtoClient, sliceUri: string): Promise<NetworkSlicesSliceStatsOutput | null> { try { const statsResponse = await client.network.slices.slice.stats({ slice: sliceUri, }); return statsResponse; } catch (error) { console.warn("Failed to fetch stats for slice:", sliceUri, error); } return null;}
async function fetchStatsForSlices( client: AtProtoClient, sliceUris: string[]): Promise<Record<string, NetworkSlicesSliceStatsOutput | null>> { const statsMap: Record<string, NetworkSlicesSliceStatsOutput | null> = {};
// Fetch stats for each slice individually (no batch stats endpoint yet) await Promise.all( sliceUris.map(async (uri) => { statsMap[uri] = await fetchStatsForSlice(client, uri); }) );
return statsMap;}
export async function getSlice( client: AtProtoClient, uri: string): Promise<NetworkSlicesSliceDefsSliceView | null> { try { const sliceRecord = await client.network.slices.slice.getRecord({ uri }); if (!sliceRecord) return null;
const creatorProfile = await getSliceActor(client, sliceRecord.did); if (!creatorProfile) return null;
const [sparklinesMap, stats, requestCount, inviteCount] = await Promise.all([ fetchSparklinesForSlices(client, [uri]), fetchStatsForSlice(client, uri), client.network.slices.waitlist.request.countRecords({ where: { slice: { eq: uri } }, }).then(r => r.count).catch(() => 0), client.network.slices.waitlist.invite.countRecords({ where: { slice: { eq: uri } }, }).then(r => r.count).catch(() => 0), ]);
const sparklineData = sparklinesMap[uri];
return sliceToView(sliceRecord, creatorProfile, sparklineData, stats, requestCount, inviteCount); } catch (error) { console.error("Failed to get slice:", error); return null; }}
export async function getSliceActor( client: AtProtoClient, did: string): Promise<NetworkSlicesActorDefsProfileViewBasic | null> { try { const profileUri = `at://${did}/network.slices.actor.profile/self`; const profileRecord = await client.network.slices.actor.profile.getRecord({ uri: profileUri, }); if (!profileRecord) return null;
// Get the actual handle using getActors const actorsResponse = await client.getActors({ where: { did: { eq: did } }, limit: 1, });
const handle = actorsResponse.actors.length > 0 ? actorsResponse.actors[0].handle ?? did : did; // fallback to DID if no handle found
return actorToView(profileRecord, handle); } catch (error) { console.error("Failed to get slice actor:", error); return null; }}
export function sliceToView( sliceRecord: RecordResponse<NetworkSlicesSlice>, creator: NetworkSlicesActorDefsProfileViewBasic, sparkline?: NetworkSlicesSliceDefsSparklinePoint[], stats?: NetworkSlicesSliceStatsOutput | null, waitlistRequestCount?: number, waitlistInviteCount?: number): NetworkSlicesSliceDefsSliceView { return { uri: sliceRecord.uri, cid: sliceRecord.cid, name: sliceRecord.value.name, domain: sliceRecord.value.domain, creator, createdAt: sliceRecord.value.createdAt, sparkline, indexedRecordCount: stats?.totalRecords || 0, indexedActorCount: stats?.totalActors || 0, indexedCollectionCount: stats?.collectionStats.length || 0, waitlistRequestCount: waitlistRequestCount || 0, waitlistInviteCount: waitlistInviteCount || 0, };}
export function actorToView( profileRecord: RecordResponse<NetworkSlicesActorProfile>, handle: string): NetworkSlicesActorDefsProfileViewBasic { return { did: profileRecord.did, handle, displayName: profileRecord.value.displayName, description: profileRecord.value.description, avatar: profileRecord.value.avatar ? recordBlobToCdnUrl(profileRecord, profileRecord.value.avatar, "avatar") : undefined, };}
export async function getSlicesForActor( client: AtProtoClient, did: string): Promise<NetworkSlicesSliceDefsSliceView[]> { try { const slicesResponse = await client.network.slices.slice.getRecords({ where: { did: { eq: did }, }, sortBy: [{ field: "createdAt", direction: "desc" }], });
// Collect slice URIs for batch sparkline and stats fetch const sliceUris = slicesResponse.records.map((record) => record.uri);
// Fetch sparklines and stats for all slices at once const [sparklinesMap, statsMap] = await Promise.all([ fetchSparklinesForSlices(client, sliceUris), fetchStatsForSlices(client, sliceUris), ]);
const sliceViews: NetworkSlicesSliceDefsSliceView[] = []; for (const sliceRecord of slicesResponse.records) { const creator = await getSliceActor(client, sliceRecord.did); if (creator) { const sparklineData = sparklinesMap[sliceRecord.uri]; const statsData = statsMap[sliceRecord.uri]; sliceViews.push( sliceToView(sliceRecord, creator, sparklineData, statsData) ); } }
return sliceViews; } catch (error) { console.error("Failed to get slices for actor:", error); return []; }}
export async function searchSlices( client: AtProtoClient, query: string, limit = 20): Promise<NetworkSlicesSliceDefsSliceView[]> { try { const slicesResponse = await client.network.slices.slice.getRecords({ where: { name: { contains: query }, }, limit, });
// Collect slice URIs for batch sparkline and stats fetch const sliceUris = slicesResponse.records.map((record) => record.uri);
// Fetch sparklines and stats for all slices at once const [sparklinesMap, statsMap] = await Promise.all([ fetchSparklinesForSlices(client, sliceUris), fetchStatsForSlices(client, sliceUris), ]);
const sliceViews: NetworkSlicesSliceDefsSliceView[] = []; for (const sliceRecord of slicesResponse.records) { const creator = await getSliceActor(client, sliceRecord.did); if (creator) { const sparklineData = sparklinesMap[sliceRecord.uri]; const statsData = statsMap[sliceRecord.uri]; sliceViews.push( sliceToView(sliceRecord, creator, sparklineData, statsData) ); } }
return sliceViews; } catch (error) { console.error("Failed to search slices:", error); return []; }}
export async function getTimeline( client: AtProtoClient, limit = 50): Promise<NetworkSlicesSliceDefsSliceView[]> { try { const slicesResponse = await client.network.slices.slice.getRecords({ limit, sortBy: [{ field: "createdAt", direction: "desc" }], });
// Collect slice URIs for batch sparkline and stats fetch const sliceUris = slicesResponse.records.map((record) => record.uri);
// Fetch sparklines and stats for all slices at once const [sparklinesMap, statsMap] = await Promise.all([ fetchSparklinesForSlices(client, sliceUris), fetchStatsForSlices(client, sliceUris), ]);
const sliceViews: NetworkSlicesSliceDefsSliceView[] = []; for (const sliceRecord of slicesResponse.records) { const creator = await getSliceActor(client, sliceRecord.did); if (creator) { const sparklineData = sparklinesMap[sliceRecord.uri]; const statsData = statsMap[sliceRecord.uri]; sliceViews.push( sliceToView(sliceRecord, creator, sparklineData, statsData) ); } }
return sliceViews; } catch (error) { console.error("Failed to get timeline:", error); return []; }}