···1212 allPosts,
1313 followingFeed,
1414 accountPreferences,
1515- fetchInteractionsToFollowingTimelineEnd
1515+ fetchInteractionsToFollowingTimelineEnd,
1616+ follows
1617 } from '$lib/state.svelte';
1718 import Icon from '@iconify/svelte';
1819 import { buildThreads, filterThreads, type ThreadPost } from '$lib/thread';
···4546 const currentPrefs = $derived(userDid ? accountPreferences.get(userDid) : null);
4647 const mutes = $derived(currentPrefs?.mutes ?? []);
47484848- // We use userDid as the 'root' for buildThreads merely to provide a context,
4949- // but we are passing `followingFeed.get(userDid)` (merged set) as the source.
4949+ const followedDids = $derived.by(() => {
5050+ if (!userDid) return new Set<Did>();
5151+ const map = follows.get(userDid);
5252+ if (!map) return new Set<Did>();
5353+ return new Set(Array.from(map.values()).map((f) => f.subject));
5454+ });
5555+5056 const threads = $derived(
5157 filterThreads(
5258 userDid
···5460 : [],
5561 $accounts,
5662 {
5757- viewOwnPosts
6363+ viewOwnPosts,
6464+ filterRootsToDids: followedDids
5865 }
5966 )
6067 );
+2-7
src/lib/state.svelte.ts
···458458 buffer.delete(uri);
459459 }
460460461461- // If we had enough in buffer, return. If we exhausted buffer but needed more?
462462- // For simplicity, just return. The UI will call loadMore again if needed/short.
463461 return;
464462 }
465463466464 const followsMap = follows.get(userDid);
467465 const subjects = new Set<Did>();
468468- if (followsMap) {
466466+ if (followsMap)
469467 for (const follow of followsMap.values()) subjects.add(follow.subject);
470470- }
471468 subjects.add(userDid);
472469473470 // 2. Find the "newest" cursor(s)
···529526 if (res.cursor) userCursors!.set(did, res.cursor);
530527 else userCursors!.set(did, null); // null = exhausted
531528532532- for (const record of res.records) {
533533- newPosts.push(record.uri);
534534- }
529529+ for (const record of res.records) newPosts.push(record.uri);
535530 }
536531537532 if (newPosts.length === 0) return;
+20-2
src/lib/thread.ts
···167167export const hasNonOwnPost = (posts: ThreadPost[], accounts: Account[]) =>
168168 posts.some((post) => !isOwnPost(post, accounts));
169169170170-// todo: add more filtering options
171170export type FilterOptions = {
172171 viewOwnPosts: boolean;
172172+ filterRootsToDids?: Set<Did>;
173173};
174174175175export const filterThreads = (threads: Thread[], accounts: Account[], opts: FilterOptions) =>
176176 threads.filter((thread) => {
177177 if (thread.posts.length === 0) return false;
178178- if (!opts.viewOwnPosts) return hasNonOwnPost(thread.posts, accounts);
178178+ if (!opts.viewOwnPosts) if (hasNonOwnPost(thread.posts, accounts)) return false;
179179+180180+ if (opts.filterRootsToDids) {
181181+ const rootDid = extractDidFromUri(thread.rootUri);
182182+ if (
183183+ rootDid &&
184184+ !opts.filterRootsToDids.has(rootDid) &&
185185+ !accounts.some((a) => a.did === rootDid)
186186+ ) {
187187+ return false;
188188+ }
189189+ }
190190+179191 return true;
180192 });
193193+194194+// Helper to extract DID from URI if not already imported from elsewhere
195195+const extractDidFromUri = (uri: ResourceUri): Did | null => {
196196+ const match = uri.match(/^at:\/\/(did:plc:[a-z0-9]+)/);
197197+ return match ? (match[1] as Did) : null;
198198+};