Bluesky app fork with some witchin' additions 馃挮 witchsky.app
bluesky fork client
120
fork

Configure Feed

Select the types of activity you want to include in your feed.

at a876aae44ea07494ebea9727350aa060b81f317b 84 lines 2.6 kB view raw
1import {copyAsync} from 'expo-file-system/legacy' 2import {type BskyAgent, type ComAtprotoRepoUploadBlob} from '@atproto/api' 3 4import {safeDeleteAsync} from '#/lib/media/manip' 5 6/** 7 * @param encoding Allows overriding the blob's type 8 */ 9export async function uploadBlob( 10 agent: BskyAgent, 11 input: string | Blob, 12 encoding?: string, 13): Promise<ComAtprotoRepoUploadBlob.Response> { 14 if (typeof input === 'string' && input.startsWith('file:')) { 15 const blob = await asBlob(input) 16 return agent.uploadBlob(blob, {encoding}) 17 } 18 19 if (typeof input === 'string' && input.startsWith('/')) { 20 const blob = await asBlob(`file://${input}`) 21 return agent.uploadBlob(blob, {encoding}) 22 } 23 24 if (typeof input === 'string' && input.startsWith('data:')) { 25 const blob = await fetch(input).then(r => r.blob()) 26 return agent.uploadBlob(blob, {encoding}) 27 } 28 29 if (input instanceof Blob) { 30 return agent.uploadBlob(input, {encoding}) 31 } 32 33 throw new TypeError(`Invalid uploadBlob input: ${typeof input}`) 34} 35 36async function asBlob(uri: string): Promise<Blob> { 37 return withSafeFile(uri, async safeUri => { 38 // Note 39 // Android does not support `fetch()` on `file://` URIs. for this reason, we 40 // use XMLHttpRequest instead of simply calling: 41 42 // return fetch(safeUri.replace('file:///', 'file:/')).then(r => r.blob()) 43 44 return await new Promise((resolve, reject) => { 45 const xhr = new XMLHttpRequest() 46 xhr.onload = () => resolve(xhr.response) 47 xhr.onerror = () => reject(new Error('Failed to load blob')) 48 xhr.responseType = 'blob' 49 xhr.open('GET', safeUri, true) 50 xhr.send(null) 51 }) 52 }) 53} 54 55// HACK 56// React native has a bug that inflates the size of jpegs on upload 57// we get around that by renaming the file ext to .bin 58// see https://github.com/facebook/react-native/issues/27099 59// -prf 60async function withSafeFile<T>( 61 uri: string, 62 fn: (path: string) => Promise<T>, 63): Promise<T> { 64 if (uri.endsWith('.jpeg') || uri.endsWith('.jpg')) { 65 // Since we don't "own" the file, we should avoid renaming or modifying it. 66 // Instead, let's copy it to a temporary file and use that (then remove the 67 // temporary file). 68 const newPath = uri.replace(/\.jpe?g$/, '.bin') 69 try { 70 await copyAsync({from: uri, to: newPath}) 71 } catch { 72 // Failed to copy the file, just use the original 73 return await fn(uri) 74 } 75 try { 76 return await fn(newPath) 77 } finally { 78 // Remove the temporary file 79 await safeDeleteAsync(newPath) 80 } 81 } else { 82 return fn(uri) 83 } 84}