Files
bsky-social-app/src/lib/api/api-polyfill.ts
T
Hailey 84caf1e8d8 Fabric - Running on iOS and Android 🥳 (#3114)
* remove unused packages, switch to `expo-linear-gradient`

* upgrade expo deps

* rm blur view

* re-add normalize-url

* replace `react-native-version-number` with `expo-application`

* add `expo-haptics`

* rm `react-native-haptic-feedback`

* migrate to `expo-haptics`

* add `expo-clipboard`

* migrate to `expo-clipboard`

* add `expo-file-system`

* migrate to `expo-file-system` and `expo-image-manipulator`

passing tests

remove other `react-native-fs` usages

move to `expo-image-manipulator` for resizes

remove react-native-image-resizer

update tests

update jest setup

simplify some logic

properly cleanup files

migrate file downloads to `expo-file-system`

rm `rn-fetch-blob`

* delete file after uploading blob on native

* fix file move error

* add `react-native-date-picker`

* rm `@reactnativecommunity/datetimepicker`

* migrate to `react-native-date-picker`

* use modal on android

* fix android alf

* use @discord/bottom-sheet

* rm some patches

* remove expo-dev-client

* rm metro config changes that have been merged

* Working build

* ignore error for now

* ignore error for now

* add newArchEnabled flag

* add types/invariant
2024-03-05 15:46:40 -08:00

101 lines
2.8 KiB
TypeScript

import {BskyAgent, stringifyLex, jsonToLex} from '@atproto/api'
import {cacheDirectory, copyAsync, moveAsync} from 'expo-file-system'
const GET_TIMEOUT = 15e3 // 15s
const POST_TIMEOUT = 60e3 // 60s
export function doPolyfill() {
BskyAgent.configure({fetch: fetchHandler})
}
interface FetchHandlerResponse {
status: number
headers: Record<string, string>
body: any
}
async function fetchHandler(
reqUri: string,
reqMethod: string,
reqHeaders: Record<string, string>,
reqBody: any,
): Promise<FetchHandlerResponse> {
const reqMimeType = reqHeaders['Content-Type'] || reqHeaders['content-type']
if (reqMimeType && reqMimeType.startsWith('application/json')) {
reqBody = stringifyLex(reqBody)
} else if (
typeof reqBody === 'string' &&
(reqBody.startsWith('/') || reqBody.startsWith('file:'))
) {
if (reqBody.endsWith('.jpeg') || reqBody.endsWith('.jpg')) {
// HACK
// React native has a bug that inflates the size of jpegs on upload
// we get around that by renaming the file ext to .bin
// see https://github.com/facebook/react-native/issues/27099
// -prf
// On some platforms, moving this file is not possible. We will attempt to move it (this is optimal, since
// we don't create duplicates) and if there is an error, we will instead copy the file to the cache directory
const fileName = reqBody.split('/').pop() ?? ''
const newPath = `${cacheDirectory ?? ''}${fileName.replace(
/\.jpe?g$/,
'.bin',
)}`
try {
await moveAsync({
from: reqBody,
to: newPath,
})
reqBody = newPath
} catch (e) {
await copyAsync({
from: reqBody,
to: newPath,
})
}
}
// NOTE
// React native treats bodies with {uri: string} as file uploads to pull from cache
// -prf
reqBody = {uri: reqBody}
}
const controller = new AbortController()
const to = setTimeout(
() => controller.abort(),
reqMethod === 'post' ? POST_TIMEOUT : GET_TIMEOUT,
)
const res = await fetch(reqUri, {
method: reqMethod,
headers: reqHeaders,
body: reqBody,
signal: controller.signal,
})
const resStatus = res.status
const resHeaders: Record<string, string> = {}
res.headers.forEach((value: string, key: string) => {
resHeaders[key] = value
})
const resMimeType = resHeaders['Content-Type'] || resHeaders['content-type']
let resBody
if (resMimeType) {
if (resMimeType.startsWith('application/json')) {
resBody = jsonToLex(await res.json())
} else if (resMimeType.startsWith('text/')) {
resBody = await res.text()
} else {
throw new Error('TODO: non-textual response body')
}
}
clearTimeout(to)
return {
status: resStatus,
headers: resHeaders,
body: resBody,
}
}