diff options
Diffstat (limited to 'src/lib/api')
-rw-r--r-- | src/lib/api/index.ts | 45 | ||||
-rw-r--r-- | src/lib/api/resolve.ts | 93 |
2 files changed, 85 insertions, 53 deletions
diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 6edb111e6..4b203d28b 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -4,7 +4,6 @@ import { AppBskyEmbedRecord, AppBskyEmbedRecordWithMedia, AppBskyEmbedVideo, - AppBskyFeedPostgate, AtUri, BlobRef, BskyAgent, @@ -17,7 +16,7 @@ import {QueryClient} from '@tanstack/react-query' import {isNetworkError} from '#/lib/strings/errors' import {shortenLinks, stripInvalidMentions} from '#/lib/strings/rich-text-manip' import {logger} from '#/logger' -import {ComposerImage, compressImage} from '#/state/gallery' +import {compressImage} from '#/state/gallery' import {writePostgateRecord} from '#/state/queries/postgate' import { fetchResolveGifQuery, @@ -25,32 +24,18 @@ import { } from '#/state/queries/resolve-link' import { createThreadgateRecord, - ThreadgateAllowUISetting, threadgateAllowUISettingToAllowRecordValue, writeThreadgateRecord, } from '#/state/queries/threadgate' -import {ComposerState, EmbedDraft} from '#/view/com/composer/state/composer' +import {ComposerDraft, EmbedDraft} from '#/view/com/composer/state/composer' import {createGIFDescription} from '../gif-alt-text' -import {LinkMeta} from '../link-meta/link-meta' import {uploadBlob} from './upload-blob' export {uploadBlob} -export interface ExternalEmbedDraft { - uri: string - isLoading: boolean - meta?: LinkMeta - embed?: AppBskyEmbedRecord.Main - localThumb?: ComposerImage -} - interface PostOpts { - composerState: ComposerState // TODO: Not used yet. - rawText: string + draft: ComposerDraft replyTo?: string - labels?: string[] - threadgate: ThreadgateAllowUISetting[] - postgate: AppBskyFeedPostgate.Record onStateChange?: (state: string) => void langs?: string[] } @@ -60,8 +45,12 @@ export async function post( queryClient: QueryClient, opts: PostOpts, ) { + const draft = opts.draft let reply - let rt = new RichText({text: opts.rawText.trimEnd()}, {cleanNewlines: true}) + let rt = new RichText( + {text: draft.richtext.text.trimEnd()}, + {cleanNewlines: true}, + ) opts.onStateChange?.('Processing...') @@ -73,7 +62,7 @@ export async function post( const embed = await resolveEmbed( agent, queryClient, - opts.composerState, + draft, opts.onStateChange, ) @@ -98,10 +87,10 @@ export async function post( // set labels let labels: ComAtprotoLabelDefs.SelfLabels | undefined - if (opts.labels?.length) { + if (draft.labels.length) { labels = { $type: 'com.atproto.label.defs#selfLabels', - values: opts.labels.map(val => ({val})), + values: draft.labels.map(val => ({val})), } } @@ -135,7 +124,7 @@ export async function post( } } - if (opts.threadgate.some(tg => tg.type !== 'everybody')) { + if (draft.threadgate.some(tg => tg.type !== 'everybody')) { try { // TODO: this needs to be batch-created with the post! await writeThreadgateRecord({ @@ -143,7 +132,7 @@ export async function post( postUri: res.uri, threadgate: createThreadgateRecord({ post: res.uri, - allow: threadgateAllowUISettingToAllowRecordValue(opts.threadgate), + allow: threadgateAllowUISettingToAllowRecordValue(draft.threadgate), }), }) } catch (e: any) { @@ -158,8 +147,8 @@ export async function post( } if ( - opts.postgate.embeddingRules?.length || - opts.postgate.detachedEmbeddingUris?.length + draft.postgate.embeddingRules?.length || + draft.postgate.detachedEmbeddingUris?.length ) { try { // TODO: this needs to be batch-created with the post! @@ -167,7 +156,7 @@ export async function post( agent, postUri: res.uri, postgate: { - ...opts.postgate, + ...draft.postgate, post: res.uri, }, }) @@ -188,7 +177,7 @@ export async function post( async function resolveEmbed( agent: BskyAgent, queryClient: QueryClient, - draft: ComposerState, + draft: ComposerDraft, onStateChange: ((state: string) => void) | undefined, ): Promise< | AppBskyEmbedImages.Main diff --git a/src/lib/api/resolve.ts b/src/lib/api/resolve.ts index 4f409e100..1115a4fb0 100644 --- a/src/lib/api/resolve.ts +++ b/src/lib/api/resolve.ts @@ -1,18 +1,21 @@ -import {AppBskyActorDefs, ComAtprotoRepoStrongRef} from '@atproto/api' +import { + AppBskyActorDefs, + AppBskyFeedPost, + AppBskyGraphStarterpack, + ComAtprotoRepoStrongRef, +} from '@atproto/api' import {AtUri} from '@atproto/api' import {BskyAgent} from '@atproto/api' import {POST_IMG_MAX} from '#/lib/constants' -import { - getFeedAsEmbed, - getListAsEmbed, - getPostAsQuote, - getStarterPackAsEmbed, -} from '#/lib/link-meta/bsky' import {getLinkMeta} from '#/lib/link-meta/link-meta' import {resolveShortLink} from '#/lib/link-meta/resolve-short-link' import {downloadAndResize} from '#/lib/media/manip' import { + createStarterPackUri, + parseStarterPackUri, +} from '#/lib/strings/starter-pack' +import { isBskyCustomFeedUrl, isBskyListUrl, isBskyPostUrl, @@ -24,6 +27,7 @@ import {ComposerImage} from '#/state/gallery' import {createComposerImage} from '#/state/gallery' import {Gif} from '#/state/queries/tenor' import {createGIFDescription} from '../gif-alt-text' +import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers' type ResolvedExternalLink = { type: 'external' @@ -60,6 +64,12 @@ export type ResolvedLink = | ResolvedPostRecord | ResolvedOtherRecord +class EmbeddingDisabledError extends Error { + constructor() { + super('Embedding is disabled for this record') + } +} + export async function resolveLink( agent: BskyAgent, uri: string, @@ -68,55 +78,88 @@ export async function resolveLink( uri = await resolveShortLink(uri) } if (isBskyPostUrl(uri)) { - // TODO: Remove this abstraction. - // TODO: Nice error messages (e.g. EmbeddingDisabledError). - const result = await getPostAsQuote(getPost, uri) + uri = convertBskyAppUrlIfNeeded(uri) + const [_0, user, _1, rkey] = uri.split('/').filter(Boolean) + const recordUri = makeRecordUri(user, 'app.bsky.feed.post', rkey) + const post = await getPost({uri: recordUri}) + if (post.viewer?.embeddingDisabled) { + throw new EmbeddingDisabledError() + } return { type: 'record', record: { - cid: result.cid, - uri: result.uri, + cid: post.cid, + uri: post.uri, }, kind: 'post', - meta: result, + meta: { + text: AppBskyFeedPost.isRecord(post.record) ? post.record.text : '', + indexedAt: post.indexedAt, + author: post.author, + }, } } if (isBskyCustomFeedUrl(uri)) { - // TODO: Remove this abstraction. - const result = await getFeedAsEmbed(agent, fetchDid, uri) + uri = convertBskyAppUrlIfNeeded(uri) + const [_0, handleOrDid, _1, rkey] = uri.split('/').filter(Boolean) + const did = await fetchDid(handleOrDid) + const feed = makeRecordUri(did, 'app.bsky.feed.generator', rkey) + const res = await agent.app.bsky.feed.getFeedGenerator({feed}) return { type: 'record', - record: result.embed!.record, + record: { + uri: res.data.view.uri, + cid: res.data.view.cid, + }, kind: 'other', meta: { // TODO: Include hydrated content instead. - title: result.meta!.title!, + title: res.data.view.displayName, }, } } if (isBskyListUrl(uri)) { - // TODO: Remove this abstraction. - const result = await getListAsEmbed(agent, fetchDid, uri) + uri = convertBskyAppUrlIfNeeded(uri) + const [_0, handleOrDid, _1, rkey] = uri.split('/').filter(Boolean) + const did = await fetchDid(handleOrDid) + const list = makeRecordUri(did, 'app.bsky.graph.list', rkey) + const res = await agent.app.bsky.graph.getList({list}) return { type: 'record', - record: result.embed!.record, + record: { + uri: res.data.list.uri, + cid: res.data.list.cid, + }, kind: 'other', meta: { // TODO: Include hydrated content instead. - title: result.meta!.title!, + title: res.data.list.name, }, } } if (isBskyStartUrl(uri) || isBskyStarterPackUrl(uri)) { - // TODO: Remove this abstraction. - const result = await getStarterPackAsEmbed(agent, fetchDid, uri) + const parsed = parseStarterPackUri(uri) + if (!parsed) { + throw new Error( + 'Unexpectedly called getStarterPackAsEmbed with a non-starterpack url', + ) + } + const did = await fetchDid(parsed.name) + const starterPack = createStarterPackUri({did, rkey: parsed.rkey}) + const res = await agent.app.bsky.graph.getStarterPack({starterPack}) + const record = res.data.starterPack.record return { type: 'record', - record: result.embed!.record, + record: { + uri: res.data.starterPack.uri, + cid: res.data.starterPack.cid, + }, kind: 'other', meta: { // TODO: Include hydrated content instead. - title: result.meta!.title!, + title: AppBskyGraphStarterpack.isRecord(record) + ? record.name + : 'Starter Pack', }, } } |