diff options
Diffstat (limited to 'src/state')
-rw-r--r-- | src/state/index.ts | 4 | ||||
-rw-r--r-- | src/state/lib/api-polyfill.ts | 76 | ||||
-rw-r--r-- | src/state/lib/api-polyfill.web.ts | 4 | ||||
-rw-r--r-- | src/state/lib/api.ts | 75 | ||||
-rw-r--r-- | src/state/lib/bg-scheduler.ts | 18 | ||||
-rw-r--r-- | src/state/lib/bg-scheduler.web.ts | 13 | ||||
-rw-r--r-- | src/state/models/profile-view.ts | 2 | ||||
-rw-r--r-- | src/state/models/root-store.ts | 9 |
8 files changed, 118 insertions, 83 deletions
diff --git a/src/state/index.ts b/src/state/index.ts index 78fba2ecf..654c15af7 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -2,7 +2,7 @@ import {autorun} from 'mobx' import {Platform} from 'react-native' import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api' import {RootStoreModel} from './models/root-store' -import * as libapi from './lib/api' +import * as apiPolyfill from './lib/api-polyfill' import * as storage from './lib/storage' export const LOCAL_DEV_SERVICE = @@ -17,7 +17,7 @@ export async function setupState(serviceUri = DEFAULT_SERVICE) { let rootStore: RootStoreModel let data: any - libapi.doPolyfill() + apiPolyfill.doPolyfill() const api = AtpApi.service(serviceUri) as SessionServiceClient rootStore = new RootStoreModel(api) diff --git a/src/state/lib/api-polyfill.ts b/src/state/lib/api-polyfill.ts new file mode 100644 index 000000000..be6f90f7a --- /dev/null +++ b/src/state/lib/api-polyfill.ts @@ -0,0 +1,76 @@ +import {sessionClient as AtpApi} from '@atproto/api' + +export function doPolyfill() { + AtpApi.xrpc.fetch = fetchHandler +} + +interface FetchHandlerResponse { + status: number + headers: Record<string, string> + body: ArrayBuffer | undefined +} + +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 = JSON.stringify(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 + const newPath = reqBody.replace(/\.jpe?g$/, '.bin') + await RNFS.moveFile(reqBody, newPath) + reqBody = 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(), 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 = 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, + } +} diff --git a/src/state/lib/api-polyfill.web.ts b/src/state/lib/api-polyfill.web.ts new file mode 100644 index 000000000..1469cf905 --- /dev/null +++ b/src/state/lib/api-polyfill.web.ts @@ -0,0 +1,4 @@ +export function doPolyfill() { + // TODO needed? native fetch may work fine -prf + // AtpApi.xrpc.fetch = fetchHandler +} diff --git a/src/state/lib/api.ts b/src/state/lib/api.ts index e498bef1b..c63f0e2f7 100644 --- a/src/state/lib/api.ts +++ b/src/state/lib/api.ts @@ -19,10 +19,6 @@ import {Image} from '../../lib/images' const TIMEOUT = 10e3 // 10s -export function doPolyfill() { - AtpApi.xrpc.fetch = fetchHandler -} - export interface ExternalEmbedDraft { uri: string isLoading: boolean @@ -199,74 +195,3 @@ export async function unfollow(store: RootStoreModel, followUri: string) { rkey: followUrip.rkey, }) } - -interface FetchHandlerResponse { - status: number - headers: Record<string, string> - body: ArrayBuffer | undefined -} - -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 = JSON.stringify(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 - const newPath = reqBody.replace(/\.jpe?g$/, '.bin') - await RNFS.moveFile(reqBody, newPath) - reqBody = 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(), 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 = 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, - } -} diff --git a/src/state/lib/bg-scheduler.ts b/src/state/lib/bg-scheduler.ts new file mode 100644 index 000000000..97ccb78b2 --- /dev/null +++ b/src/state/lib/bg-scheduler.ts @@ -0,0 +1,18 @@ +import BackgroundFetch, { + BackgroundFetchStatus, +} from 'react-native-background-fetch' + +export function configure( + handler: (taskId: string) => Promise<void>, + timeoutHandler: (taskId: string) => Promise<void>, +): Promise<BackgroundFetchStatus> { + return BackgroundFetch.configure( + {minimumFetchInterval: 15}, + handler, + timeoutHandler, + ) +} + +export function finish(taskId: string) { + return BackgroundFetch.finish(taskId) +} diff --git a/src/state/lib/bg-scheduler.web.ts b/src/state/lib/bg-scheduler.web.ts new file mode 100644 index 000000000..91ec9428f --- /dev/null +++ b/src/state/lib/bg-scheduler.web.ts @@ -0,0 +1,13 @@ +type BackgroundFetchStatus = 0 | 1 | 2 + +export async function configure( + _handler: (taskId: string) => Promise<void>, + _timeoutHandler: (taskId: string) => Promise<void>, +): Promise<BackgroundFetchStatus> { + // TODO + return 0 +} + +export function finish(_taskId: string) { + // TODO +} diff --git a/src/state/models/profile-view.ts b/src/state/models/profile-view.ts index a1535693c..3228c57e8 100644 --- a/src/state/models/profile-view.ts +++ b/src/state/models/profile-view.ts @@ -1,5 +1,5 @@ import {makeAutoObservable, runInAction} from 'mobx' -import {Image as PickedImage} from 'react-native-image-crop-picker' +import {Image as PickedImage} from '../../view/com/util/images/ImageCropPicker' import { AppBskyActorGetProfile as GetProfile, AppBskyActorProfile as Profile, diff --git a/src/state/models/root-store.ts b/src/state/models/root-store.ts index c4798ad0b..2f6931cdc 100644 --- a/src/state/models/root-store.ts +++ b/src/state/models/root-store.ts @@ -6,7 +6,7 @@ import {makeAutoObservable} from 'mobx' import {sessionClient as AtpApi, SessionServiceClient} from '@atproto/api' import {createContext, useContext} from 'react' import {DeviceEventEmitter, EmitterSubscription} from 'react-native' -import BackgroundFetch from 'react-native-background-fetch' +import * as BgScheduler from '../lib/bg-scheduler' import {isObj, hasProp} from '../lib/type-guards' import {LogModel} from './log' import {SessionModel} from './session' @@ -124,8 +124,7 @@ export class RootStoreModel { // background fetch runs every 15 minutes *at most* and will get slowed down // based on some heuristics run by iOS, meaning it is not a reliable form of delivery // -prf - BackgroundFetch.configure( - {minimumFetchInterval: 15}, + BgScheduler.configure( this.onBgFetch.bind(this), this.onBgFetchTimeout.bind(this), ).then(status => { @@ -138,12 +137,12 @@ export class RootStoreModel { if (this.session.hasSession) { await this.me.bgFetchNotifications() } - BackgroundFetch.finish(taskId) + BgScheduler.finish(taskId) } onBgFetchTimeout(taskId: string) { this.log.debug(`Background fetch timed out for task ${taskId}`) - BackgroundFetch.finish(taskId) + BgScheduler.finish(taskId) } } |