diff options
author | dan <dan.abramov@gmail.com> | 2024-10-08 08:58:42 +0900 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-10-07 16:58:42 -0700 |
commit | dd8be2e939d2879e2bb23b2ccd843a034d19b8dd (patch) | |
tree | fe5ec58ce2fdc72e0112993e85d4efdc265eca25 /src/lib/api/resolve.ts | |
parent | e564fe9cc6abd69f4dfc0cef968dc38741cfc759 (diff) | |
download | voidsky-dd8be2e939d2879e2bb23b2ccd843a034d19b8dd.tar.zst |
Use composer state as source of truth for embeds/links on publish (#5606)
Co-authored-by: Mary <git@mary.my.id> Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/lib/api/resolve.ts')
-rw-r--r-- | src/lib/api/resolve.ts | 161 |
1 files changed, 161 insertions, 0 deletions
diff --git a/src/lib/api/resolve.ts b/src/lib/api/resolve.ts new file mode 100644 index 000000000..a97a3f31c --- /dev/null +++ b/src/lib/api/resolve.ts @@ -0,0 +1,161 @@ +import {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 { + isBskyCustomFeedUrl, + isBskyListUrl, + isBskyPostUrl, + isBskyStarterPackUrl, + isBskyStartUrl, + isShortLink, +} from '#/lib/strings/url-helpers' +import {ComposerImage} from '#/state/gallery' +import {createComposerImage} from '#/state/gallery' +import {Gif} from '#/state/queries/tenor' +import {createGIFDescription} from '../gif-alt-text' + +type ResolvedExternalLink = { + type: 'external' + uri: string + title: string + description: string + thumb: ComposerImage | undefined +} + +type ResolvedRecord = { + type: 'record' + record: ComAtprotoRepoStrongRef.Main +} + +type ResolvedLink = ResolvedExternalLink | ResolvedRecord + +export async function resolveLink( + agent: BskyAgent, + uri: string, +): Promise<ResolvedLink> { + if (isShortLink(uri)) { + uri = await resolveShortLink(uri) + } + if (isBskyPostUrl(uri)) { + // TODO: Remove this abstraction. + // TODO: Nice error messages (e.g. EmbeddingDisabledError). + const result = await getPostAsQuote(getPost, uri) + return { + type: 'record', + record: { + cid: result.cid, + uri: result.uri, + }, + } + } + if (isBskyCustomFeedUrl(uri)) { + // TODO: Remove this abstraction. + const result = await getFeedAsEmbed(agent, fetchDid, uri) + return { + type: 'record', + record: result.embed!.record, // TODO: Fix types. + } + } + if (isBskyListUrl(uri)) { + // TODO: Remove this abstraction. + const result = await getListAsEmbed(agent, fetchDid, uri) + return { + type: 'record', + record: result.embed!.record, // TODO: Fix types. + } + } + if (isBskyStartUrl(uri) || isBskyStarterPackUrl(uri)) { + // TODO: Remove this abstraction. + const result = await getStarterPackAsEmbed(agent, fetchDid, uri) + return { + type: 'record', + record: result.embed!.record, // TODO: Fix types. + } + } + return resolveExternal(agent, uri) + + // Forked from useGetPost. TODO: move into RQ. + async function getPost({uri}: {uri: string}) { + const urip = new AtUri(uri) + if (!urip.host.startsWith('did:')) { + const res = await agent.resolveHandle({ + handle: urip.host, + }) + urip.host = res.data.did + } + const res = await agent.getPosts({ + uris: [urip.toString()], + }) + if (res.success && res.data.posts[0]) { + return res.data.posts[0] + } + throw new Error('getPost: post not found') + } + + // Forked from useFetchDid. TODO: move into RQ. + async function fetchDid(handleOrDid: string) { + let identifier = handleOrDid + if (!identifier.startsWith('did:')) { + const res = await agent.resolveHandle({handle: identifier}) + identifier = res.data.did + } + return identifier + } +} + +export async function resolveGif( + agent: BskyAgent, + gif: Gif, +): Promise<ResolvedExternalLink> { + const uri = `${gif.media_formats.gif.url}?hh=${gif.media_formats.gif.dims[1]}&ww=${gif.media_formats.gif.dims[0]}` + return { + type: 'external', + uri, + title: gif.content_description, + description: createGIFDescription(gif.content_description), + thumb: await imageToThumb(gif.media_formats.preview.url), + } +} + +async function resolveExternal( + agent: BskyAgent, + uri: string, +): Promise<ResolvedExternalLink> { + const result = await getLinkMeta(agent, uri) + return { + type: 'external', + uri: result.url, + title: result.title ?? '', + description: result.description ?? '', + thumb: result.image ? await imageToThumb(result.image) : undefined, + } +} + +async function imageToThumb( + imageUri: string, +): Promise<ComposerImage | undefined> { + try { + const img = await downloadAndResize({ + uri: imageUri, + width: POST_IMG_MAX.width, + height: POST_IMG_MAX.height, + mode: 'contain', + maxSize: POST_IMG_MAX.size, + timeout: 15e3, + }) + if (img) { + return await createComposerImage(img) + } + } catch {} +} |