survive session-bundle rebuild in chat, feeds, prefs, and age assurance
Review fixes (PR #11182 round 2). Client identity is no longer stable across token refresh - on web, a cross-tab sync rebuilds the bundle and disposes the old clients (whose fetch now throws) - so anything that captured a client at construction time must re-point at the live one: - MessagesEventBus and Convo get updateClient(); providers sync it via effect. No remount, so poll cursors and optimistic pendingMessages survive. - FeedAPI implementations get setClient(); the post-feed queryFn and pollLatest re-point page-held apis before fetching (Merge/Home apis are stateful across pages, so they cannot be rebuilt per fetch). - usePreferencesQuery applies fetched labeler dids to the live appview client (applyLabelersToClient, factored from session/moderation), restoring the old BskyAgent.getPreferences header side effect. - ageAssurance redirect overlay/dialog polling is mount-only with render-synced refs; a client swap mid-poll no longer latches the unmounted flag and strands the overlay. - Convo message-failure classification defers to lex's shouldRetry() instead of treating all status-less errors as recoverable. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -19,6 +19,10 @@ export class AuthorFeedAPI implements FeedAPI {
|
||||
this._params = feedParams
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
get params() {
|
||||
const params = {...this._params}
|
||||
params.includePins = params.filter === 'posts_and_author_threads'
|
||||
|
||||
@@ -37,6 +37,10 @@ export class CustomFeedAPI implements FeedAPI {
|
||||
this.userInterests = userInterests
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
const contentLangs = getContentLanguages().join(',')
|
||||
const res = await this.client.call(
|
||||
|
||||
@@ -11,6 +11,10 @@ export class DemoFeedAPI implements FeedAPI {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/require-await
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
return DEMO_FEED.feed[0]
|
||||
|
||||
@@ -10,6 +10,10 @@ export class FollowingFeedAPI implements FeedAPI {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
const res = await this.client.call(app.bsky.feed.getTimeline, {
|
||||
limit: 1,
|
||||
|
||||
@@ -56,6 +56,12 @@ export class HomeFeedAPI implements FeedAPI {
|
||||
this.userInterests = userInterests
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
this.following.setClient(client)
|
||||
this.discover.setClient(client)
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.following = new FollowingFeedAPI({client: this.client})
|
||||
this.discover = new CustomFeedAPI({
|
||||
|
||||
@@ -18,6 +18,10 @@ export class LikesFeedAPI implements FeedAPI {
|
||||
this.params = feedParams
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
const res = await this.client.call(app.bsky.feed.getActorLikes, {
|
||||
...this.params,
|
||||
|
||||
@@ -18,6 +18,10 @@ export class ListFeedAPI implements FeedAPI {
|
||||
this.params = feedParams
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
const res = await this.client.call(app.bsky.feed.getListFeed, {
|
||||
...this.params,
|
||||
|
||||
@@ -64,6 +64,14 @@ export class MergeFeedAPI implements FeedAPI {
|
||||
})
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
this.following.setClient(client)
|
||||
for (const feed of this.customFeeds) {
|
||||
feed.setClient(client)
|
||||
}
|
||||
}
|
||||
|
||||
reset() {
|
||||
this.following = new MergeFeedSource_Following({
|
||||
client: this.client,
|
||||
@@ -203,6 +211,10 @@ class MergeFeedSource {
|
||||
this.feedTuners = feedTuners
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
get numReady() {
|
||||
return this.queue.length
|
||||
}
|
||||
|
||||
@@ -27,6 +27,10 @@ export class PostListFeedAPI implements FeedAPI {
|
||||
}
|
||||
}
|
||||
|
||||
setClient(client: Client) {
|
||||
this.client = client
|
||||
}
|
||||
|
||||
async peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost> {
|
||||
if (this.peek) return this.peek
|
||||
throw new Error('Has not fetched yet')
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import {type Client} from '@atproto/lex-client'
|
||||
|
||||
import {type app} from '#/lexicons'
|
||||
|
||||
export interface FeedAPIResponse {
|
||||
@@ -6,6 +8,13 @@ export interface FeedAPIResponse {
|
||||
}
|
||||
|
||||
export interface FeedAPI {
|
||||
/**
|
||||
* Swap in a fresh client. Feed pages retain their FeedAPI across paginations,
|
||||
* so the client captured at construction goes stale after a session-bundle
|
||||
* rebuild (web cross-tab token sync). Callers re-point the api at the current
|
||||
* client before each fetch/peek so a disposed client is never used.
|
||||
*/
|
||||
setClient(client: Client): void
|
||||
peekLatest(): Promise<app.bsky.feed.defs.FeedViewPost>
|
||||
fetch({
|
||||
cursor,
|
||||
|
||||
Reference in New Issue
Block a user