revoke media URLs
This commit is contained in:
@@ -142,6 +142,7 @@ import {
|
||||
useSaveDraftMutation,
|
||||
} from './drafts/state/queries'
|
||||
import {type DraftSummary} from './drafts/state/schema'
|
||||
import {revokeAllMediaUrls} from './drafts/state/storage'
|
||||
import {PostLanguageSelect} from './select-language/PostLanguageSelect'
|
||||
import {
|
||||
type AssetType,
|
||||
@@ -504,6 +505,7 @@ export const ComposePost = ({
|
||||
const onClose = useCallback(() => {
|
||||
closeComposer()
|
||||
clearThumbnailCache(queryClient)
|
||||
revokeAllMediaUrls()
|
||||
}, [closeComposer, queryClient])
|
||||
|
||||
const handleSaveDraft = React.useCallback(async () => {
|
||||
|
||||
@@ -14,6 +14,7 @@ import {IS_NATIVE} from '#/env'
|
||||
import {DraftItem} from './DraftItem'
|
||||
import {useDeleteDraftMutation, useDraftsQuery} from './state/queries'
|
||||
import {type DraftSummary} from './state/schema'
|
||||
import {revokeAllMediaUrls} from './state/storage'
|
||||
|
||||
export function DraftsListDialog({
|
||||
control,
|
||||
@@ -36,6 +37,12 @@ export function DraftsListDialog({
|
||||
const handleSelectDraft = useCallback(
|
||||
(summary: DraftSummary) => {
|
||||
control.close(() => {
|
||||
// Revoke preview URLs
|
||||
// It would be neater to do it in the `onClose` callback
|
||||
// on the dialog itself, but this would wipe out the new ones
|
||||
// that will be created by `onSelectDraft`
|
||||
// ndb: if we miss any, it'll be handled by the composer closing
|
||||
revokeAllMediaUrls()
|
||||
onSelectDraft(summary)
|
||||
})
|
||||
},
|
||||
@@ -53,7 +60,11 @@ export function DraftsListDialog({
|
||||
() => (
|
||||
<Button
|
||||
label={_(msg`Back`)}
|
||||
onPress={() => control.close()}
|
||||
onPress={() => {
|
||||
control.close(() => {
|
||||
revokeAllMediaUrls()
|
||||
})
|
||||
}}
|
||||
size="small"
|
||||
color="primary"
|
||||
variant="ghost">
|
||||
@@ -93,7 +104,7 @@ export function DraftsListDialog({
|
||||
|
||||
const onEndReached = useCallback(() => {
|
||||
if (hasNextPage && !isFetchingNextPage) {
|
||||
fetchNextPage()
|
||||
void fetchNextPage()
|
||||
}
|
||||
}, [hasNextPage, isFetchingNextPage, fetchNextPage])
|
||||
|
||||
@@ -132,7 +143,7 @@ export function DraftsListDialog({
|
||||
<Dialog.InnerFlatList
|
||||
data={drafts}
|
||||
renderItem={renderItem}
|
||||
keyExtractor={item => item.id}
|
||||
keyExtractor={(item: DraftSummary) => item.id}
|
||||
ListHeaderComponent={web(header)}
|
||||
stickyHeaderIndices={web([0])}
|
||||
ListEmptyComponent={emptyComponent}
|
||||
|
||||
@@ -155,3 +155,17 @@ export function clearMediaCache(): void {
|
||||
cachePopulated = false
|
||||
populateCachePromise = null
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke a media URL (no-op on native - only needed for web blob URLs)
|
||||
*/
|
||||
export function revokeMediaUrl(_url: string): void {
|
||||
// No-op on native - file URIs don't need revocation
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke all media URLs (no-op on native - only needed for web blob URLs)
|
||||
*/
|
||||
export function revokeAllMediaUrls(): void {
|
||||
// No-op on native - file URIs don't need revocation
|
||||
}
|
||||
|
||||
@@ -84,6 +84,11 @@ export async function saveMediaToLocal(
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Track blob URLs created by loadMediaFromLocal for cleanup
|
||||
*/
|
||||
const createdBlobUrls = new Set<string>()
|
||||
|
||||
/**
|
||||
* Load a media file from IndexedDB
|
||||
* @returns A blob URL for the saved media
|
||||
@@ -97,7 +102,10 @@ export async function loadMediaFromLocal(
|
||||
throw new Error(`Media file not found: ${localRefPath}`)
|
||||
}
|
||||
|
||||
return URL.createObjectURL(record.blob)
|
||||
const url = URL.createObjectURL(record.blob)
|
||||
logger.debug('Created blob URL', {url})
|
||||
createdBlobUrls.add(url)
|
||||
return url
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -165,6 +173,20 @@ export function clearMediaCache(): void {
|
||||
*/
|
||||
export function revokeMediaUrl(url: string): void {
|
||||
if (url.startsWith('blob:')) {
|
||||
logger.debug('Revoking blob URL', {url})
|
||||
URL.revokeObjectURL(url)
|
||||
createdBlobUrls.delete(url)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Revoke all blob URLs created by loadMediaFromLocal.
|
||||
* Call this when closing the drafts list dialog to prevent memory leaks.
|
||||
*/
|
||||
export function revokeAllMediaUrls(): void {
|
||||
logger.debug(`Revoking ${createdBlobUrls.size} blob URLs`)
|
||||
for (const url of createdBlobUrls) {
|
||||
URL.revokeObjectURL(url)
|
||||
}
|
||||
createdBlobUrls.clear()
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ export const RQKEY = 'video-thumbnail'
|
||||
|
||||
export function clearThumbnailCache(queryClient: QueryClient) {
|
||||
clearCache().catch(() => {})
|
||||
queryClient.resetQueries({queryKey: [RQKEY]})
|
||||
void queryClient.resetQueries({queryKey: [RQKEY]})
|
||||
}
|
||||
|
||||
export function VideoTranscodeBackdrop({uri}: {uri: string}) {
|
||||
|
||||
Reference in New Issue
Block a user