Skip to content

Commit

Permalink
✨ Load pages and ensure scrollbar is always visible (#760)
Browse files Browse the repository at this point in the history
  • Loading branch information
MontaGhanmy authored Dec 9, 2024
1 parent 4c05cf9 commit 9bc8b29
Show file tree
Hide file tree
Showing 2 changed files with 51 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export const useDriveItem = (id: string) => {
// const children = useRecoilValue(DriveItemChildrenAtom(id));
const [children, setChildren] = useRecoilState(DriveItemChildrenAtom(id));
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
const [, setPaginateItem] = useRecoilState(DriveItemPagination);
const [paginateItem, setPaginateItem] = useRecoilState(DriveItemPagination);
const {
refresh: refreshItem,
create,
Expand Down Expand Up @@ -172,6 +172,7 @@ export const useDriveItem = (id: string) => {
remove,
refresh,
restore,
paginateItem,
loadNextPage,
};
};
Expand Down
66 changes: 49 additions & 17 deletions tdrive/frontend/src/app/views/client/body/drive/browser.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store';
import { formatBytes } from '@features/drive/utils';
import useRouterCompany from '@features/router/hooks/use-router-company';
import _ from 'lodash';
import _, { get, set } from 'lodash';
import { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react';
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
import { DrivePreview } from '../../viewer/drive-preview';
Expand Down Expand Up @@ -77,9 +77,9 @@ export default memo(
? (user?.companies || []).find(company => company?.company.id === companyId)?.role
: 'member';
setTdriveTabToken(tdriveTabContextToken || null);
const [ filter ] = useRecoilState(SharedWithMeFilterState);
const [filter] = useRecoilState(SharedWithMeFilterState);
const { viewId, dirId, itemId } = useRouteState();
const [sortLabel] = useRecoilState(DriveItemSort)
const [sortLabel] = useRecoilState(DriveItemSort);
const [parentId, _setParentId] = useRecoilState(
DriveCurrentFolderAtom({
context: context,
Expand All @@ -94,8 +94,6 @@ export default memo(
history.push(RouterServices.generateRouteFromState({ viewId: parentId }));
}, [viewId, dirId]);



const [loadingParentChange, setLoadingParentChange] = useState(false);
const {
sharedWithMe,
Expand All @@ -108,6 +106,7 @@ export default memo(
loading: loadingParent,
path,
loadNextPage,
paginateItem,
} = useDriveItem(parentId);
const { uploadTree } = useDriveUpload();

Expand Down Expand Up @@ -145,12 +144,13 @@ export default memo(
if (item?.id) setUploadModalState({ open: true, parent_id: item.id });
}, [item?.id, setUploadModalState]);

const items = item?.is_directory === false
const items =
item?.is_directory === false
? //We use this hack for public shared single file
item
? [item]
: []
: children
: children;

const documents = items.filter(i => !i.is_directory);

Expand Down Expand Up @@ -241,32 +241,33 @@ export default memo(
}

// Infinite scroll
const scrollViwer = useRef<HTMLDivElement>(null);
const scrollViewer = useRef<HTMLDivElement>(null);

const handleScroll = async () => {
const scrollTop = scrollViwer.current?.scrollTop || 0;
const scrollHeight = scrollViwer.current?.scrollHeight || 0;
const clientHeight = scrollViwer.current?.clientHeight || 0;
const scrollTop = scrollViewer.current?.scrollTop || 0;
const scrollHeight = scrollViewer.current?.scrollHeight || 0;
const clientHeight = scrollViewer.current?.clientHeight || 0;
if (scrollTop > 0 && scrollTop + clientHeight >= scrollHeight) {
await loadNextPage(parentId);
}
};

useEffect(() => {
if(!loading) scrollViwer.current?.addEventListener('scroll', handleScroll, { passive: true });
if (!loading)
scrollViewer.current?.addEventListener('scroll', handleScroll, { passive: true });
return () => {
scrollViwer.current?.removeEventListener('scroll', handleScroll);
scrollViewer.current?.removeEventListener('scroll', handleScroll);
};
}, [parentId, loading]);

// Scroll to item in view
const scrollTillItemInView = itemId && itemId?.length > 0;
const scrollItemId = itemId || '';

useEffect(() => {
const itemInChildren = children.find(item => item.id === scrollItemId);
if (!loading && scrollTillItemInView && !itemInChildren) {
scrollViwer.current?.scrollTo(0, scrollViwer.current?.scrollHeight);
scrollViewer.current?.scrollTo(0, scrollViewer.current?.scrollHeight);
} else {
if (!loading && itemInChildren) {
// scroll to preview item using id for current preview routes
Expand All @@ -279,6 +280,35 @@ export default memo(
}
}, [loading, children]);

// Determine the number of items that can fit within the scroll viewer's visible area before the scrollbar appears.
const getItemsPerPage = () => {
const scrollViewerElement = scrollViewer?.current || null;
const itemHeight = scrollViewerElement?.firstElementChild?.clientHeight || 0;
const viewerHeight = scrollViewerElement?.clientHeight || 0;
return itemHeight > 0 ? Math.ceil(viewerHeight / itemHeight) : 0;
};

const [itemsPerPage, setItemsPerPage] = useState(0);

useEffect(() => {
const handleResize = () => {
setItemsPerPage(getItemsPerPage());
};
handleResize(); // intially set the items per page for the current view
window.addEventListener('resize', handleResize);
return () => window.removeEventListener('resize', handleResize);
}, [getItemsPerPage]);

// Load additional pages as needed to ensure the scrollbar remains visible
useEffect(() => {
const currentPage = Math.floor((paginateItem?.page || 1) / (paginateItem?.limit || 1));
const targetPages = Math.ceil(itemsPerPage / (paginateItem?.limit || 1));

if (!loading && currentPage < targetPages) {
loadNextPage(parentId);
}
}, [paginateItem, loading, parentId, itemsPerPage]);

return (
<>
{viewId == 'shared-with-me' ? (
Expand Down Expand Up @@ -418,7 +448,9 @@ export default memo(
sortLabel.order === 'asc' ? 'transform rotate-180' : ''
}`}
/>
<span>{Languages.t('components.item_context_menu.sorting.selected.' + sortLabel.by)}</span>
<span>
{Languages.t('components.item_context_menu.sorting.selected.' + sortLabel.by)}
</span>
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
</Button>
</Menu>
Expand All @@ -439,7 +471,7 @@ export default memo(
</div>

<DndContext sensors={sensors} onDragEnd={handleDragEnd} onDragStart={handleDragStart}>
<div className="grow overflow-auto" ref={scrollViwer}>
<div className="grow overflow-auto" ref={scrollViewer}>
{items.length === 0 && !loading && (
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
<Subtitle className="block mb-2">
Expand Down

0 comments on commit 9bc8b29

Please sign in to comment.