diff --git a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx index 6d58f0669..f5f336343 100644 --- a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx +++ b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx @@ -392,6 +392,29 @@ let PostDropdownMenuItems = ({ }) }, [isPinned, pinPostMutate, postCid, postUri]) + // HUGE thanks to juli.ee and mary.my.id for pdsls and atcute/identity-resolver respectively + // as a reference for this code + const fetchPds = useCallback(async () => { + const did = post.author.did + let doc + if (did.startsWith('did:web:')) { + const hostPart = did.slice(8) + doc = await fetch(`https://${hostPart}/.well-known/did.json`) + .then(r => r.json()) + .catch(() => null) + } else if (did.startsWith('did:plc')) { + doc = await fetch(`https://plc.directory/${encodeURIComponent(did)}`) + .then(r => r.json()) + .catch(() => null) + } + if (!doc) return null + const pds = doc.service.find( + (service: {type: string}) => service.type === 'AtprotoPersonalDataServer', + ) + if (!pds) return null + return pds.serviceEndpoint + }, [post]) + const onPressDownloadVideo = useCallback(async () => { if (post.embed?.$type !== 'app.bsky.embed.video#view') return const video = post.embed as AppBskyEmbedVideo.View @@ -409,7 +432,7 @@ let PostDropdownMenuItems = ({ if (success) Toast.show('Video downloaded', 'check') else Toast.show('Failed to download video', 'xmark') - }, [post]) + }, [post, fetchPds]) const onPressDownloadGif = useCallback(async () => { if (post.embed?.$type !== 'app.bsky.embed.external#view') return -- 2.51.2 From ade32b44ab1aec8cd3e24ef809af49856448917a Mon Sep 17 00:00:00 2001 From: m-doescode <80221594+m-doescode@users.noreply.github.com> Date: Wed, 30 Apr 2025 14:19:53 +0200 Subject: [PATCH 2/3] refactored pds url resolution into its own file, and made use of it for video downloads --- .../util/forms/PostDropdownBtnMenuItems.tsx | 25 +------------------ 1 file changed, 1 insertion(+), 24 deletions(-) diff --git a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx index f5f336343..6d58f0669 100644 --- a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx +++ b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx @@ -392,29 +392,6 @@ let PostDropdownMenuItems = ({ }) }, [isPinned, pinPostMutate, postCid, postUri]) - // HUGE thanks to juli.ee and mary.my.id for pdsls and atcute/identity-resolver respectively - // as a reference for this code - const fetchPds = useCallback(async () => { - const did = post.author.did - let doc - if (did.startsWith('did:web:')) { - const hostPart = did.slice(8) - doc = await fetch(`https://${hostPart}/.well-known/did.json`) - .then(r => r.json()) - .catch(() => null) - } else if (did.startsWith('did:plc')) { - doc = await fetch(`https://plc.directory/${encodeURIComponent(did)}`) - .then(r => r.json()) - .catch(() => null) - } - if (!doc) return null - const pds = doc.service.find( - (service: {type: string}) => service.type === 'AtprotoPersonalDataServer', - ) - if (!pds) return null - return pds.serviceEndpoint - }, [post]) - const onPressDownloadVideo = useCallback(async () => { if (post.embed?.$type !== 'app.bsky.embed.video#view') return const video = post.embed as AppBskyEmbedVideo.View @@ -432,7 +409,7 @@ let PostDropdownMenuItems = ({ if (success) Toast.show('Video downloaded', 'check') else Toast.show('Failed to download video', 'xmark') - }, [post, fetchPds]) + }, [post]) const onPressDownloadGif = useCallback(async () => { if (post.embed?.$type !== 'app.bsky.embed.external#view') return -- 2.51.2 From 5c3ed005ce92f4631a88f70a9e0a2a65ea8d67d9 Mon Sep 17 00:00:00 2001 From: m-doescode <80221594+m-doescode@users.noreply.github.com> Date: Wed, 21 May 2025 18:13:07 +0200 Subject: [PATCH 3/3] fix: video and gif downloads not working for quote posts --- .../util/forms/PostDropdownBtnMenuItems.tsx | 48 +++++++++++++------ 1 file changed, 34 insertions(+), 14 deletions(-) diff --git a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx index 6d58f0669..8bd138d54 100644 --- a/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx +++ b/src/view/com/util/forms/PostDropdownBtnMenuItems.tsx @@ -8,6 +8,7 @@ import { import * as Clipboard from 'expo-clipboard' import { type AppBskyEmbedExternal, + type AppBskyEmbedRecordWithMedia, type AppBskyEmbedVideo, type AppBskyFeedDefs, AppBskyFeedPost, @@ -392,11 +393,32 @@ let PostDropdownMenuItems = ({ }) }, [isPinned, pinPostMutate, postCid, postUri]) + const videoEmbed: AppBskyEmbedVideo.View | undefined = React.useMemo(() => { + if (post.embed?.$type === 'app.bsky.embed.video#view') + return post.embed as AppBskyEmbedVideo.View + if (post.embed?.$type === 'app.bsky.embed.recordWithMedia#view') { + const embed = post.embed as AppBskyEmbedRecordWithMedia.View | undefined + if (embed?.media.$type === 'app.bsky.embed.video#view') + return embed?.media as AppBskyEmbedVideo.View + } + return undefined + }, [post]) + + const gifEmbed: AppBskyEmbedExternal.View | undefined = React.useMemo(() => { + if (post.embed?.$type === 'app.bsky.embed.external#view') + return post.embed as AppBskyEmbedExternal.View + if (post.embed?.$type === 'app.bsky.embed.recordWithMedia#view') { + const embed = post.embed as AppBskyEmbedRecordWithMedia.View | undefined + if (embed?.media.$type === 'app.bsky.embed.external#view') + return embed?.media as AppBskyEmbedExternal.View + } + return undefined + }, [post]) + const onPressDownloadVideo = useCallback(async () => { - if (post.embed?.$type !== 'app.bsky.embed.video#view') return - const video = post.embed as AppBskyEmbedVideo.View + if (!videoEmbed) return const did = post.author.did - const cid = video.cid + const cid = videoEmbed.cid if (!did.startsWith('did:')) return const pdsUrl = await resolvePdsServiceUrl(did as `did:${string}`) const uri = `${pdsUrl}/xrpc/com.atproto.sync.getBlob?did=${did}&cid=${cid}` @@ -409,29 +431,27 @@ let PostDropdownMenuItems = ({ if (success) Toast.show('Video downloaded', 'check') else Toast.show('Failed to download video', 'xmark') - }, [post]) + }, [post, videoEmbed]) const onPressDownloadGif = useCallback(async () => { - if (post.embed?.$type !== 'app.bsky.embed.external#view') return - const media = post.embed as AppBskyEmbedExternal.View + if (!gifEmbed) return Toast.show('Downloading GIF...', 'download') let success - if (isWeb) success = await downloadVideoWeb({uri: media.external.uri}) - else success = await saveVideoToMediaLibrary({uri: media.external.uri}) + if (isWeb) success = await downloadVideoWeb({uri: gifEmbed.external.uri}) + else success = await saveVideoToMediaLibrary({uri: gifEmbed.external.uri}) if (success) Toast.show('GIF downloaded', 'check') else Toast.show('Failed to download GIF', 'xmark') - }, [post]) + }, [gifEmbed]) const isEmbedGif = useCallback(() => { - if (post.embed?.$type !== 'app.bsky.embed.external#view') return false - const embed = post.embed as AppBskyEmbedExternal.View + if (!gifEmbed) return false // Janky workaround by checking if the domain is tenor.com - const url = new URL(embed.external.uri) + const url = new URL(gifEmbed.external.uri) return url.host === 'media.tenor.com' - }, [post]) + }, [gifEmbed]) const onBlockAuthor = useCallback(async () => { try { @@ -507,7 +527,7 @@ let PostDropdownMenuItems = ({ )} - {post.embed?.$type === 'app.bsky.embed.video#view' && ( + {videoEmbed && ( <>