From 4bfdd92b65c1094ebf5eb361023b102104d1b4df Mon Sep 17 00:00:00 2001 From: dawn <90008@gaze.systems> Date: Wed, 7 Jan 2026 07:42:15 +0300 Subject: [PATCH] on other user profiles, only fetch thread chains upwards, not downwards --- src/components/ProfileView.svelte | 1 + src/components/TimelineView.svelte | 4 ++- src/lib/at/fetch.ts | 57 +++++++++++++++++------------- src/lib/state.svelte.ts | 13 +++++-- 4 files changed, 46 insertions(+), 29 deletions(-) diff --git a/src/components/ProfileView.svelte b/src/components/ProfileView.svelte index 8463dee..669fcd1 100644 --- a/src/components/ProfileView.svelte +++ b/src/components/ProfileView.svelte @@ -146,6 +146,7 @@ Ok | undefined + cacheFn: (did: Did, rkey: RecordKey) => Ok | undefined, + options?: Partial ): Promise, string>> => { let posts: Map = new Map(); try { @@ -115,33 +120,35 @@ export const hydratePosts = async ( }; await Promise.all(posts.values().map(fetchUpwardsChain)); - try { - const fetchDownwardsChain = async (post: PostWithUri) => { - const { repo: postRepo } = expect(parseCanonicalResourceUri(post.uri)); - if (repo === postRepo) return; + if (options?.downwards !== 'none') { + try { + const fetchDownwardsChain = async (post: PostWithUri) => { + const { repo: postRepo } = expect(parseCanonicalResourceUri(post.uri)); + if (repo === postRepo) return; - // get chains that are the same author until we exhaust them - const backlinks = await client.getBacklinks(post.uri, replySource); - if (!backlinks.ok) return; + // get chains that are the same author until we exhaust them + const backlinks = await client.getBacklinks(post.uri, replySource); + if (!backlinks.ok) return; - const promises = []; - for (const reply of backlinks.value.records) { - if (reply.did !== postRepo) continue; - // if we already have this reply, then we already fetched this chain / are fetching it - if (posts.has(toCanonicalUri(reply))) continue; - const record = - cacheFn(reply.did, reply.rkey) ?? - (await client.getRecord(AppBskyFeedPost.mainSchema, reply.did, reply.rkey)); - if (!record.ok) break; // TODO: this doesnt handle deleted posts in between - posts.set(record.value.uri, record.value); - promises.push(fetchDownwardsChain(record.value)); - } + const promises = []; + for (const reply of backlinks.value.records) { + if (reply.did !== postRepo) continue; + // if we already have this reply, then we already fetched this chain / are fetching it + if (posts.has(toCanonicalUri(reply))) continue; + const record = + cacheFn(reply.did, reply.rkey) ?? + (await client.getRecord(AppBskyFeedPost.mainSchema, reply.did, reply.rkey)); + if (!record.ok) break; // TODO: this doesnt handle deleted posts in between + posts.set(record.value.uri, record.value); + promises.push(fetchDownwardsChain(record.value)); + } - await Promise.all(promises); - }; - await Promise.all(posts.values().map(fetchDownwardsChain)); - } catch (error) { - return err(`cant fetch post reply chain: ${error}`); + await Promise.all(promises); + }; + await Promise.all(posts.values().map(fetchDownwardsChain)); + } catch (error) { + return err(`cant fetch post reply chain: ${error}`); + } } return ok(posts); diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index e757a53..6819e27 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -7,7 +7,7 @@ import { } from './at/client.svelte'; import { SvelteMap, SvelteDate, SvelteSet } from 'svelte/reactivity'; import type { Did, Handle, Nsid, RecordKey, ResourceUri } from '@atcute/lexicons'; -import { fetchPosts, hydratePosts, type PostWithUri } from './at/fetch'; +import { fetchPosts, hydratePosts, type HydrateOptions, type PostWithUri } from './at/fetch'; import { parseCanonicalResourceUri, type AtprotoDid } from '@atcute/lexicons/syntax'; import { AppBskyActorProfile, @@ -479,7 +479,8 @@ export const fetchTimeline = async ( client: AtpClient, subject: Did, limit: number = 6, - withBacklinks: boolean = true + withBacklinks: boolean = true, + hydrateOptions?: Partial ) => { const cursor = postCursors.get(subject); if (cursor && cursor.end) return; @@ -490,7 +491,13 @@ export const fetchTimeline = async ( // if the cursor is undefined, we've reached the end of the timeline const newCursor = { value: accPosts.value.cursor, end: !accPosts.value.cursor }; postCursors.set(subject, newCursor); - const hydrated = await hydratePosts(client, subject, accPosts.value.posts, hydrateCacheFn); + const hydrated = await hydratePosts( + client, + subject, + accPosts.value.posts, + hydrateCacheFn, + hydrateOptions + ); if (!hydrated.ok) throw `cant hydrate posts ${subject}: ${hydrated.error}`; addPosts(hydrated.value.values()); -- 2.51.2