this repo has no description
at patch-1 88 lines 2.5 kB view raw
1/** 2 * The following is the react-native fetch handler used currently in the bluesky app 3 * It's not our prettiest work, but it gets the job done 4 */ 5 6import { BskyAgent, stringifyLex, jsonToLex } from '@atproto/api' 7import RNFS from 'react-native-fs' 8 9const GET_TIMEOUT = 15e3 // 15s 10const POST_TIMEOUT = 60e3 // 60s 11 12export function doPolyfill() { 13 BskyAgent.configure({ fetch: fetchHandler }) 14} 15 16interface FetchHandlerResponse { 17 status: number 18 headers: Record<string, string> 19 body: ArrayBuffer | undefined 20} 21 22async function fetchHandler( 23 reqUri: string, 24 reqMethod: string, 25 reqHeaders: Record<string, string>, 26 reqBody: any, 27): Promise<FetchHandlerResponse> { 28 const reqMimeType = reqHeaders['Content-Type'] || reqHeaders['content-type'] 29 if (reqMimeType && reqMimeType.startsWith('application/json')) { 30 reqBody = stringifyLex(reqBody) 31 } else if ( 32 typeof reqBody === 'string' && 33 (reqBody.startsWith('/') || reqBody.startsWith('file:')) 34 ) { 35 if (reqBody.endsWith('.jpeg') || reqBody.endsWith('.jpg')) { 36 // HACK 37 // React native has a bug that inflates the size of jpegs on upload 38 // we get around that by renaming the file ext to .bin 39 // see https://github.com/facebook/react-native/issues/27099 40 // -prf 41 const newPath = reqBody.replace(/\.jpe?g$/, '.bin') 42 await RNFS.moveFile(reqBody, newPath) 43 reqBody = newPath 44 } 45 // NOTE 46 // React native treats bodies with {uri: string} as file uploads to pull from cache 47 // -prf 48 reqBody = { uri: reqBody } 49 } 50 51 const controller = new AbortController() 52 const to = setTimeout( 53 () => controller.abort(), 54 reqMethod === 'post' ? POST_TIMEOUT : GET_TIMEOUT, 55 ) 56 57 const res = await fetch(reqUri, { 58 method: reqMethod, 59 headers: reqHeaders, 60 body: reqBody, 61 signal: controller.signal, 62 }) 63 64 const resStatus = res.status 65 const resHeaders: Record<string, string> = {} 66 res.headers.forEach((value: string, key: string) => { 67 resHeaders[key] = value 68 }) 69 const resMimeType = resHeaders['Content-Type'] || resHeaders['content-type'] 70 let resBody 71 if (resMimeType) { 72 if (resMimeType.startsWith('application/json')) { 73 resBody = jsonToLex(await res.json()) 74 } else if (resMimeType.startsWith('text/')) { 75 resBody = await res.text() 76 } else { 77 resBody = await res.blob() 78 } 79 } 80 81 clearTimeout(to) 82 83 return { 84 status: resStatus, 85 headers: resHeaders, 86 body: resBody, 87 } 88}