Use a post and handle-resolution cache to enable quick postthread loading (#1097)
* Use a post and handle-resolution cache to enable quick postthread loading * Fix positioning of thread when loaded from cache and give more visual cues * Include parent posts in cache * Include notifications in cache
This commit is contained in:
parent
7256169506
commit
a63f97aef2
9 changed files with 167 additions and 18 deletions
5
src/state/models/cache/handle-resolutions.ts
vendored
Normal file
5
src/state/models/cache/handle-resolutions.ts
vendored
Normal file
|
@ -0,0 +1,5 @@
|
|||
import {LRUMap} from 'lru_map'
|
||||
|
||||
export class HandleResolutionsCache {
|
||||
cache: LRUMap<string, string> = new LRUMap(500)
|
||||
}
|
31
src/state/models/cache/posts.ts
vendored
Normal file
31
src/state/models/cache/posts.ts
vendored
Normal file
|
@ -0,0 +1,31 @@
|
|||
import {LRUMap} from 'lru_map'
|
||||
import {RootStoreModel} from '../root-store'
|
||||
import {AppBskyFeedDefs} from '@atproto/api'
|
||||
|
||||
type PostView = AppBskyFeedDefs.PostView
|
||||
|
||||
export class PostsCache {
|
||||
cache: LRUMap<string, PostView> = new LRUMap(500)
|
||||
|
||||
constructor(public rootStore: RootStoreModel) {}
|
||||
|
||||
set(uri: string, postView: PostView) {
|
||||
this.cache.set(uri, postView)
|
||||
if (postView.author.handle) {
|
||||
this.rootStore.handleResolutions.cache.set(
|
||||
postView.author.handle,
|
||||
postView.author.did,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fromFeedItem(feedItem: AppBskyFeedDefs.FeedViewPost) {
|
||||
this.set(feedItem.post.uri, feedItem.post)
|
||||
if (
|
||||
feedItem.reply?.parent &&
|
||||
AppBskyFeedDefs.isPostView(feedItem.reply?.parent)
|
||||
) {
|
||||
this.set(feedItem.reply.parent.uri, feedItem.reply.parent)
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ import {PostThreadItemModel} from './post-thread-item'
|
|||
export class PostThreadModel {
|
||||
// state
|
||||
isLoading = false
|
||||
isLoadingFromCache = false
|
||||
isFromCache = false
|
||||
isRefreshing = false
|
||||
hasLoaded = false
|
||||
error = ''
|
||||
|
@ -20,7 +22,7 @@ export class PostThreadModel {
|
|||
params: GetPostThread.QueryParams
|
||||
|
||||
// data
|
||||
thread?: PostThreadItemModel
|
||||
thread?: PostThreadItemModel | null = null
|
||||
isBlocked = false
|
||||
|
||||
constructor(
|
||||
|
@ -52,7 +54,7 @@ export class PostThreadModel {
|
|||
}
|
||||
|
||||
get hasContent() {
|
||||
return typeof this.thread !== 'undefined'
|
||||
return !!this.thread
|
||||
}
|
||||
|
||||
get hasError() {
|
||||
|
@ -82,10 +84,16 @@ export class PostThreadModel {
|
|||
if (!this.resolvedUri) {
|
||||
await this._resolveUri()
|
||||
}
|
||||
|
||||
if (this.hasContent) {
|
||||
await this.update()
|
||||
} else {
|
||||
await this._load()
|
||||
const precache = this.rootStore.posts.cache.get(this.resolvedUri)
|
||||
if (precache) {
|
||||
await this._loadPrecached(precache)
|
||||
} else {
|
||||
await this._load()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -169,6 +177,37 @@ export class PostThreadModel {
|
|||
})
|
||||
}
|
||||
|
||||
async _loadPrecached(precache: AppBskyFeedDefs.PostView) {
|
||||
// start with the cached version
|
||||
this.isLoadingFromCache = true
|
||||
this.isFromCache = true
|
||||
this._replaceAll({
|
||||
success: true,
|
||||
headers: {},
|
||||
data: {
|
||||
thread: {
|
||||
post: precache,
|
||||
},
|
||||
},
|
||||
})
|
||||
this._xIdle()
|
||||
|
||||
// then update in the background
|
||||
try {
|
||||
const res = await this.rootStore.agent.getPostThread(
|
||||
Object.assign({}, this.params, {uri: this.resolvedUri}),
|
||||
)
|
||||
this._replaceAll(res)
|
||||
} catch (e: any) {
|
||||
console.log(e)
|
||||
this._xIdle(e)
|
||||
} finally {
|
||||
runInAction(() => {
|
||||
this.isLoadingFromCache = false
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
async _load(isRefreshing = false) {
|
||||
if (this.hasLoaded && !isRefreshing) {
|
||||
return
|
||||
|
|
|
@ -253,6 +253,12 @@ export class ProfileModel {
|
|||
try {
|
||||
const res = await this.rootStore.agent.getProfile(this.params)
|
||||
this.rootStore.profiles.overwrite(this.params.actor, res) // cache invalidation
|
||||
if (res.data.handle) {
|
||||
this.rootStore.handleResolutions.cache.set(
|
||||
res.data.handle,
|
||||
res.data.did,
|
||||
)
|
||||
}
|
||||
this._replaceAll(res)
|
||||
await this._createRichText()
|
||||
this._xIdle()
|
||||
|
|
|
@ -503,7 +503,9 @@ export class NotificationsFeedModel {
|
|||
const postsRes = await this.rootStore.agent.app.bsky.feed.getPosts({
|
||||
uris: [addedUri],
|
||||
})
|
||||
notif.setAdditionalData(postsRes.data.posts[0])
|
||||
const post = postsRes.data.posts[0]
|
||||
notif.setAdditionalData(post)
|
||||
this.rootStore.posts.set(post.uri, post)
|
||||
}
|
||||
const filtered = this._filterNotifications([notif])
|
||||
return filtered[0]
|
||||
|
@ -611,6 +613,7 @@ export class NotificationsFeedModel {
|
|||
),
|
||||
)
|
||||
for (const post of postsChunks.flat()) {
|
||||
this.rootStore.posts.set(post.uri, post)
|
||||
const models = addedPostMap.get(post.uri)
|
||||
if (models?.length) {
|
||||
for (const model of models) {
|
||||
|
|
|
@ -374,6 +374,9 @@ export class PostsFeedModel {
|
|||
this.rootStore.me.follows.hydrateProfiles(
|
||||
res.data.feed.map(item => item.post.author),
|
||||
)
|
||||
for (const item of res.data.feed) {
|
||||
this.rootStore.posts.fromFeedItem(item)
|
||||
}
|
||||
|
||||
const slices = this.tuner.tune(res.data.feed, this.feedTuners)
|
||||
|
||||
|
@ -405,6 +408,7 @@ export class PostsFeedModel {
|
|||
res: GetTimeline.Response | GetAuthorFeed.Response | GetCustomFeed.Response,
|
||||
) {
|
||||
for (const item of res.data.feed) {
|
||||
this.rootStore.posts.fromFeedItem(item)
|
||||
const existingSlice = this.slices.find(slice =>
|
||||
slice.containsUri(item.post.uri),
|
||||
)
|
||||
|
|
|
@ -12,7 +12,9 @@ import {isObj, hasProp} from 'lib/type-guards'
|
|||
import {LogModel} from './log'
|
||||
import {SessionModel} from './session'
|
||||
import {ShellUiModel} from './ui/shell'
|
||||
import {HandleResolutionsCache} from './cache/handle-resolutions'
|
||||
import {ProfilesCache} from './cache/profiles-view'
|
||||
import {PostsCache} from './cache/posts'
|
||||
import {LinkMetasCache} from './cache/link-metas'
|
||||
import {NotificationsFeedItemModel} from './feeds/notifications'
|
||||
import {MeModel} from './me'
|
||||
|
@ -45,7 +47,9 @@ export class RootStoreModel {
|
|||
preferences = new PreferencesModel(this)
|
||||
me = new MeModel(this)
|
||||
invitedUsers = new InvitedUsers(this)
|
||||
handleResolutions = new HandleResolutionsCache()
|
||||
profiles = new ProfilesCache(this)
|
||||
posts = new PostsCache(this)
|
||||
linkMetas = new LinkMetasCache(this)
|
||||
imageSizes = new ImageSizesCache()
|
||||
mutedThreads = new MutedThreads()
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue