Performance fixes with new getPosts (#525)
* Update notifications to fetch in a batch using getPosts * Improve search perf with getPosts * Bump @atproto/api@0.2.9 * Just use post uri for keyzio/stable
parent
da8af38dcc
commit
1b356556c9
|
@ -22,7 +22,7 @@
|
|||
"e2e:run": "detox test --configuration ios.sim.debug --take-screenshots all"
|
||||
},
|
||||
"dependencies": {
|
||||
"@atproto/api": "0.2.8",
|
||||
"@atproto/api": "0.2.9",
|
||||
"@bam.tech/react-native-image-resizer": "^3.0.4",
|
||||
"@braintree/sanitize-url": "^6.0.2",
|
||||
"@expo/webpack-config": "^18.0.1",
|
||||
|
|
|
@ -11,13 +11,6 @@ import * as apilib from 'lib/api/index'
|
|||
import {cleanError} from 'lib/strings/errors'
|
||||
import {updateDataOptimistically} from 'lib/async/revertible'
|
||||
|
||||
function* reactKeyGenerator(): Generator<string> {
|
||||
let counter = 0
|
||||
while (true) {
|
||||
yield `item-${counter++}`
|
||||
}
|
||||
}
|
||||
|
||||
export class PostThreadItemModel {
|
||||
// ui state
|
||||
_reactKey: string = ''
|
||||
|
@ -55,10 +48,9 @@ export class PostThreadItemModel {
|
|||
|
||||
constructor(
|
||||
public rootStore: RootStoreModel,
|
||||
reactKey: string,
|
||||
v: AppBskyFeedDefs.ThreadViewPost,
|
||||
) {
|
||||
this._reactKey = reactKey
|
||||
this._reactKey = `thread-${v.post.uri}`
|
||||
this.post = v.post
|
||||
if (FeedPost.isRecord(this.post.record)) {
|
||||
const valid = FeedPost.validateRecord(this.post.record)
|
||||
|
@ -82,7 +74,6 @@ export class PostThreadItemModel {
|
|||
}
|
||||
|
||||
assignTreeModels(
|
||||
keyGen: Generator<string>,
|
||||
v: AppBskyFeedDefs.ThreadViewPost,
|
||||
higlightedPostUri: string,
|
||||
includeParent = true,
|
||||
|
@ -91,22 +82,12 @@ export class PostThreadItemModel {
|
|||
// parents
|
||||
if (includeParent && v.parent) {
|
||||
if (AppBskyFeedDefs.isThreadViewPost(v.parent)) {
|
||||
const parentModel = new PostThreadItemModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
v.parent,
|
||||
)
|
||||
const parentModel = new PostThreadItemModel(this.rootStore, v.parent)
|
||||
parentModel._depth = this._depth - 1
|
||||
parentModel._showChildReplyLine = true
|
||||
if (v.parent.parent) {
|
||||
parentModel._showParentReplyLine = true //parentModel.uri !== higlightedPostUri
|
||||
parentModel.assignTreeModels(
|
||||
keyGen,
|
||||
v.parent,
|
||||
higlightedPostUri,
|
||||
true,
|
||||
false,
|
||||
)
|
||||
parentModel.assignTreeModels(v.parent, higlightedPostUri, true, false)
|
||||
}
|
||||
this.parent = parentModel
|
||||
} else if (AppBskyFeedDefs.isNotFoundPost(v.parent)) {
|
||||
|
@ -118,23 +99,13 @@ export class PostThreadItemModel {
|
|||
const replies = []
|
||||
for (const item of v.replies) {
|
||||
if (AppBskyFeedDefs.isThreadViewPost(item)) {
|
||||
const itemModel = new PostThreadItemModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
item,
|
||||
)
|
||||
const itemModel = new PostThreadItemModel(this.rootStore, item)
|
||||
itemModel._depth = this._depth + 1
|
||||
itemModel._showParentReplyLine =
|
||||
itemModel.parentUri !== higlightedPostUri
|
||||
if (item.replies?.length) {
|
||||
itemModel._showChildReplyLine = true
|
||||
itemModel.assignTreeModels(
|
||||
keyGen,
|
||||
item,
|
||||
higlightedPostUri,
|
||||
false,
|
||||
true,
|
||||
)
|
||||
itemModel.assignTreeModels(item, higlightedPostUri, false, true)
|
||||
}
|
||||
replies.push(itemModel)
|
||||
} else if (AppBskyFeedDefs.isNotFoundPost(item)) {
|
||||
|
@ -241,6 +212,19 @@ export class PostThreadModel {
|
|||
this.params = params
|
||||
}
|
||||
|
||||
static fromPostView(
|
||||
rootStore: RootStoreModel,
|
||||
postView: AppBskyFeedDefs.PostView,
|
||||
) {
|
||||
const model = new PostThreadModel(rootStore, {uri: postView.uri})
|
||||
model.resolvedUri = postView.uri
|
||||
model.hasLoaded = true
|
||||
model.thread = new PostThreadItemModel(rootStore, {
|
||||
post: postView,
|
||||
})
|
||||
return model
|
||||
}
|
||||
|
||||
get hasContent() {
|
||||
return typeof this.thread !== 'undefined'
|
||||
}
|
||||
|
@ -360,6 +344,9 @@ export class PostThreadModel {
|
|||
}
|
||||
|
||||
async _load(isRefreshing = false) {
|
||||
if (this.hasLoaded && !isRefreshing) {
|
||||
return
|
||||
}
|
||||
this._xLoading(isRefreshing)
|
||||
try {
|
||||
const res = await this.rootStore.agent.getPostThread(
|
||||
|
@ -374,15 +361,12 @@ export class PostThreadModel {
|
|||
|
||||
_replaceAll(res: GetPostThread.Response) {
|
||||
sortThread(res.data.thread)
|
||||
const keyGen = reactKeyGenerator()
|
||||
const thread = new PostThreadItemModel(
|
||||
this.rootStore,
|
||||
keyGen.next().value,
|
||||
res.data.thread as AppBskyFeedDefs.ThreadViewPost,
|
||||
)
|
||||
thread._isHighlightedPost = true
|
||||
thread.assignTreeModels(
|
||||
keyGen,
|
||||
res.data.thread as AppBskyFeedDefs.ThreadViewPost,
|
||||
thread.uri,
|
||||
)
|
||||
|
|
|
@ -2,6 +2,7 @@ import {makeAutoObservable, runInAction} from 'mobx'
|
|||
import {
|
||||
AppBskyNotificationListNotifications as ListNotifications,
|
||||
AppBskyActorDefs,
|
||||
AppBskyFeedDefs,
|
||||
AppBskyFeedPost,
|
||||
AppBskyFeedRepost,
|
||||
AppBskyFeedLike,
|
||||
|
@ -146,6 +147,14 @@ export class NotificationsFeedItemModel {
|
|||
return false
|
||||
}
|
||||
|
||||
get additionaDataUri(): string | undefined {
|
||||
if (this.isReply || this.isQuote || this.isMention) {
|
||||
return this.uri
|
||||
} else if (this.isLike || this.isRepost) {
|
||||
return this.subjectUri
|
||||
}
|
||||
}
|
||||
|
||||
get subjectUri(): string {
|
||||
if (this.reasonSubject) {
|
||||
return this.reasonSubject
|
||||
|
@ -193,28 +202,11 @@ export class NotificationsFeedItemModel {
|
|||
)
|
||||
}
|
||||
|
||||
async fetchAdditionalData() {
|
||||
if (!this.needsAdditionalData) {
|
||||
return
|
||||
}
|
||||
let postUri
|
||||
if (this.isReply || this.isQuote || this.isMention) {
|
||||
postUri = this.uri
|
||||
} else if (this.isLike || this.isRepost) {
|
||||
postUri = this.subjectUri
|
||||
}
|
||||
if (postUri) {
|
||||
this.additionalPost = new PostThreadModel(this.rootStore, {
|
||||
uri: postUri,
|
||||
depth: 0,
|
||||
})
|
||||
await this.additionalPost.setup().catch(e => {
|
||||
this.rootStore.log.error(
|
||||
'Failed to load post needed by notification',
|
||||
e,
|
||||
setAdditionalData(additionalPost: AppBskyFeedDefs.PostView) {
|
||||
this.additionalPost = PostThreadModel.fromPostView(
|
||||
this.rootStore,
|
||||
additionalPost,
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -464,7 +456,13 @@ export class NotificationsFeedModel {
|
|||
'mostRecent',
|
||||
res.data.notifications[0],
|
||||
)
|
||||
await notif.fetchAdditionalData()
|
||||
const addedUri = notif.additionaDataUri
|
||||
if (addedUri) {
|
||||
const postsRes = await this.rootStore.agent.app.bsky.feed.getPosts({
|
||||
uris: [addedUri],
|
||||
})
|
||||
notif.setAdditionalData(postsRes.data.posts[0])
|
||||
}
|
||||
const filtered = this._filterNotifications([notif])
|
||||
return filtered[0]
|
||||
}
|
||||
|
@ -536,25 +534,39 @@ export class NotificationsFeedModel {
|
|||
async _fetchItemModels(
|
||||
items: ListNotifications.Notification[],
|
||||
): Promise<NotificationsFeedItemModel[]> {
|
||||
const promises = []
|
||||
// construct item models and track who needs more data
|
||||
const itemModels: NotificationsFeedItemModel[] = []
|
||||
const addedPostMap = new Map<string, NotificationsFeedItemModel[]>()
|
||||
for (const item of items) {
|
||||
const itemModel = new NotificationsFeedItemModel(
|
||||
this.rootStore,
|
||||
`item-${_idCounter++}`,
|
||||
item,
|
||||
)
|
||||
if (itemModel.needsAdditionalData) {
|
||||
promises.push(itemModel.fetchAdditionalData())
|
||||
const uri = itemModel.additionaDataUri
|
||||
if (uri) {
|
||||
const models = addedPostMap.get(uri) || []
|
||||
models.push(itemModel)
|
||||
addedPostMap.set(uri, models)
|
||||
}
|
||||
itemModels.push(itemModel)
|
||||
}
|
||||
await Promise.all(promises).catch(e => {
|
||||
this.rootStore.log.error(
|
||||
'Uncaught failure during notifications _processNotifications()',
|
||||
e,
|
||||
)
|
||||
|
||||
// fetch additional data
|
||||
if (addedPostMap.size > 0) {
|
||||
const postsRes = await this.rootStore.agent.app.bsky.feed.getPosts({
|
||||
uris: Array.from(addedPostMap.keys()),
|
||||
})
|
||||
for (const post of postsRes.data.posts) {
|
||||
const models = addedPostMap.get(post.uri)
|
||||
if (models?.length) {
|
||||
for (const model of models) {
|
||||
model.setAdditionalData(post)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return itemModels
|
||||
}
|
||||
|
||||
|
|
|
@ -1,13 +1,14 @@
|
|||
import {makeAutoObservable, runInAction} from 'mobx'
|
||||
import {searchProfiles, searchPosts} from 'lib/api/search'
|
||||
import {AppBskyActorDefs} from '@atproto/api'
|
||||
import {PostThreadModel} from '../content/post-thread'
|
||||
import {AppBskyActorDefs, AppBskyFeedDefs} from '@atproto/api'
|
||||
import {RootStoreModel} from '../root-store'
|
||||
|
||||
export class SearchUIModel {
|
||||
isPostsLoading = false
|
||||
isProfilesLoading = false
|
||||
query: string = ''
|
||||
postUris: string[] = []
|
||||
posts: PostThreadModel[] = []
|
||||
profiles: AppBskyActorDefs.ProfileView[] = []
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {
|
||||
|
@ -15,7 +16,7 @@ export class SearchUIModel {
|
|||
}
|
||||
|
||||
async fetch(q: string) {
|
||||
this.postUris = []
|
||||
this.posts = []
|
||||
this.profiles = []
|
||||
this.query = q
|
||||
if (!q.trim()) {
|
||||
|
@ -29,8 +30,22 @@ export class SearchUIModel {
|
|||
searchPosts(q).catch(_e => []),
|
||||
searchProfiles(q).catch(_e => []),
|
||||
])
|
||||
|
||||
let posts: AppBskyFeedDefs.PostView[] = []
|
||||
if (postsSearch?.length) {
|
||||
do {
|
||||
const res = await this.rootStore.agent.app.bsky.feed.getPosts({
|
||||
uris: postsSearch
|
||||
.splice(0, 25)
|
||||
.map(p => `at://${p.user.did}/${p.tid}`),
|
||||
})
|
||||
posts = posts.concat(res.data.posts)
|
||||
} while (postsSearch.length)
|
||||
}
|
||||
runInAction(() => {
|
||||
this.postUris = postsSearch?.map(p => `at://${p.user.did}/${p.tid}`) || []
|
||||
this.posts = posts.map(post =>
|
||||
PostThreadModel.fromPostView(this.rootStore, post),
|
||||
)
|
||||
this.isPostsLoading = false
|
||||
})
|
||||
|
||||
|
|
|
@ -49,7 +49,7 @@ const PostResults = observer(({model}: {model: SearchUIModel}) => {
|
|||
)
|
||||
}
|
||||
|
||||
if (model.postUris.length === 0) {
|
||||
if (model.posts.length === 0) {
|
||||
return (
|
||||
<CenteredView>
|
||||
<Text type="xl" style={[styles.empty, pal.text]}>
|
||||
|
@ -61,8 +61,13 @@ const PostResults = observer(({model}: {model: SearchUIModel}) => {
|
|||
|
||||
return (
|
||||
<ScrollView style={pal.view}>
|
||||
{model.postUris.map(uri => (
|
||||
<Post key={uri} uri={uri} hideError />
|
||||
{model.posts.map(post => (
|
||||
<Post
|
||||
key={post.resolvedUri}
|
||||
uri={post.resolvedUri}
|
||||
initView={post}
|
||||
hideError
|
||||
/>
|
||||
))}
|
||||
<View style={s.footerSpacer} />
|
||||
<View style={s.footerSpacer} />
|
||||
|
|
|
@ -30,10 +30,10 @@
|
|||
tlds "^1.234.0"
|
||||
typed-emitter "^2.1.0"
|
||||
|
||||
"@atproto/api@0.2.8":
|
||||
version "0.2.8"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.2.8.tgz#92ed413804ecb43aaa45ec18afc93d6f2b28a689"
|
||||
integrity sha512-LfPgtf3UNg2W/AxHkJMJrLNT9QAD6bi16Sw5Zt3mgANrDnHWGygA7gRpeNdgVI+kFEhQfrIItemJvWLIB9BJDQ==
|
||||
"@atproto/api@0.2.9":
|
||||
version "0.2.9"
|
||||
resolved "https://registry.yarnpkg.com/@atproto/api/-/api-0.2.9.tgz#08e29da66d1a9001d9d3ce427548c1760d805e99"
|
||||
integrity sha512-r00IqidX2YF3VUEa4MUO2Vxqp3+QhI1cSNcWgzT4LsANapzrwdDTM+rY2Ejp9na3F+unO4SWRW3o434cVmG5gw==
|
||||
dependencies:
|
||||
"@atproto/common-web" "*"
|
||||
"@atproto/uri" "*"
|
||||
|
|
Loading…
Reference in New Issue