From 30bada4acc7a2e7a3e33fcfbd47f0e45a6827a89 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Sun, 6 Oct 2024 19:45:57 +0900 Subject: [PATCH] Inline helper methods --- src/lib/api/index.ts | 11 +- src/lib/api/resolve.ts | 88 ++++++++++--- src/lib/link-meta/bsky.ts | 225 --------------------------------- src/lib/link-meta/link-meta.ts | 12 +- 4 files changed, 76 insertions(+), 260 deletions(-) delete mode 100644 src/lib/link-meta/bsky.ts diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index b553e371b6..4b203d28b6 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -16,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, @@ -29,19 +29,10 @@ import { } from '#/state/queries/threadgate' 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 { draft: ComposerDraft replyTo?: string diff --git a/src/lib/api/resolve.ts b/src/lib/api/resolve.ts index bcb1041187..75081756ed 100644 --- a/src/lib/api/resolve.ts +++ b/src/lib/api/resolve.ts @@ -1,17 +1,20 @@ -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, @@ -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,50 +78,88 @@ export async function resolveLink( uri = await resolveShortLink(uri) } if (isBskyPostUrl(uri)) { - 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)) { - 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)) { - 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)) { - const result = await getStarterPackAsEmbed(agent, fetchDid, uri) + const parsed = parseStarterPackUri(uri) + if (!parsed) { + throw new Error( + 'Unexepectedly 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', }, } } diff --git a/src/lib/link-meta/bsky.ts b/src/lib/link-meta/bsky.ts deleted file mode 100644 index 3d49f5237f..0000000000 --- a/src/lib/link-meta/bsky.ts +++ /dev/null @@ -1,225 +0,0 @@ -import {AppBskyFeedPost, AppBskyGraphStarterpack, BskyAgent} from '@atproto/api' - -import {useFetchDid} from '#/state/queries/handle' -import {useGetPost} from '#/state/queries/post' -import * as apilib from 'lib/api/index' -import { - createStarterPackUri, - parseStarterPackUri, -} from 'lib/strings/starter-pack' -import {ComposerOptsQuote} from 'state/shell/composer' -// import {match as matchRoute} from 'view/routes' -import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers' -import {LikelyType, LinkMeta} from './link-meta' - -// TODO -// import {Home} from 'view/screens/Home' -// import {Search} from 'view/screens/Search' -// import {Notifications} from 'view/screens/Notifications' -// import {PostThread} from 'view/screens/PostThread' -// import {PostUpvotedBy} from 'view/screens/PostUpvotedBy' -// import {PostRepostedBy} from 'view/screens/PostRepostedBy' -// import {Profile} from 'view/screens/Profile' -// import {ProfileFollowers} from 'view/screens/ProfileFollowers' -// import {ProfileFollows} from 'view/screens/ProfileFollows' - -// NOTE -// this is a hack around the lack of hosted social metadata -// remove once that's implemented -// -prf -export async function extractBskyMeta( - agent: BskyAgent, - url: string, -): Promise { - url = convertBskyAppUrlIfNeeded(url) - // const route = matchRoute(url) - let meta: LinkMeta = { - likelyType: LikelyType.AtpData, - url, - // title: route.defaultTitle, - } - - // if (route.Com === Home) { - // meta = { - // ...meta, - // title: 'Bluesky', - // description: 'A new kind of social network', - // } - // } else if (route.Com === Search) { - // meta = { - // ...meta, - // title: 'Search - Bluesky', - // description: 'A new kind of social network', - // } - // } else if (route.Com === Notifications) { - // meta = { - // ...meta, - // title: 'Notifications - Bluesky', - // description: 'A new kind of social network', - // } - // } else if ( - // route.Com === PostThread || - // route.Com === PostUpvotedBy || - // route.Com === PostRepostedBy - // ) { - // // post and post-related screens - // const threadUri = makeRecordUri( - // route.params.name, - // 'app.bsky.feed.post', - // route.params.rkey, - // ) - // const threadView = new PostThreadViewModel(store, { - // uri: threadUri, - // depth: 0, - // }) - // await threadView.setup().catch(_err => undefined) - // const title = [ - // route.Com === PostUpvotedBy - // ? 'Likes on a post by' - // : route.Com === PostRepostedBy - // ? 'Reposts of a post by' - // : 'Post by', - // threadView.thread?.post.author.displayName || - // threadView.thread?.post.author.handle || - // 'a bluesky user', - // ].join(' ') - // meta = { - // ...meta, - // title, - // description: threadView.thread?.postRecord?.text, - // } - // } else if ( - // route.Com === Profile || - // route.Com === ProfileFollowers || - // route.Com === ProfileFollows - // ) { - // // profile and profile-related screens - // const profile = await store.profiles.getProfile(route.params.name) - // if (profile?.data) { - // meta = { - // ...meta, - // title: profile.data.displayName || profile.data.handle, - // description: profile.data.description, - // } - // } - // } - - return meta -} - -export class EmbeddingDisabledError extends Error { - constructor() { - super('Embedding is disabled for this record') - } -} -export async function getPostAsQuote( - getPost: ReturnType, - url: string, -): Promise { - url = convertBskyAppUrlIfNeeded(url) - const [_0, user, _1, rkey] = url.split('/').filter(Boolean) - const uri = makeRecordUri(user, 'app.bsky.feed.post', rkey) - const post = await getPost({uri: uri}) - if (post.viewer?.embeddingDisabled) { - throw new EmbeddingDisabledError() - } - return { - uri: post.uri, - cid: post.cid, - text: AppBskyFeedPost.isRecord(post.record) ? post.record.text : '', - indexedAt: post.indexedAt, - author: post.author, - } -} - -export async function getFeedAsEmbed( - agent: BskyAgent, - fetchDid: ReturnType, - url: string, -): Promise { - url = convertBskyAppUrlIfNeeded(url) - const [_0, handleOrDid, _1, rkey] = url.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 { - isLoading: false, - uri: feed, - meta: { - url: feed, - likelyType: LikelyType.AtpData, - title: res.data.view.displayName, - }, - embed: { - $type: 'app.bsky.embed.record', - record: { - uri: res.data.view.uri, - cid: res.data.view.cid, - }, - }, - } -} - -export async function getListAsEmbed( - agent: BskyAgent, - fetchDid: ReturnType, - url: string, -): Promise { - url = convertBskyAppUrlIfNeeded(url) - const [_0, handleOrDid, _1, rkey] = url.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 { - isLoading: false, - uri: list, - meta: { - url: list, - likelyType: LikelyType.AtpData, - title: res.data.list.name, - }, - embed: { - $type: 'app.bsky.embed.record', - record: { - uri: res.data.list.uri, - cid: res.data.list.cid, - }, - }, - } -} - -export async function getStarterPackAsEmbed( - agent: BskyAgent, - fetchDid: ReturnType, - url: string, -): Promise { - const parsed = parseStarterPackUri(url) - if (!parsed) { - throw new Error( - 'Unexepectedly 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 { - isLoading: false, - uri: starterPack, - meta: { - url: starterPack, - likelyType: LikelyType.AtpData, - // Validation here should never fail - title: AppBskyGraphStarterpack.isRecord(record) - ? record.name - : 'Starter Pack', - }, - embed: { - $type: 'app.bsky.embed.record', - record: { - uri: res.data.starterPack.uri, - cid: res.data.starterPack.cid, - }, - }, - } -} diff --git a/src/lib/link-meta/link-meta.ts b/src/lib/link-meta/link-meta.ts index 6416df2b72..ea9190b6ee 100644 --- a/src/lib/link-meta/link-meta.ts +++ b/src/lib/link-meta/link-meta.ts @@ -1,10 +1,9 @@ import {BskyAgent} from '@atproto/api' -import {LINK_META_PROXY} from 'lib/constants' -import {getGiphyMetaUri} from 'lib/strings/embed-player' -import {parseStarterPackUri} from 'lib/strings/starter-pack' +import {LINK_META_PROXY} from '#/lib/constants' +import {getGiphyMetaUri} from '#/lib/strings/embed-player' +import {parseStarterPackUri} from '#/lib/strings/starter-pack' import {isBskyAppUrl} from '../strings/url-helpers' -import {extractBskyMeta} from './bsky' export enum LikelyType { HTML, @@ -31,7 +30,10 @@ export async function getLinkMeta( timeout = 15e3, ): Promise { if (isBskyAppUrl(url) && !parseStarterPackUri(url)) { - return extractBskyMeta(agent, url) + return { + likelyType: LikelyType.AtpData, + url, + } } let urlp