diff --git a/src/components/FeedTimelineView.svelte b/src/components/FeedTimelineView.svelte index 4f38bf7..b1845ad 100644 --- a/src/components/FeedTimelineView.svelte +++ b/src/components/FeedTimelineView.svelte @@ -46,6 +46,7 @@ selectedFeed; feedServiceDid = null; newPostsAvailable = false; + displayCount = 15; loaderState.reset(); fetchFeedGenerator(client ?? viewClient, selectedFeed).then((meta) => { feedServiceDid = meta?.did ?? null; @@ -78,11 +79,14 @@ if (!userDid) return; scrollContainer?.scrollTo({ top: 0, behavior: 'smooth' }); newPostsAvailable = false; + displayCount = 15; resetFeed(userDid, selectedFeed); loaderState.reset(); loadMore(); }; + let displayCount = $state(15); + const feedPosts = $derived.by(() => { if (!userDid) return []; const uris = feedTimelines.get(userDid)?.get(selectedFeed) ?? []; @@ -94,6 +98,8 @@ .filter((p): p is NonNullable => p !== undefined); }); + const renderedPosts = $derived(feedPosts.slice(0, displayCount)); + const loadMore = async () => { if (loading || !client || !userDid || !feedServiceDid) return; @@ -101,22 +107,28 @@ loaderState.status = 'LOADING'; try { - const result = await fetchFeed(client, selectedFeed, feedServiceDid); - - if (client.user && userDid) { - if (!fetchingInteractions) { - scheduledFetchInteractions = false; - fetchingInteractions = true; - await fetchInteractionsToFeedTimelineEnd(client, userDid, selectedFeed); - fetchingInteractions = false; - } else { - scheduledFetchInteractions = true; + displayCount += 10; + const bufferSize = feedPosts.length - displayCount; + const cursor = feedCursors.get(userDid)?.get(selectedFeed); + + if (bufferSize < 5 && !cursor?.end) { + const result = await fetchFeed(client, selectedFeed, feedServiceDid); + if (client.user && userDid) { + if (!fetchingInteractions) { + scheduledFetchInteractions = false; + fetchingInteractions = true; + await fetchInteractionsToFeedTimelineEnd(client, userDid, selectedFeed); + fetchingInteractions = false; + } else { + scheduledFetchInteractions = true; + } } + console.log('feed loaded', result?.end); + if (result?.end) loaderState.complete(); + } else { + if (cursor?.end && displayCount >= feedPosts.length) loaderState.complete(); } - loaderState.loaded(); - console.log('feed loaded', result?.end); - if (result?.end) loaderState.complete(); } catch (error) { loadError = `${error}`; loaderState.error(); @@ -135,7 +147,7 @@ {#snippet feedPostsView()} - {#each feedPosts as post, i (post.uri)} + {#each renderedPosts as post, i (post.uri)} {@const uriParts = post.uri.split('/')} {@const postDid = uriParts[2] as Did} {@const postRkey = uriParts[4] as RecordKey} @@ -155,7 +167,7 @@ }} /> - {#if i < feedPosts.length - 1} + {#if i < renderedPosts.length - 1}
diff --git a/src/components/FollowingTimelineView.svelte b/src/components/FollowingTimelineView.svelte index c57c742..715378b 100644 --- a/src/components/FollowingTimelineView.svelte +++ b/src/components/FollowingTimelineView.svelte @@ -119,6 +119,7 @@ 0)} diff --git a/src/components/GenericTimelineView.svelte b/src/components/GenericTimelineView.svelte index d3e6b03..3c60a60 100644 --- a/src/components/GenericTimelineView.svelte +++ b/src/components/GenericTimelineView.svelte @@ -18,6 +18,7 @@ interface Props { client?: AtpClient | null; threads: Thread[]; + timelineId?: string; postComposerState: PostComposerState; class?: string; isLoggedIn?: boolean; // Controls rendering of the list vs NotLoggedIn @@ -29,6 +30,7 @@ let { client = null, threads, + timelineId = undefined, postComposerState = $bindable(), class: className = '', isLoggedIn = false, @@ -50,6 +52,14 @@ return threads.filter((t) => t.newestTime <= boundaryTime!); }); + let displayCount = $state(15); + $effect(() => { + timelineId; + displayCount = 15; + }); + + const renderedThreads = $derived(visibleThreads.slice(0, displayCount)); + $effect(() => { if (threads.length > 0) { if (isAtTop) boundaryTime = threads[0].newestTime; @@ -77,9 +87,13 @@ loadError = ''; try { - await onLoadMore(); + displayCount += 10; + const bufferSize = visibleThreads.length - displayCount; + + if (bufferSize < 5 && !isComplete) await onLoadMore(); + loaderState.loaded(); - if (isComplete) loaderState.complete(); + if (isComplete && displayCount >= visibleThreads.length) loaderState.complete(); } catch (error) { loadError = `${error}`; loaderState.error(); @@ -116,7 +130,7 @@ {/snippet} {#snippet threadsView()} - {#each visibleThreads as thread, i (thread.rootUri)} + {#each renderedThreads as thread, i (thread.rootUri)}
{#if thread.branchParentPost} {@render replyPost(thread.branchParentPost)} @@ -165,7 +179,7 @@ {/if} {/each}
- {#if i < visibleThreads.length - 1} + {#if i < renderedThreads.length - 1}
diff --git a/src/components/ReplyTimelineView.svelte b/src/components/ReplyTimelineView.svelte index c0f9e36..3937b11 100644 --- a/src/components/ReplyTimelineView.svelte +++ b/src/components/ReplyTimelineView.svelte @@ -85,6 +85,7 @@ 0)} diff --git a/src/lib/state.svelte.ts b/src/lib/state.svelte.ts index 0a68aca..75c34e7 100644 --- a/src/lib/state.svelte.ts +++ b/src/lib/state.svelte.ts @@ -919,13 +919,13 @@ export const fetchInteractionsToFollowingTimelineEnd = async ( const feed = followingFeed.get(userDid); if (!feed || feed.size === 0) return; - let minTimestamp = Date.now() * 1000; + let minTimestamp = Date.now(); let found = false; for (const uri of feed) { const post = getPostFromUri(uri); if (post) { - const ts = new Date(post.record.createdAt).getTime() * 1000; + const ts = new Date(post.record.createdAt).getTime(); if (ts < minTimestamp) minTimestamp = ts; found = true; } @@ -948,13 +948,13 @@ export const fetchInteractionsToFeedTimelineEnd = async ( const posts = userFeedTimelines.get(feedUri); if (!posts || posts.length === 0) return; - let minTimestamp = Date.now() * 1000; + let minTimestamp = Date.now(); let found = false; for (const uri of posts) { const post = getPostFromUri(uri); if (post) { - const ts = new Date(post.record.createdAt).getTime() * 1000; + const ts = new Date(post.record.createdAt).getTime(); if (ts < minTimestamp) minTimestamp = ts; found = true; }