csr tangled client in solid-js
Something went wrong. Try again.
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359import { ok } from '@atcute/client';import type { OAuthUserAgent } from '@atcute/oauth-browser-client';import type { ResolvedActor } from '@atcute/identity-resolver';import type { Did, Nsid, ResourceUri } from '@atcute/lexicons/syntax';import { ShTangledString, ShTangledFeedStar } from '@atcute/tangled';
import { appviewFallback, listAllAppviewRecords, getDistinctAppviewRecordCount, type AppviewRecordView,} from './appview';import { getRpc } from './client';import { STRING_COLLECTION, STAR_COLLECTION, FEED_COMMENT_COLLECTION } from './constants';import { resolveActor, createAuthRpc } from './identity';import { parseAtUri, type RecordCreationResult } from './records';import { appviewRecordToHydrated } from './hydration';
export interface StringRecord { uri: ResourceUri; cid: string; rkey: string; value: ShTangledString.Main;}
export interface StringComment { uri: ResourceUri; cid?: string; rkey: string; author: ResolvedActor; value: { $type: string; body: string; createdAt: string; subject: { uri: string; cid: string }; replyTo?: { uri: string; cid: string }; };}
export interface StringStarSummary { count: number; isStarred: boolean; currentUserStarRkey?: string;}
const fetchAllStringRecords = async ( actor: ResolvedActor, collection: Nsid,): Promise<Array<{ uri: ResourceUri; cid: string; value: unknown }>> => { const rpc = getRpc(actor.pds); const records: Array<{ uri: ResourceUri; cid: string; value: unknown }> = []; let cursor: string | undefined;
do { const page = await ok( rpc.get('com.atproto.repo.listRecords', { params: { repo: actor.did, collection, limit: 100, cursor, reverse: true, }, }), );
records.push(...page.records); cursor = page.cursor; } while (cursor);
return records;};
const listStringRecordsFromAppview = async (owner: string | ResolvedActor): Promise<StringRecord[]> => { const actor = typeof owner === 'string' ? await resolveActor(owner) : owner; const records = await listAllAppviewRecords<ShTangledString.Main>('sh.tangled.string.listStrings', actor.did);
return records.map((record) => ({ uri: record.uri, cid: record.cid ?? '', rkey: parseAtUri(record.uri).rkey, value: record.value, }));};
const listStringRecordsFromPds = async (owner: string | ResolvedActor): Promise<StringRecord[]> => { const actor = typeof owner === 'string' ? await resolveActor(owner) : owner; const records = await fetchAllStringRecords(actor, STRING_COLLECTION);
return records.map((record) => ({ uri: record.uri, cid: record.cid, rkey: parseAtUri(record.uri).rkey, value: record.value as ShTangledString.Main, }));};
export const listStringRecords = async (owner: string | ResolvedActor): Promise<StringRecord[]> => appviewFallback( () => listStringRecordsFromAppview(owner), () => listStringRecordsFromPds(owner), );
export const getString = async ( ownerIdentifier: string, rkey: string,): Promise<{ owner: ResolvedActor; record: StringRecord }> => { const owner = await resolveActor(ownerIdentifier); const response = await ok( getRpc(owner.pds).get('com.atproto.repo.getRecord', { params: { repo: owner.did, collection: STRING_COLLECTION, rkey, }, }), );
const record: StringRecord = { uri: response.uri, cid: response.cid ?? '', rkey, value: response.value as ShTangledString.Main, }; return { owner, record };};
export const getStringRecord = async (uri: ResourceUri): Promise<{ owner: ResolvedActor; record: StringRecord }> => { const parsed = parseAtUri(uri); const owner = await resolveActor(parsed.did); const response = await ok( getRpc(owner.pds).get('com.atproto.repo.getRecord', { params: { repo: owner.did, collection: STRING_COLLECTION, rkey: parsed.rkey, }, }), );
const record: StringRecord = { uri: response.uri, cid: response.cid ?? '', rkey: parsed.rkey, value: response.value as ShTangledString.Main, }; return { owner, record };};
export const deleteString = async (agent: OAuthUserAgent, rkey: string): Promise<void> => { const rpc = createAuthRpc(agent); await ok( rpc.post('com.atproto.repo.deleteRecord', { input: { repo: agent.sub, collection: STRING_COLLECTION, rkey, }, }), );};
export const updateString = async ( agent: OAuthUserAgent, rkey: string, input: { filename: string; description?: string; contents: string }, swapRecord?: string,): Promise<RecordCreationResult> => { const rpc = createAuthRpc(agent); const record: ShTangledString.Main = { $type: 'sh.tangled.string', filename: input.filename.trim(), description: input.description?.trim() || '', contents: input.contents, createdAt: new Date().toISOString(), };
const result = await ok( rpc.post('com.atproto.repo.putRecord', { input: { repo: agent.sub, collection: STRING_COLLECTION, rkey, record, swapRecord: swapRecord || undefined, }, }), );
return { uri: result.uri, rkey: parseAtUri(result.uri).rkey, };};
export const createString = async ( agent: OAuthUserAgent, input: { filename: string; description?: string; contents: string },): Promise<RecordCreationResult> => { const rpc = createAuthRpc(agent); const record: ShTangledString.Main = { $type: 'sh.tangled.string', filename: input.filename.trim(), description: input.description?.trim() || '', contents: input.contents, createdAt: new Date().toISOString(), };
const result = await ok( rpc.post('com.atproto.repo.createRecord', { input: { repo: agent.sub, collection: STRING_COLLECTION, record, }, }), );
return { uri: result.uri, rkey: parseAtUri(result.uri).rkey, };};
export const listStringComments = async (stringUri: ResourceUri): Promise<StringComment[]> => { const records = await listAllAppviewRecords<any>( 'sh.tangled.feed.listComments', stringUri, ); const comments = await Promise.all(records.map(async (record) => { const hydrated = await appviewRecordToHydrated<any>(record); const val = hydrated.value; const bodyText = (val && typeof val === 'object' && val.body && typeof val.body === 'object' && 'text' in val.body) ? val.body.text : (val && typeof val === 'object' && typeof val.body === 'string' ? val.body : ''); return { ...hydrated, value: { $type: 'sh.tangled.feed.comment', body: bodyText, createdAt: (val && typeof val === 'object' && val.createdAt) || new Date().toISOString(), subject: (val && typeof val === 'object' && val.subject) || { uri: stringUri, cid: '' }, replyTo: val && typeof val === 'object' ? val.replyTo : undefined, } }; })); comments.sort((a, b) => new Date(a.value.createdAt).getTime() - new Date(b.value.createdAt).getTime()); return comments;};
export const createStringComment = async ( agent: OAuthUserAgent, stringUri: ResourceUri, stringCid: string, body: string, replyTo?: { uri: ResourceUri; cid: string },): Promise<RecordCreationResult> => { const rpc = createAuthRpc(agent); const record = { $type: 'sh.tangled.feed.comment', subject: { uri: stringUri, cid: stringCid, }, body: { $type: 'sh.tangled.markup.markdown', text: body.trim(), }, createdAt: new Date().toISOString(), ...(replyTo ? { replyTo: { uri: replyTo.uri, cid: replyTo.cid, } } : {}), };
const result = await ok( rpc.post('com.atproto.repo.createRecord', { input: { repo: agent.sub, collection: FEED_COMMENT_COLLECTION, record, }, }), );
return { uri: result.uri, rkey: parseAtUri(result.uri).rkey, };};
export const getStringStarSummary = async ( stringUri: ResourceUri, viewerDid?: Did | null,): Promise<StringStarSummary> => { const count = await getDistinctAppviewRecordCount('sh.tangled.feed.countStars', stringUri); let currentUserStar: AppviewRecordView<ShTangledFeedStar.Main> | undefined;
if (viewerDid) { const stars = await listAllAppviewRecords<ShTangledFeedStar.Main>('sh.tangled.feed.listStarsBy', viewerDid); currentUserStar = stars.find((star) => { const subject = star.value.subject as { uri?: unknown }; return subject?.uri === stringUri; }); }
return { count, isStarred: !!currentUserStar, currentUserStarRkey: currentUserStar ? parseAtUri(currentUserStar.uri).rkey : undefined, };};
export const createStringStar = async ( agent: OAuthUserAgent, stringUri: ResourceUri,): Promise<RecordCreationResult> => { const rpc = createAuthRpc(agent); const record: ShTangledFeedStar.Main = { $type: 'sh.tangled.feed.star', subject: { $type: 'sh.tangled.feed.star#string', uri: stringUri, }, createdAt: new Date().toISOString(), }; const result = await ok( rpc.post('com.atproto.repo.createRecord', { input: { repo: agent.sub, collection: STAR_COLLECTION, record, }, }), );
return { uri: result.uri, rkey: parseAtUri(result.uri).rkey, };};
export const deleteStringStar = async (agent: OAuthUserAgent, rkey: string): Promise<void> => { const rpc = createAuthRpc(agent); await ok( rpc.post('com.atproto.repo.deleteRecord', { input: { repo: agent.sub, collection: STAR_COLLECTION, rkey, }, }), );};