Remove deprecated models and mobx usage (react-query refactor) (#1934)
* Update login page to use service query * Update modal to use session instead of store * Move image sizes cache off store * Update settings to no longer use store * Update link-meta fetch to use agent instead of rootstore * Remove deprecated resolveName() * Delete deprecated link-metas cache * Delete deprecated posts cache * Delete all remaining mobx models, including the root store * Strip out unused mobx observer wrappers
This commit is contained in:
parent
e637798e05
commit
54faa7e176
81 changed files with 1084 additions and 1941 deletions
|
@ -7,13 +7,21 @@ import {
|
|||
useAnalytics as useAnalyticsOrig,
|
||||
ClientMethods,
|
||||
} from '@segment/analytics-react-native'
|
||||
import {AppInfo} from 'state/models/root-store'
|
||||
import {z} from 'zod'
|
||||
import {useSession} from '#/state/session'
|
||||
import {sha256} from 'js-sha256'
|
||||
import {ScreenEvent, TrackEvent} from './types'
|
||||
import {logger} from '#/logger'
|
||||
import {listenSessionLoaded} from '#/state/events'
|
||||
|
||||
export const appInfo = z.object({
|
||||
build: z.string().optional(),
|
||||
name: z.string().optional(),
|
||||
namespace: z.string().optional(),
|
||||
version: z.string().optional(),
|
||||
})
|
||||
export type AppInfo = z.infer<typeof appInfo>
|
||||
|
||||
const segmentClient = createClient({
|
||||
writeKey: '8I6DsgfiSLuoONyaunGoiQM7A6y2ybdI',
|
||||
trackAppLifecycleEvents: false,
|
||||
|
@ -128,7 +136,11 @@ async function writeAppInfo(value: AppInfo) {
|
|||
await AsyncStorage.setItem('BSKY_APP_INFO', JSON.stringify(value))
|
||||
}
|
||||
|
||||
async function readAppInfo(): Promise<Partial<AppInfo> | undefined> {
|
||||
async function readAppInfo(): Promise<AppInfo | undefined> {
|
||||
const rawData = await AsyncStorage.getItem('BSKY_APP_INFO')
|
||||
return rawData ? JSON.parse(rawData) : undefined
|
||||
const obj = rawData ? JSON.parse(rawData) : undefined
|
||||
if (!obj || typeof obj !== 'object') {
|
||||
return undefined
|
||||
}
|
||||
return obj
|
||||
}
|
||||
|
|
|
@ -10,7 +10,6 @@ import {
|
|||
RichText,
|
||||
} from '@atproto/api'
|
||||
import {AtUri} from '@atproto/api'
|
||||
import {RootStoreModel} from 'state/models/root-store'
|
||||
import {isNetworkError} from 'lib/strings/errors'
|
||||
import {LinkMeta} from '../link-meta/link-meta'
|
||||
import {isWeb} from 'platform/detection'
|
||||
|
@ -26,33 +25,6 @@ export interface ExternalEmbedDraft {
|
|||
localThumb?: ImageModel
|
||||
}
|
||||
|
||||
export async function resolveName(store: RootStoreModel, didOrHandle: string) {
|
||||
if (!didOrHandle) {
|
||||
throw new Error('Invalid handle: ""')
|
||||
}
|
||||
if (didOrHandle.startsWith('did:')) {
|
||||
return didOrHandle
|
||||
}
|
||||
|
||||
// we run the resolution always to ensure freshness
|
||||
const promise = store.agent
|
||||
.resolveHandle({
|
||||
handle: didOrHandle,
|
||||
})
|
||||
.then(res => {
|
||||
store.handleResolutions.cache.set(didOrHandle, res.data.did)
|
||||
return res.data.did
|
||||
})
|
||||
|
||||
// but we can return immediately if it's cached
|
||||
const cached = store.handleResolutions.cache.get(didOrHandle)
|
||||
if (cached) {
|
||||
return cached
|
||||
}
|
||||
|
||||
return promise
|
||||
}
|
||||
|
||||
export async function uploadBlob(
|
||||
agent: BskyAgent,
|
||||
blob: string,
|
||||
|
|
|
@ -1,68 +0,0 @@
|
|||
import {runInAction} from 'mobx'
|
||||
import {deepObserve} from 'mobx-utils'
|
||||
import set from 'lodash.set'
|
||||
|
||||
const ongoingActions = new Set<any>()
|
||||
|
||||
/**
|
||||
* This is a TypeScript function that optimistically updates data on the client-side before sending a
|
||||
* request to the server and rolling back changes if the request fails.
|
||||
* @param {T} model - The object or record that needs to be updated optimistically.
|
||||
* @param preUpdate - `preUpdate` is a function that is called before the server update is executed. It
|
||||
* can be used to perform any necessary actions or updates on the model or UI before the server update
|
||||
* is initiated.
|
||||
* @param serverUpdate - `serverUpdate` is a function that returns a Promise representing the server
|
||||
* update operation. This function is called after the previous state of the model has been recorded
|
||||
* and the `preUpdate` function has been executed. If the server update is successful, the `postUpdate`
|
||||
* function is called with the result
|
||||
* @param [postUpdate] - `postUpdate` is an optional callback function that will be called after the
|
||||
* server update is successful. It takes in the response from the server update as its parameter. If
|
||||
* this parameter is not provided, nothing will happen after the server update.
|
||||
* @returns A Promise that resolves to `void`.
|
||||
*/
|
||||
export const updateDataOptimistically = async <
|
||||
T extends Record<string, any>,
|
||||
U,
|
||||
>(
|
||||
model: T,
|
||||
preUpdate: () => void,
|
||||
serverUpdate: () => Promise<U>,
|
||||
postUpdate?: (res: U) => void,
|
||||
): Promise<void> => {
|
||||
if (ongoingActions.has(model)) {
|
||||
return
|
||||
}
|
||||
ongoingActions.add(model)
|
||||
|
||||
const prevState: Map<string, any> = new Map<string, any>()
|
||||
const dispose = deepObserve(model, (change, path) => {
|
||||
if (change.observableKind === 'object') {
|
||||
if (change.type === 'update') {
|
||||
prevState.set(
|
||||
[path, change.name].filter(Boolean).join('.'),
|
||||
change.oldValue,
|
||||
)
|
||||
} else if (change.type === 'add') {
|
||||
prevState.set([path, change.name].filter(Boolean).join('.'), undefined)
|
||||
}
|
||||
}
|
||||
})
|
||||
preUpdate()
|
||||
dispose()
|
||||
|
||||
try {
|
||||
const res = await serverUpdate()
|
||||
runInAction(() => {
|
||||
postUpdate?.(res)
|
||||
})
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
prevState.forEach((value, path) => {
|
||||
set(model, path, value)
|
||||
})
|
||||
})
|
||||
throw error
|
||||
} finally {
|
||||
ongoingActions.delete(model)
|
||||
}
|
||||
}
|
|
@ -1,9 +1,8 @@
|
|||
import {AppBskyFeedPost} from '@atproto/api'
|
||||
import {AppBskyFeedPost, BskyAgent} from '@atproto/api'
|
||||
import * as apilib from 'lib/api/index'
|
||||
import {LikelyType, LinkMeta} from './link-meta'
|
||||
// import {match as matchRoute} from 'view/routes'
|
||||
import {convertBskyAppUrlIfNeeded, makeRecordUri} from '../strings/url-helpers'
|
||||
import {RootStoreModel} from 'state/index'
|
||||
import {ComposerOptsQuote} from 'state/shell/composer'
|
||||
import {useGetPost} from '#/state/queries/post'
|
||||
|
||||
|
@ -23,7 +22,7 @@ import {useGetPost} from '#/state/queries/post'
|
|||
// remove once that's implemented
|
||||
// -prf
|
||||
export async function extractBskyMeta(
|
||||
store: RootStoreModel,
|
||||
agent: BskyAgent,
|
||||
url: string,
|
||||
): Promise<LinkMeta> {
|
||||
url = convertBskyAppUrlIfNeeded(url)
|
||||
|
@ -120,13 +119,13 @@ export async function getPostAsQuote(
|
|||
}
|
||||
|
||||
export async function getFeedAsEmbed(
|
||||
store: RootStoreModel,
|
||||
agent: BskyAgent,
|
||||
url: string,
|
||||
): Promise<apilib.ExternalEmbedDraft> {
|
||||
url = convertBskyAppUrlIfNeeded(url)
|
||||
const [_0, user, _1, rkey] = url.split('/').filter(Boolean)
|
||||
const feed = makeRecordUri(user, 'app.bsky.feed.generator', rkey)
|
||||
const res = await store.agent.app.bsky.feed.getFeedGenerator({feed})
|
||||
const res = await agent.app.bsky.feed.getFeedGenerator({feed})
|
||||
return {
|
||||
isLoading: false,
|
||||
uri: feed,
|
||||
|
@ -146,13 +145,13 @@ export async function getFeedAsEmbed(
|
|||
}
|
||||
|
||||
export async function getListAsEmbed(
|
||||
store: RootStoreModel,
|
||||
agent: BskyAgent,
|
||||
url: string,
|
||||
): Promise<apilib.ExternalEmbedDraft> {
|
||||
url = convertBskyAppUrlIfNeeded(url)
|
||||
const [_0, user, _1, rkey] = url.split('/').filter(Boolean)
|
||||
const list = makeRecordUri(user, 'app.bsky.graph.list', rkey)
|
||||
const res = await store.agent.app.bsky.graph.getList({list})
|
||||
const res = await agent.app.bsky.graph.getList({list})
|
||||
return {
|
||||
isLoading: false,
|
||||
uri: list,
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import {BskyAgent} from '@atproto/api'
|
||||
import {isBskyAppUrl} from '../strings/url-helpers'
|
||||
import {RootStoreModel} from 'state/index'
|
||||
import {extractBskyMeta} from './bsky'
|
||||
import {LINK_META_PROXY} from 'lib/constants'
|
||||
|
||||
|
@ -23,12 +23,12 @@ export interface LinkMeta {
|
|||
}
|
||||
|
||||
export async function getLinkMeta(
|
||||
store: RootStoreModel,
|
||||
agent: BskyAgent,
|
||||
url: string,
|
||||
timeout = 5e3,
|
||||
): Promise<LinkMeta> {
|
||||
if (isBskyAppUrl(url)) {
|
||||
return extractBskyMeta(store, url)
|
||||
return extractBskyMeta(agent, url)
|
||||
}
|
||||
|
||||
let urlp
|
||||
|
@ -55,9 +55,9 @@ export async function getLinkMeta(
|
|||
const to = setTimeout(() => controller.abort(), timeout || 5e3)
|
||||
|
||||
const response = await fetch(
|
||||
`${LINK_META_PROXY(
|
||||
store.session.currentSession?.service || '',
|
||||
)}${encodeURIComponent(url)}`,
|
||||
`${LINK_META_PROXY(agent.service.toString() || '')}${encodeURIComponent(
|
||||
url,
|
||||
)}`,
|
||||
{signal: controller.signal},
|
||||
)
|
||||
|
||||
|
|
34
src/lib/media/image-sizes.ts
Normal file
34
src/lib/media/image-sizes.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import {Image} from 'react-native'
|
||||
import type {Dimensions} from 'lib/media/types'
|
||||
|
||||
const sizes: Map<string, Dimensions> = new Map()
|
||||
const activeRequests: Map<string, Promise<Dimensions>> = new Map()
|
||||
|
||||
export function get(uri: string): Dimensions | undefined {
|
||||
return sizes.get(uri)
|
||||
}
|
||||
|
||||
export async function fetch(uri: string): Promise<Dimensions> {
|
||||
const Dimensions = sizes.get(uri)
|
||||
if (Dimensions) {
|
||||
return Dimensions
|
||||
}
|
||||
|
||||
const prom =
|
||||
activeRequests.get(uri) ||
|
||||
new Promise<Dimensions>(resolve => {
|
||||
Image.getSize(
|
||||
uri,
|
||||
(width: number, height: number) => resolve({width, height}),
|
||||
(err: any) => {
|
||||
console.error('Failed to fetch image dimensions for', uri, err)
|
||||
resolve({width: 0, height: 0})
|
||||
},
|
||||
)
|
||||
})
|
||||
activeRequests.set(uri, prom)
|
||||
const res = await prom
|
||||
activeRequests.delete(uri)
|
||||
sizes.set(uri, res)
|
||||
return res
|
||||
}
|
|
@ -1,5 +1,5 @@
|
|||
import {AtUri} from '@atproto/api'
|
||||
import {PROD_SERVICE} from 'state/index'
|
||||
import {PROD_SERVICE} from 'lib/constants'
|
||||
import TLDs from 'tlds'
|
||||
import psl from 'psl'
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue