bluesky client without react native baggage written in sveltekit
0
fork

Configure Feed

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

improved cache for feed items

ansxor 4067f307 5bd38bea

+51 -14
+16 -2
src/routes/feed/[[aturl]]/+layout.svelte
··· 1 1 <script lang="ts"> 2 2 import { page } from '$app/state'; 3 + import { invalidate } from '$app/navigation'; 4 + import { clearCacheEntry } from './feed-cache'; 3 5 let { children, data } = $props(); 6 + 7 + function handleFeedItemClick(href: string, key: string) { 8 + if (page.url.pathname === href) { 9 + clearCacheEntry(key); 10 + invalidate(`feed:${key}`); 11 + document.querySelector('.layout')?.scrollTo({ top: 0, behavior: 'smooth' }); 12 + } 13 + } 4 14 </script> 5 15 6 16 <div 7 17 class="sticky top-0 z-10 flex overflow-x-auto border-x border-b border-post-border bg-body-background" 8 18 > 9 - {#each data.items as feedItem} 19 + {#each data.items as feedItem (feedItem.type + feedItem.value)} 10 20 {#if feedItem.pinned} 11 21 {#if feedItem.type === 'timeline' && feedItem.value === 'following'} 12 - <a href="/feed" class="feedItem {!page.params.aturl ? 'feedItem--selected' : ''}" 22 + <a 23 + href="/feed" 24 + onclick={() => handleFeedItemClick('/feed', 'timeline')} 25 + class="feedItem {!page.params.aturl ? 'feedItem--selected' : ''}" 13 26 >Following</a 14 27 > 15 28 {:else if feedItem.type === 'feed' && data.feedMap[feedItem.value]} 16 29 <a 17 30 href="/feed/{encodeURIComponent(feedItem.value)}" 31 + onclick={() => handleFeedItemClick(`/feed/${encodeURIComponent(feedItem.value)}`, feedItem.value)} 18 32 class="feedItem {page.params.aturl === feedItem.value ? 'feedItem--selected' : ''}" 19 33 >{data.feedMap[feedItem.value]?.displayName ?? feedItem.value}</a 20 34 >
+29 -11
src/routes/feed/[[aturl]]/+page.svelte
··· 1 1 <script lang="ts"> 2 2 import Post from '$lib/components/Post.svelte'; 3 + import { page } from '$app/state'; 3 4 4 5 let { data } = $props(); 6 + let resolvedFeed: any[] = $state([]); 7 + let lastKey: string | undefined = $state(undefined); 8 + 9 + $effect(() => { 10 + const key = page.params.aturl ?? 'timeline'; 11 + const feed = data.feed; 12 + const isTabSwitch = key !== lastKey; 13 + 14 + if (feed instanceof Promise) { 15 + if (isTabSwitch) { 16 + resolvedFeed = []; 17 + lastKey = key; 18 + } 19 + feed.then((result) => { 20 + // Only update if we're still on the same tab 21 + const currentKey = page.params.aturl ?? 'timeline'; 22 + if (currentKey === key) { 23 + resolvedFeed = result.data.feed; 24 + } 25 + }); 26 + } else { 27 + lastKey = key; 28 + resolvedFeed = feed.data.feed; 29 + } 30 + }); 5 31 </script> 6 32 7 - {#if data.feed instanceof Promise} 8 - {#await data.feed} 9 - <p>Loading...</p> 10 - {:then feedData} 11 - {#each feedData.data.feed as entry, i (i)} 12 - <Post post={entry.post} /> 13 - {/each} 14 - {:catch error} 15 - <p>Error: {error.message}</p> 16 - {/await} 33 + {#if resolvedFeed.length === 0} 34 + <p>Loading...</p> 17 35 {:else} 18 - {#each data.feed.data.feed as entry, i (i)} 36 + {#each resolvedFeed as entry, i (i)} 19 37 <Post post={entry.post} /> 20 38 {/each} 21 39 {/if}
+1 -1
src/routes/feed/[[aturl]]/+page.ts
··· 1 1 import { getClient } from '$lib/atproto'; 2 2 import type { ResourceUri } from '@atcute/lexicons'; 3 - const cache = new Map(); 3 + import { cache } from './feed-cache'; 4 4 5 5 export async function load({ params, depends }) { 6 6 depends(`feed:${params.aturl ?? 'timeline'}`);
+5
src/routes/feed/[[aturl]]/feed-cache.ts
··· 1 + export const cache = new Map(); 2 + 3 + export function clearCacheEntry(key: string) { 4 + cache.delete(key); 5 + }