From 4441338574ae16e0d8c8f479385b5be2917d7bdc Mon Sep 17 00:00:00 2001 From: "Timothy J. Baek" Date: Sun, 4 Aug 2024 15:58:36 +0200 Subject: [PATCH] refac: onScroll -> IntersectionObserver for infinite scroll --- src/lib/components/layout/Sidebar.svelte | 91 ++++++++++++++---------- src/lib/stores/index.ts | 1 + 2 files changed, 56 insertions(+), 36 deletions(-) diff --git a/src/lib/components/layout/Sidebar.svelte b/src/lib/components/layout/Sidebar.svelte index 0095a8fcd..07dac20f0 100644 --- a/src/lib/components/layout/Sidebar.svelte +++ b/src/lib/components/layout/Sidebar.svelte @@ -39,7 +39,7 @@ import UserMenu from './Sidebar/UserMenu.svelte'; import ChatItem from './Sidebar/ChatItem.svelte'; import DeleteConfirmDialog from '$lib/components/common/ConfirmDialog.svelte'; - import Sparkles from '../icons/Sparkles.svelte'; + import Spinner from '../common/Spinner.svelte'; const BREAKPOINT = 768; @@ -58,10 +58,8 @@ let paginationScrollThreashold = 0.6; let nextPageLoading = false; let chatPagniationComplete = false; - // number of chats per page depends on screen size. - // 35px is the height of each chat item. - // load 5 extra chats - pageLimit.set(Math.round(window.innerHeight / 35) + 5); + + pageLimit.set(20); $: filteredChatList = $chats.filter((chat) => { if (search === '') { @@ -153,6 +151,48 @@ window.addEventListener('focus', onFocus); window.addEventListener('blur', onBlur); + // Infinite scroll + const loader = document.getElementById('loader'); + + const observer = new IntersectionObserver( + (entries, observer) => { + entries.forEach((entry) => { + if (entry.isIntersecting) { + loadMoreContent(); + observer.unobserve(loader); // Stop observing until content is loaded + } + }); + }, + { + root: null, // viewport + rootMargin: '0px', + threshold: 1.0 // When 100% of the loader is visible + } + ); + + observer.observe(loader); + const loadMoreContent = async () => { + if (!$scrollPaginationEnabled) return; + if ($tagView) return; + if (nextPageLoading) return; + if (chatPagniationComplete) return; + + nextPageLoading = true; + pageSkip.set($pageSkip + 1); + // extend existing chats + const nextPageChats = await getChatList( + localStorage.token, + $pageSkip * $pageLimit, + $pageLimit + ); + // once the bottom of the list has been reached (no results) there is no need to continue querying + chatPagniationComplete = nextPageChats.length === 0; + await chats.set([...$chats, ...nextPageChats]); + nextPageLoading = false; + + observer.observe(loader); // Start observing again after content is loaded + }; + return () => { window.removeEventListener('keydown', onKeyDown); window.removeEventListener('keyup', onKeyUp); @@ -427,8 +467,8 @@ bind:value={search} on:focus={async () => { disablePagination(); + // TODO: migrate backend for more scalable search mechanism await chats.set(await getChatList(localStorage.token)); // when searching, load all chats - enrichChatsWithContent($chats); }} /> @@ -506,33 +546,7 @@ {/if} -
{ - if (!$scrollPaginationEnabled) return; - if ($tagView) return; - if (nextPageLoading) return; - if (chatPagniationComplete) return; - - const maxScroll = e.target.scrollHeight - e.target.clientHeight; - const currentPos = e.target.scrollTop; - const ratio = currentPos / maxScroll; - if (ratio >= paginationScrollThreashold) { - nextPageLoading = true; - pageSkip.set($pageSkip + 1); - // extend existing chats - const nextPageChats = await getChatList( - localStorage.token, - $pageSkip * $pageLimit, - $pageLimit - ); - // once the bottom of the list has been reached (no results) there is no need to continue querying - chatPagniationComplete = nextPageChats.length === 0; - await chats.set([...$chats, ...nextPageChats]); - nextPageLoading = false; - } - }} - > +
{#each filteredChatList as chat, idx} {#if idx === 0 || (idx > 0 && chat.time_range !== filteredChatList[idx - 1].time_range)}
{/each} - {#if nextPageLoading} -
- + + {#if !chatPagniationComplete} +
+ +
Loading...
{/if}
diff --git a/src/lib/stores/index.ts b/src/lib/stores/index.ts index 6e14fcb2c..b33046caa 100644 --- a/src/lib/stores/index.ts +++ b/src/lib/stores/index.ts @@ -41,6 +41,7 @@ export const showSettings = writable(false); export const showArchivedChats = writable(false); export const showChangelog = writable(false); export const showCallOverlay = writable(false); + export const scrollPaginationEnabled = writable(true); export const pageSkip = writable(0); export const pageLimit = writable(-1);