From d758458e8ee8bac800c6467d9e0a3b5b2f64fa1b Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 20:05:30 +0900 Subject: [PATCH 1/7] Remove unsafe type coercions from posting embed --- src/lib/api/index.ts | 38 ++++++++++++++++++++------------------ 1 file changed, 20 insertions(+), 18 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 1e51c7f255..e393cdccb1 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -13,6 +13,8 @@ import { RichText, } from '@atproto/api' +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 {writePostgateRecord} from '#/state/queries/postgate' @@ -22,8 +24,6 @@ import { threadgateAllowUISettingToAllowRecordValue, writeThreadgateRecord, } from '#/state/queries/threadgate' -import {isNetworkError} from 'lib/strings/errors' -import {shortenLinks, stripInvalidMentions} from 'lib/strings/rich-text-manip' import {LinkMeta} from '../link-meta/link-meta' import {uploadBlob} from './upload-blob' @@ -78,14 +78,16 @@ export async function post(agent: BskyAgent, opts: PostOpts) { rt = stripInvalidMentions(rt) // add quote embed if present + let recordEmbed: AppBskyEmbedRecord.Main | undefined if (opts.quote) { - embed = { + recordEmbed = { $type: 'app.bsky.embed.record', record: { uri: opts.quote.uri, cid: opts.quote.cid, }, - } as AppBskyEmbedRecord.Main + } + embed = recordEmbed } // add image embed if present @@ -111,20 +113,20 @@ export async function post(agent: BskyAgent, opts: PostOpts) { }) } - if (opts.quote) { + if (recordEmbed) { embed = { $type: 'app.bsky.embed.recordWithMedia', - record: embed, + record: recordEmbed, media: { $type: 'app.bsky.embed.images', images, }, - } as AppBskyEmbedRecordWithMedia.Main + } } else { embed = { $type: 'app.bsky.embed.images', images, - } as AppBskyEmbedImages.Main + } } } @@ -140,18 +142,18 @@ export async function post(agent: BskyAgent, opts: PostOpts) { return {lang: caption.lang, file: data.blob} }), ) - if (opts.quote) { + if (recordEmbed) { embed = { $type: 'app.bsky.embed.recordWithMedia', - record: embed, + record: recordEmbed, media: { $type: 'app.bsky.embed.video', video: opts.video.blobRef, alt: opts.video.altText || undefined, captions: captions.length === 0 ? undefined : captions, aspectRatio: opts.video.aspectRatio, - } as AppBskyEmbedVideo.Main, - } as AppBskyEmbedRecordWithMedia.Main + }, + } } else { embed = { $type: 'app.bsky.embed.video', @@ -159,7 +161,7 @@ export async function post(agent: BskyAgent, opts: PostOpts) { alt: opts.video.altText || undefined, captions: captions.length === 0 ? undefined : captions, aspectRatio: opts.video.aspectRatio, - } as AppBskyEmbedVideo.Main + } } } @@ -178,10 +180,10 @@ export async function post(agent: BskyAgent, opts: PostOpts) { thumb = res.data.blob } - if (opts.quote) { + if (recordEmbed) { embed = { $type: 'app.bsky.embed.recordWithMedia', - record: embed, + record: recordEmbed, media: { $type: 'app.bsky.embed.external', external: { @@ -190,8 +192,8 @@ export async function post(agent: BskyAgent, opts: PostOpts) { description: opts.extLink.meta?.description || '', thumb, }, - } as AppBskyEmbedExternal.Main, - } as AppBskyEmbedRecordWithMedia.Main + }, + } } else { embed = { $type: 'app.bsky.embed.external', @@ -201,7 +203,7 @@ export async function post(agent: BskyAgent, opts: PostOpts) { description: opts.extLink.meta?.description || '', thumb, }, - } as AppBskyEmbedExternal.Main + } } } } From c80e8ea2ecca9993df20994cf38cbdff63f0c368 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 20:12:25 +0900 Subject: [PATCH 2/7] Extract resolveEmbed into a separate function --- src/lib/api/index.ts | 235 ++++++++++++++++++++++--------------------- 1 file changed, 120 insertions(+), 115 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index e393cdccb1..b3c2ea533f 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -60,13 +60,6 @@ interface PostOpts { } export async function post(agent: BskyAgent, opts: PostOpts) { - let embed: - | AppBskyEmbedImages.Main - | AppBskyEmbedExternal.Main - | AppBskyEmbedRecord.Main - | AppBskyEmbedVideo.Main - | AppBskyEmbedRecordWithMedia.Main - | undefined let reply let rt = new RichText({text: opts.rawText.trimEnd()}, {cleanNewlines: true}) @@ -77,7 +70,125 @@ export async function post(agent: BskyAgent, opts: PostOpts) { rt = shortenLinks(rt) rt = stripInvalidMentions(rt) - // add quote embed if present + const embed = await resolveEmbed(agent, opts) + + // add replyTo if post is a reply to another post + if (opts.replyTo) { + const replyToUrip = new AtUri(opts.replyTo) + const parentPost = await agent.getPost({ + repo: replyToUrip.host, + rkey: replyToUrip.rkey, + }) + if (parentPost) { + const parentRef = { + uri: parentPost.uri, + cid: parentPost.cid, + } + reply = { + root: parentPost.value.reply?.root || parentRef, + parent: parentRef, + } + } + } + + // set labels + let labels: ComAtprotoLabelDefs.SelfLabels | undefined + if (opts.labels?.length) { + labels = { + $type: 'com.atproto.label.defs#selfLabels', + values: opts.labels.map(val => ({val})), + } + } + + // add top 3 languages from user preferences if langs is provided + let langs = opts.langs + if (opts.langs) { + langs = opts.langs.slice(0, 3) + } + + let res + try { + opts.onStateChange?.('Posting...') + res = await agent.post({ + text: rt.text, + facets: rt.facets, + reply, + embed, + langs, + labels, + }) + } catch (e: any) { + logger.error(`Failed to create post`, { + safeMessage: e.message, + }) + if (isNetworkError(e)) { + throw new Error( + 'Post failed to upload. Please check your Internet connection and try again.', + ) + } else { + throw e + } + } + + if (opts.threadgate.some(tg => tg.type !== 'everybody')) { + try { + // TODO: this needs to be batch-created with the post! + await writeThreadgateRecord({ + agent, + postUri: res.uri, + threadgate: createThreadgateRecord({ + post: res.uri, + allow: threadgateAllowUISettingToAllowRecordValue(opts.threadgate), + }), + }) + } catch (e: any) { + logger.error(`Failed to create threadgate`, { + context: 'composer', + safeMessage: e.message, + }) + throw new Error( + 'Failed to save post interaction settings. Your post was created but users may be able to interact with it.', + ) + } + } + + if ( + opts.postgate.embeddingRules?.length || + opts.postgate.detachedEmbeddingUris?.length + ) { + try { + // TODO: this needs to be batch-created with the post! + await writePostgateRecord({ + agent, + postUri: res.uri, + postgate: { + ...opts.postgate, + post: res.uri, + }, + }) + } catch (e: any) { + logger.error(`Failed to create postgate`, { + context: 'composer', + safeMessage: e.message, + }) + throw new Error( + 'Failed to save post interaction settings. Your post was created but users may be able to interact with it.', + ) + } + } + + return res +} + +async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { + let embed: + | AppBskyEmbedImages.Main + | AppBskyEmbedExternal.Main + | AppBskyEmbedRecord.Main + | AppBskyEmbedVideo.Main + | AppBskyEmbedRecordWithMedia.Main + | undefined + let recordEmbed: AppBskyEmbedRecord.Main | undefined if (opts.quote) { recordEmbed = { @@ -207,111 +318,5 @@ export async function post(agent: BskyAgent, opts: PostOpts) { } } } - - // add replyTo if post is a reply to another post - if (opts.replyTo) { - const replyToUrip = new AtUri(opts.replyTo) - const parentPost = await agent.getPost({ - repo: replyToUrip.host, - rkey: replyToUrip.rkey, - }) - if (parentPost) { - const parentRef = { - uri: parentPost.uri, - cid: parentPost.cid, - } - reply = { - root: parentPost.value.reply?.root || parentRef, - parent: parentRef, - } - } - } - - // set labels - let labels: ComAtprotoLabelDefs.SelfLabels | undefined - if (opts.labels?.length) { - labels = { - $type: 'com.atproto.label.defs#selfLabels', - values: opts.labels.map(val => ({val})), - } - } - - // add top 3 languages from user preferences if langs is provided - let langs = opts.langs - if (opts.langs) { - langs = opts.langs.slice(0, 3) - } - - let res - try { - opts.onStateChange?.('Posting...') - res = await agent.post({ - text: rt.text, - facets: rt.facets, - reply, - embed, - langs, - labels, - }) - } catch (e: any) { - logger.error(`Failed to create post`, { - safeMessage: e.message, - }) - if (isNetworkError(e)) { - throw new Error( - 'Post failed to upload. Please check your Internet connection and try again.', - ) - } else { - throw e - } - } - - if (opts.threadgate.some(tg => tg.type !== 'everybody')) { - try { - // TODO: this needs to be batch-created with the post! - await writeThreadgateRecord({ - agent, - postUri: res.uri, - threadgate: createThreadgateRecord({ - post: res.uri, - allow: threadgateAllowUISettingToAllowRecordValue(opts.threadgate), - }), - }) - } catch (e: any) { - logger.error(`Failed to create threadgate`, { - context: 'composer', - safeMessage: e.message, - }) - throw new Error( - 'Failed to save post interaction settings. Your post was created but users may be able to interact with it.', - ) - } - } - - if ( - opts.postgate.embeddingRules?.length || - opts.postgate.detachedEmbeddingUris?.length - ) { - try { - // TODO: this needs to be batch-created with the post! - await writePostgateRecord({ - agent, - postUri: res.uri, - postgate: { - ...opts.postgate, - post: res.uri, - }, - }) - } catch (e: any) { - logger.error(`Failed to create postgate`, { - context: 'composer', - safeMessage: e.message, - }) - throw new Error( - 'Failed to save post interaction settings. Your post was created but users may be able to interact with it.', - ) - } - } - - return res + return embed } From cc8db4809c39c2fe79ec0d8b516fd5b50d9c59d1 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 20:18:39 +0900 Subject: [PATCH 3/7] Refactor to if-else because these are mutually exclusive --- src/lib/api/index.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index b3c2ea533f..02ac76787c 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -201,7 +201,6 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { embed = recordEmbed } - // add image embed if present if (opts.images?.length) { logger.debug(`Uploading images`, { count: opts.images.length, @@ -239,10 +238,7 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { images, } } - } - - // add video embed if present - if (opts.video) { + } else if (opts.video) { const captions = await Promise.all( opts.video.captions .filter(caption => caption.lang !== '') @@ -274,10 +270,7 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { aspectRatio: opts.video.aspectRatio, } } - } - - // add external embed if present - if (opts.extLink && !opts.images?.length) { + } else if (opts.extLink) { if (opts.extLink.embed) { embed = opts.extLink.embed } else { From e79c2f757e45f50c64b7871c7de408804cac024d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 20:27:12 +0900 Subject: [PATCH 4/7] Refactor resolveEmbed to early returns --- src/lib/api/index.ts | 146 ++++++++++++++++++++++--------------------- 1 file changed, 76 insertions(+), 70 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 02ac76787c..3ae3ec76f7 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,10 +1,7 @@ import { AppBskyEmbedDefs, - AppBskyEmbedExternal, AppBskyEmbedImages, AppBskyEmbedRecord, - AppBskyEmbedRecordWithMedia, - AppBskyEmbedVideo, AppBskyFeedPostgate, AtUri, BlobRef, @@ -181,26 +178,6 @@ export async function post(agent: BskyAgent, opts: PostOpts) { } async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { - let embed: - | AppBskyEmbedImages.Main - | AppBskyEmbedExternal.Main - | AppBskyEmbedRecord.Main - | AppBskyEmbedVideo.Main - | AppBskyEmbedRecordWithMedia.Main - | undefined - - let recordEmbed: AppBskyEmbedRecord.Main | undefined - if (opts.quote) { - recordEmbed = { - $type: 'app.bsky.embed.record', - record: { - uri: opts.quote.uri, - cid: opts.quote.cid, - }, - } - embed = recordEmbed - } - if (opts.images?.length) { logger.debug(`Uploading images`, { count: opts.images.length, @@ -223,22 +200,29 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { }) } - if (recordEmbed) { - embed = { + if (opts.quote) { + return { $type: 'app.bsky.embed.recordWithMedia', - record: recordEmbed, + record: { + $type: 'app.bsky.embed.record', + record: { + uri: opts.quote.uri, + cid: opts.quote.cid, + }, + }, media: { $type: 'app.bsky.embed.images', images, }, } - } else { - embed = { - $type: 'app.bsky.embed.images', - images, - } } - } else if (opts.video) { + return { + $type: 'app.bsky.embed.images', + images, + } + } + + if (opts.video) { const captions = await Promise.all( opts.video.captions .filter(caption => caption.lang !== '') @@ -249,10 +233,16 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { return {lang: caption.lang, file: data.blob} }), ) - if (recordEmbed) { - embed = { + if (opts.quote) { + return { $type: 'app.bsky.embed.recordWithMedia', - record: recordEmbed, + record: { + $type: 'app.bsky.embed.record', + record: { + uri: opts.quote.uri, + cid: opts.quote.cid, + }, + }, media: { $type: 'app.bsky.embed.video', video: opts.video.blobRef, @@ -261,45 +251,41 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { aspectRatio: opts.video.aspectRatio, }, } - } else { - embed = { - $type: 'app.bsky.embed.video', - video: opts.video.blobRef, - alt: opts.video.altText || undefined, - captions: captions.length === 0 ? undefined : captions, - aspectRatio: opts.video.aspectRatio, - } } - } else if (opts.extLink) { + return { + $type: 'app.bsky.embed.video', + video: opts.video.blobRef, + alt: opts.video.altText || undefined, + captions: captions.length === 0 ? undefined : captions, + aspectRatio: opts.video.aspectRatio, + } + } + + if (opts.extLink) { if (opts.extLink.embed) { - embed = opts.extLink.embed - } else { - let thumb - if (opts.extLink.localThumb) { - opts.onStateChange?.('Uploading link thumbnail...') + return opts.extLink.embed + } + let thumb + if (opts.extLink.localThumb) { + opts.onStateChange?.('Uploading link thumbnail...') - const {path, mime} = opts.extLink.localThumb.source - const res = await uploadBlob(agent, path, mime) + const {path, mime} = opts.extLink.localThumb.source + const res = await uploadBlob(agent, path, mime) - thumb = res.data.blob - } + thumb = res.data.blob + } - if (recordEmbed) { - embed = { - $type: 'app.bsky.embed.recordWithMedia', - record: recordEmbed, - media: { - $type: 'app.bsky.embed.external', - external: { - uri: opts.extLink.uri, - title: opts.extLink.meta?.title || '', - description: opts.extLink.meta?.description || '', - thumb, - }, + if (opts.quote) { + return { + $type: 'app.bsky.embed.recordWithMedia', + record: { + $type: 'app.bsky.embed.record', + record: { + uri: opts.quote.uri, + cid: opts.quote.cid, }, - } - } else { - embed = { + }, + media: { $type: 'app.bsky.embed.external', external: { uri: opts.extLink.uri, @@ -307,9 +293,29 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { description: opts.extLink.meta?.description || '', thumb, }, - } + }, } } + return { + $type: 'app.bsky.embed.external', + external: { + uri: opts.extLink.uri, + title: opts.extLink.meta?.title || '', + description: opts.extLink.meta?.description || '', + thumb, + }, + } } - return embed + + if (opts.quote) { + return { + $type: 'app.bsky.embed.record', + record: { + uri: opts.quote.uri, + cid: opts.quote.cid, + }, + } + } + + return undefined } From 0006eb202b0620faa44099fa4b30bd2d6e36806d Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 21:19:00 +0900 Subject: [PATCH 5/7] Separate resolving embed and media --- src/lib/api/index.ts | 131 +++++++++++++++++-------------------------- 1 file changed, 52 insertions(+), 79 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 3ae3ec76f7..2e0c5ac88f 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -1,7 +1,10 @@ import { AppBskyEmbedDefs, + AppBskyEmbedExternal, AppBskyEmbedImages, AppBskyEmbedRecord, + AppBskyEmbedRecordWithMedia, + AppBskyEmbedVideo, AppBskyFeedPostgate, AtUri, BlobRef, @@ -177,51 +180,76 @@ export async function post(agent: BskyAgent, opts: PostOpts) { return res } -async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { +async function resolveEmbed( + agent: BskyAgent, + opts: PostOpts, +): Promise< + | AppBskyEmbedImages.Main + | AppBskyEmbedVideo.Main + | AppBskyEmbedExternal.Main + | AppBskyEmbedRecord.Main + | AppBskyEmbedRecordWithMedia.Main + | undefined +> { + if (opts.extLink?.embed) { + return opts.extLink.embed + } + const media = await resolveMedia(agent, opts) + if (opts.quote) { + const quoteRecord = { + $type: 'app.bsky.embed.record', + record: { + uri: opts.quote.uri, + cid: opts.quote.cid, + }, + } + if (media) { + return { + $type: 'app.bsky.embed.recordWithMedia', + record: quoteRecord, + media, + } + } else { + return quoteRecord + } + } + if (media) { + return media + } + return undefined +} + +async function resolveMedia( + agent: BskyAgent, + opts: PostOpts, +): Promise< + | AppBskyEmbedExternal.Main + | AppBskyEmbedImages.Main + | AppBskyEmbedVideo.Main + | undefined +> { if (opts.images?.length) { logger.debug(`Uploading images`, { count: opts.images.length, }) - const images: AppBskyEmbedImages.Image[] = [] for (const image of opts.images) { opts.onStateChange?.(`Uploading image #${images.length + 1}...`) - logger.debug(`Compressing image`) const {path, width, height, mime} = await compressImage(image) - logger.debug(`Uploading image`) const res = await uploadBlob(agent, path, mime) - images.push({ image: res.data.blob, alt: image.alt, aspectRatio: {width, height}, }) } - - if (opts.quote) { - return { - $type: 'app.bsky.embed.recordWithMedia', - record: { - $type: 'app.bsky.embed.record', - record: { - uri: opts.quote.uri, - cid: opts.quote.cid, - }, - }, - media: { - $type: 'app.bsky.embed.images', - images, - }, - } - } return { $type: 'app.bsky.embed.images', images, } } - if (opts.video) { const captions = await Promise.all( opts.video.captions @@ -233,25 +261,6 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { return {lang: caption.lang, file: data.blob} }), ) - if (opts.quote) { - return { - $type: 'app.bsky.embed.recordWithMedia', - record: { - $type: 'app.bsky.embed.record', - record: { - uri: opts.quote.uri, - cid: opts.quote.cid, - }, - }, - media: { - $type: 'app.bsky.embed.video', - video: opts.video.blobRef, - alt: opts.video.altText || undefined, - captions: captions.length === 0 ? undefined : captions, - aspectRatio: opts.video.aspectRatio, - }, - } - } return { $type: 'app.bsky.embed.video', video: opts.video.blobRef, @@ -260,42 +269,17 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { aspectRatio: opts.video.aspectRatio, } } - if (opts.extLink) { if (opts.extLink.embed) { - return opts.extLink.embed + return undefined } let thumb if (opts.extLink.localThumb) { opts.onStateChange?.('Uploading link thumbnail...') - const {path, mime} = opts.extLink.localThumb.source const res = await uploadBlob(agent, path, mime) - thumb = res.data.blob } - - if (opts.quote) { - return { - $type: 'app.bsky.embed.recordWithMedia', - record: { - $type: 'app.bsky.embed.record', - record: { - uri: opts.quote.uri, - cid: opts.quote.cid, - }, - }, - media: { - $type: 'app.bsky.embed.external', - external: { - uri: opts.extLink.uri, - title: opts.extLink.meta?.title || '', - description: opts.extLink.meta?.description || '', - thumb, - }, - }, - } - } return { $type: 'app.bsky.embed.external', external: { @@ -306,16 +290,5 @@ async function resolveEmbed(agent: BskyAgent, opts: PostOpts) { }, } } - - if (opts.quote) { - return { - $type: 'app.bsky.embed.record', - record: { - uri: opts.quote.uri, - cid: opts.quote.cid, - }, - } - } - return undefined } From c36b4444899759c06b685a0a63708f17ff9ffdc5 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Mon, 30 Sep 2024 21:21:34 +0900 Subject: [PATCH 6/7] Parallelize image upload --- src/lib/api/index.ts | 27 ++++++++++++++------------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index 2e0c5ac88f..f02769daa2 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -232,19 +232,20 @@ async function resolveMedia( logger.debug(`Uploading images`, { count: opts.images.length, }) - const images: AppBskyEmbedImages.Image[] = [] - for (const image of opts.images) { - opts.onStateChange?.(`Uploading image #${images.length + 1}...`) - logger.debug(`Compressing image`) - const {path, width, height, mime} = await compressImage(image) - logger.debug(`Uploading image`) - const res = await uploadBlob(agent, path, mime) - images.push({ - image: res.data.blob, - alt: image.alt, - aspectRatio: {width, height}, - }) - } + opts.onStateChange?.(`Uploading images...`) + const images: AppBskyEmbedImages.Image[] = await Promise.all( + opts.images.map(async (image, i) => { + logger.debug(`Compressing image #${i}`) + const {path, width, height, mime} = await compressImage(image) + logger.debug(`Uploading image #${i}`) + const res = await uploadBlob(agent, path, mime) + return { + image: res.data.blob, + alt: image.alt, + aspectRatio: {width, height}, + } + }), + ) return { $type: 'app.bsky.embed.images', images, From 004b2bd84be5e02bcf6ff1f65a2f8c0d57a5ca43 Mon Sep 17 00:00:00 2001 From: Dan Abramov Date: Tue, 1 Oct 2024 16:22:26 +0900 Subject: [PATCH 7/7] Prioritize not dropping media --- src/lib/api/index.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib/api/index.ts b/src/lib/api/index.ts index f02769daa2..08d4cb9629 100644 --- a/src/lib/api/index.ts +++ b/src/lib/api/index.ts @@ -191,9 +191,6 @@ async function resolveEmbed( | AppBskyEmbedRecordWithMedia.Main | undefined > { - if (opts.extLink?.embed) { - return opts.extLink.embed - } const media = await resolveMedia(agent, opts) if (opts.quote) { const quoteRecord = { @@ -216,6 +213,9 @@ async function resolveEmbed( if (media) { return media } + if (opts.extLink?.embed) { + return opts.extLink.embed + } return undefined }