migrate file system api

This commit is contained in:
Samuel Newman
2026-09-04 13:54:39 +03:00
parent a197340bce
commit ece5693362
12 changed files with 228 additions and 255 deletions
+42 -43
View File
@@ -1,10 +1,4 @@
import {
cacheDirectory,
copyAsync,
deleteAsync,
makeDirectoryAsync,
moveAsync,
} from 'expo-file-system/legacy'
import {Directory, File, Paths} from 'expo-file-system'
import {type ImageManipulatorContext, SaveFormat} from 'expo-image-manipulator'
import {nanoid} from 'nanoid/non-secure'
@@ -50,11 +44,14 @@ type ComposerImageWithTransformation = ComposerImageBase & {
export type ComposerImage =
ComposerImageWithoutTransformation | ComposerImageWithTransformation
let _imageCacheDirectory: string
let _imageCacheDirectory: Directory
function getImageCacheDirectory(): string | null {
function getImageCacheDirectory(): Directory | null {
if (IS_NATIVE) {
return (_imageCacheDirectory ??= joinPath(cacheDirectory!, 'bsky-composer'))
return (_imageCacheDirectory ??= new Directory(
Paths.cache,
'bsky-composer',
))
}
return null
@@ -273,13 +270,14 @@ export async function compressImage(
async function moveIfNecessary(from: string) {
const cacheDir = IS_NATIVE && getImageCacheDirectory()
if (cacheDir && !from.startsWith(cacheDir)) {
const to = joinPath(cacheDir, nanoid(36))
if (cacheDir && !from.startsWith(cacheDir.uri)) {
const source = new File(normalizeFileUri(from))
const destination = new File(cacheDir, nanoid(36))
await makeDirectoryAsync(cacheDir, {intermediates: true})
await moveAsync({from, to})
cacheDir.create({idempotent: true, intermediates: true})
await source.move(destination)
return to
return destination.uri
}
return from
@@ -316,20 +314,15 @@ async function copyToCache(from: string): Promise<string> {
// Native: copy to cache directory to survive OS temp file cleanup
const cacheDir = getImageCacheDirectory()
if (!cacheDir || from.startsWith(cacheDir)) {
if (!cacheDir || from.startsWith(cacheDir.uri)) {
return from
}
const to = joinPath(cacheDir, nanoid(36))
await makeDirectoryAsync(cacheDir, {intermediates: true})
const destination = new File(cacheDir, nanoid(36))
cacheDir.create({idempotent: true, intermediates: true})
let normalizedFrom = from
if (!from.startsWith('file://') && from.startsWith('/')) {
normalizedFrom = `file://${from}`
}
await copyAsync({from: normalizedFrom, to})
return to
await new File(normalizeFileUri(from)).copy(destination)
return destination.uri
}
/**
@@ -363,36 +356,42 @@ function blobToDataUri(blob: Blob): Promise<string> {
const SYSTEM_MEDIA_CACHE_DIRS = ['ImagePicker', 'ImageManipulator']
/** Purge files that were created to accomodate image manipulation */
export async function purgeTemporaryImageFiles() {
export function purgeTemporaryImageFiles() {
if (!IS_NATIVE) {
return
}
const cacheDir = getImageCacheDirectory()
if (cacheDir) {
await deleteAsync(cacheDir, {idempotent: true})
await makeDirectoryAsync(cacheDir)
try {
if (cacheDir.exists) {
cacheDir.delete()
}
cacheDir.create()
} catch (err) {
logger.warn('Failed to purge composer image cache', {safeMessage: err})
}
}
// We don't recreate these - the respective expo modules recreate them on
// demand the next time they run.
await Promise.all(
SYSTEM_MEDIA_CACHE_DIRS.map(dir =>
deleteAsync(joinPath(cacheDirectory!, dir), {idempotent: true}),
),
)
for (const dir of SYSTEM_MEDIA_CACHE_DIRS) {
try {
const directory = new Directory(Paths.cache, dir)
if (directory.exists) {
directory.delete()
}
} catch (err) {
logger.warn('Failed to purge system image cache', {
safeMessage: err,
directory: dir,
})
}
}
}
function joinPath(a: string, b: string) {
if (a.endsWith('/')) {
if (b.startsWith('/')) {
return a.slice(0, -1) + b
}
return a + b
} else if (b.startsWith('/')) {
return a + b
}
return a + '/' + b
function normalizeFileUri(uri: string): string {
return uri.startsWith('/') ? `file://${uri}` : uri
}
function containImageRes(