From 30d6d9929871bcd8e87eb806d84e4b04619a84fb Mon Sep 17 00:00:00 2001 From: dawn <90008@gaze.systems> Date: Wed, 21 Jan 2026 11:12:22 +0300 Subject: [PATCH] dont show threads on following timeline if the root thread is not user we follow --- src/components/FollowingTimelineView.svelte | 15 ++++++++++---- src/lib/state.svelte.ts | 9 ++------- src/lib/thread.ts | 22 +++++++++++++++++++-- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/src/components/FollowingTimelineView.svelte b/src/components/FollowingTimelineView.svelte index 25648d7..094779d 100644 --- a/src/components/FollowingTimelineView.svelte +++ b/src/components/FollowingTimelineView.svelte @@ -12,7 +12,8 @@ allPosts, followingFeed, accountPreferences, - fetchInteractionsToFollowingTimelineEnd + fetchInteractionsToFollowingTimelineEnd, + follows } from '$lib/state.svelte'; import Icon from '@iconify/svelte'; import { buildThreads, filterThreads, type ThreadPost } from '$lib/thread'; @@ -45,8 +46,13 @@ const currentPrefs = $derived(userDid ? accountPreferences.get(userDid) : null); const mutes = $derived(currentPrefs?.mutes ?? []); - // We use userDid as the 'root' for buildThreads merely to provide a context, - // but we are passing `followingFeed.get(userDid)` (merged set) as the source. + const followedDids = $derived.by(() => { + if (!userDid) return new Set(); + const map = follows.get(userDid); + if (!map) return new Set(); + return new Set(Array.from(map.values()).map((f) => f.subject)); + }); + const threads = $derived( filterThreads( userDid @@ -54,7 +60,8 @@ : [], $accounts, { - viewOwnPosts + viewOwnPosts, + filterRootsToDids: followedDids } ) ); diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index a12a072..a25f223 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -458,16 +458,13 @@ export const fetchFollowingTimeline = async (client: AtpClient, targetDid?: Did, buffer.delete(uri); } - // If we had enough in buffer, return. If we exhausted buffer but needed more? - // For simplicity, just return. The UI will call loadMore again if needed/short. return; } const followsMap = follows.get(userDid); const subjects = new Set(); - if (followsMap) { + if (followsMap) for (const follow of followsMap.values()) subjects.add(follow.subject); - } subjects.add(userDid); // 2. Find the "newest" cursor(s) @@ -529,9 +526,7 @@ export const fetchFollowingTimeline = async (client: AtpClient, targetDid?: Did, if (res.cursor) userCursors!.set(did, res.cursor); else userCursors!.set(did, null); // null = exhausted - for (const record of res.records) { - newPosts.push(record.uri); - } + for (const record of res.records) newPosts.push(record.uri); } if (newPosts.length === 0) return; diff --git a/src/lib/thread.ts b/src/lib/thread.ts index 1cc1b4f..fa7a2bb 100644 --- a/src/lib/thread.ts +++ b/src/lib/thread.ts @@ -167,14 +167,32 @@ export const isOwnPost = (post: ThreadPost, accounts: Account[]) => export const hasNonOwnPost = (posts: ThreadPost[], accounts: Account[]) => posts.some((post) => !isOwnPost(post, accounts)); -// todo: add more filtering options export type FilterOptions = { viewOwnPosts: boolean; + filterRootsToDids?: Set; }; export const filterThreads = (threads: Thread[], accounts: Account[], opts: FilterOptions) => threads.filter((thread) => { if (thread.posts.length === 0) return false; - if (!opts.viewOwnPosts) return hasNonOwnPost(thread.posts, accounts); + if (!opts.viewOwnPosts) if (hasNonOwnPost(thread.posts, accounts)) return false; + + if (opts.filterRootsToDids) { + const rootDid = extractDidFromUri(thread.rootUri); + if ( + rootDid && + !opts.filterRootsToDids.has(rootDid) && + !accounts.some((a) => a.did === rootDid) + ) { + return false; + } + } + return true; }); + +// Helper to extract DID from URI if not already imported from elsewhere +const extractDidFromUri = (uri: ResourceUri): Did | null => { + const match = uri.match(/^at:\/\/(did:plc:[a-z0-9]+)/); + return match ? (match[1] as Did) : null; +}; -- 2.51.2