diff options
author | Matthieu Sieben <matthieusieben@users.noreply.github.com> | 2024-05-12 23:18:42 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-05-12 14:18:42 -0700 |
commit | 00a57df5b16bc946c50079914962cc2819011e80 (patch) | |
tree | 4040fad00e74757d846bc503147b9e601e443c84 /src/lib/media/manip.ts | |
parent | 4458b031732149d6f9c107582b9e4ec343385518 (diff) | |
download | voidsky-00a57df5b16bc946c50079914962cc2819011e80.tar.zst |
✅ Fix "Download CAR file" on mobile (#3816)
* download CAR file using AtpAgent instead of building URL * add loader icon on download car button * actually save to disk on android * style nits * bottom margin nit * localize toast * remove fallback so back button works correctly * keep throwing an error if mime type isn't used * be more explicit with toasts * send errors to sentry when encountered --------- Co-authored-by: Hailey <me@haileyok.com>
Diffstat (limited to 'src/lib/media/manip.ts')
-rw-r--r-- | src/lib/media/manip.ts | 74 |
1 files changed, 73 insertions, 1 deletions
diff --git a/src/lib/media/manip.ts b/src/lib/media/manip.ts index 9cd4abc62..71d5c701f 100644 --- a/src/lib/media/manip.ts +++ b/src/lib/media/manip.ts @@ -1,12 +1,23 @@ import {Image as RNImage, Share as RNShare} from 'react-native' import {Image} from 'react-native-image-crop-picker' import uuid from 'react-native-uuid' -import {cacheDirectory, copyAsync, deleteAsync} from 'expo-file-system' +import { + cacheDirectory, + copyAsync, + deleteAsync, + documentDirectory, + EncodingType, + makeDirectoryAsync, + StorageAccessFramework, + writeAsStringAsync, +} from 'expo-file-system' import * as MediaLibrary from 'expo-media-library' import * as Sharing from 'expo-sharing' import ImageResizer from '@bam.tech/react-native-image-resizer' +import {Buffer} from 'buffer' import RNFetchBlob from 'rn-fetch-blob' +import {logger} from '#/logger' import {isAndroid, isIOS} from 'platform/detection' import {Dimensions} from './types' @@ -240,3 +251,64 @@ function normalizePath(str: string, allPlatforms = false): string { } return str } + +export async function saveBytesToDisk( + filename: string, + bytes: Uint8Array, + type: string, +) { + const encoded = Buffer.from(bytes).toString('base64') + return await saveToDevice(filename, encoded, type) +} + +export async function saveToDevice( + filename: string, + encoded: string, + type: string, +) { + try { + if (isIOS) { + const tmpFileUrl = await withTempFile(filename, encoded) + await Sharing.shareAsync(tmpFileUrl, {UTI: type}) + safeDeleteAsync(tmpFileUrl) + return true + } else { + const permissions = + await StorageAccessFramework.requestDirectoryPermissionsAsync() + + if (!permissions.granted) { + return false + } + + const fileUrl = await StorageAccessFramework.createFileAsync( + permissions.directoryUri, + filename, + type, + ) + + await writeAsStringAsync(fileUrl, encoded, { + encoding: EncodingType.Base64, + }) + return true + } + } catch (e) { + logger.error('Error occurred while saving file', {message: e}) + return false + } +} + +async function withTempFile( + filename: string, + encoded: string, +): Promise<string> { + // Using a directory so that the file name is not a random string + // documentDirectory will always be available on native, so we assert as a string. + const tmpDirUri = joinPath(documentDirectory as string, String(uuid.v4())) + await makeDirectoryAsync(tmpDirUri, {intermediates: true}) + + const tmpFileUrl = joinPath(tmpDirUri, filename) + await writeAsStringAsync(tmpFileUrl, encoded, { + encoding: EncodingType.Base64, + }) + return tmpFileUrl +} |