appview-less bluesky client
24
fork

Configure Feed

Select the types of activity you want to include in your feed.

dont show threads on following timeline if the root thread is not user we follow

dawn 30d6d992 f0963bf0

+33 -13
+11 -4
src/components/FollowingTimelineView.svelte
··· 12 12 allPosts, 13 13 followingFeed, 14 14 accountPreferences, 15 - fetchInteractionsToFollowingTimelineEnd 15 + fetchInteractionsToFollowingTimelineEnd, 16 + follows 16 17 } from '$lib/state.svelte'; 17 18 import Icon from '@iconify/svelte'; 18 19 import { buildThreads, filterThreads, type ThreadPost } from '$lib/thread'; ··· 45 46 const currentPrefs = $derived(userDid ? accountPreferences.get(userDid) : null); 46 47 const mutes = $derived(currentPrefs?.mutes ?? []); 47 48 48 - // We use userDid as the 'root' for buildThreads merely to provide a context, 49 - // but we are passing `followingFeed.get(userDid)` (merged set) as the source. 49 + const followedDids = $derived.by(() => { 50 + if (!userDid) return new Set<Did>(); 51 + const map = follows.get(userDid); 52 + if (!map) return new Set<Did>(); 53 + return new Set(Array.from(map.values()).map((f) => f.subject)); 54 + }); 55 + 50 56 const threads = $derived( 51 57 filterThreads( 52 58 userDid ··· 54 60 : [], 55 61 $accounts, 56 62 { 57 - viewOwnPosts 63 + viewOwnPosts, 64 + filterRootsToDids: followedDids 58 65 } 59 66 ) 60 67 );
+2 -7
src/lib/state.svelte.ts
··· 458 458 buffer.delete(uri); 459 459 } 460 460 461 - // If we had enough in buffer, return. If we exhausted buffer but needed more? 462 - // For simplicity, just return. The UI will call loadMore again if needed/short. 463 461 return; 464 462 } 465 463 466 464 const followsMap = follows.get(userDid); 467 465 const subjects = new Set<Did>(); 468 - if (followsMap) { 466 + if (followsMap) 469 467 for (const follow of followsMap.values()) subjects.add(follow.subject); 470 - } 471 468 subjects.add(userDid); 472 469 473 470 // 2. Find the "newest" cursor(s) ··· 529 526 if (res.cursor) userCursors!.set(did, res.cursor); 530 527 else userCursors!.set(did, null); // null = exhausted 531 528 532 - for (const record of res.records) { 533 - newPosts.push(record.uri); 534 - } 529 + for (const record of res.records) newPosts.push(record.uri); 535 530 } 536 531 537 532 if (newPosts.length === 0) return;
+20 -2
src/lib/thread.ts
··· 167 167 export const hasNonOwnPost = (posts: ThreadPost[], accounts: Account[]) => 168 168 posts.some((post) => !isOwnPost(post, accounts)); 169 169 170 - // todo: add more filtering options 171 170 export type FilterOptions = { 172 171 viewOwnPosts: boolean; 172 + filterRootsToDids?: Set<Did>; 173 173 }; 174 174 175 175 export const filterThreads = (threads: Thread[], accounts: Account[], opts: FilterOptions) => 176 176 threads.filter((thread) => { 177 177 if (thread.posts.length === 0) return false; 178 - if (!opts.viewOwnPosts) return hasNonOwnPost(thread.posts, accounts); 178 + if (!opts.viewOwnPosts) if (hasNonOwnPost(thread.posts, accounts)) return false; 179 + 180 + if (opts.filterRootsToDids) { 181 + const rootDid = extractDidFromUri(thread.rootUri); 182 + if ( 183 + rootDid && 184 + !opts.filterRootsToDids.has(rootDid) && 185 + !accounts.some((a) => a.did === rootDid) 186 + ) { 187 + return false; 188 + } 189 + } 190 + 179 191 return true; 180 192 }); 193 + 194 + // Helper to extract DID from URI if not already imported from elsewhere 195 + const extractDidFromUri = (uri: ResourceUri): Did | null => { 196 + const match = uri.match(/^at:\/\/(did:plc:[a-z0-9]+)/); 197 + return match ? (match[1] as Did) : null; 198 + };