✨ Load pages and ensure scrollbar is always visible (#760)
This commit is contained in:
@@ -20,7 +20,7 @@ export const useDriveItem = (id: string) => {
|
|||||||
// const children = useRecoilValue(DriveItemChildrenAtom(id));
|
// const children = useRecoilValue(DriveItemChildrenAtom(id));
|
||||||
const [children, setChildren] = useRecoilState(DriveItemChildrenAtom(id));
|
const [children, setChildren] = useRecoilState(DriveItemChildrenAtom(id));
|
||||||
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
|
const [loading, setLoading] = useRecoilState(LoadingStateInitTrue('useDriveItem-' + id));
|
||||||
const [, setPaginateItem] = useRecoilState(DriveItemPagination);
|
const [paginateItem, setPaginateItem] = useRecoilState(DriveItemPagination);
|
||||||
const {
|
const {
|
||||||
refresh: refreshItem,
|
refresh: refreshItem,
|
||||||
create,
|
create,
|
||||||
@@ -172,6 +172,7 @@ export const useDriveItem = (id: string) => {
|
|||||||
remove,
|
remove,
|
||||||
refresh,
|
refresh,
|
||||||
restore,
|
restore,
|
||||||
|
paginateItem,
|
||||||
loadNextPage,
|
loadNextPage,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import { useDriveUpload } from '@features/drive/hooks/use-drive-upload';
|
|||||||
import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store';
|
import { DriveItemSelectedList, DriveItemSort } from '@features/drive/state/store';
|
||||||
import { formatBytes } from '@features/drive/utils';
|
import { formatBytes } from '@features/drive/utils';
|
||||||
import useRouterCompany from '@features/router/hooks/use-router-company';
|
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 { memo, Suspense, useCallback, useEffect, useRef, useState } from 'react';
|
||||||
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
|
import { atomFamily, useRecoilState, useSetRecoilState } from 'recoil';
|
||||||
import { DrivePreview } from '../../viewer/drive-preview';
|
import { DrivePreview } from '../../viewer/drive-preview';
|
||||||
@@ -77,9 +77,9 @@ export default memo(
|
|||||||
? (user?.companies || []).find(company => company?.company.id === companyId)?.role
|
? (user?.companies || []).find(company => company?.company.id === companyId)?.role
|
||||||
: 'member';
|
: 'member';
|
||||||
setTdriveTabToken(tdriveTabContextToken || null);
|
setTdriveTabToken(tdriveTabContextToken || null);
|
||||||
const [ filter ] = useRecoilState(SharedWithMeFilterState);
|
const [filter] = useRecoilState(SharedWithMeFilterState);
|
||||||
const { viewId, dirId, itemId } = useRouteState();
|
const { viewId, dirId, itemId } = useRouteState();
|
||||||
const [sortLabel] = useRecoilState(DriveItemSort)
|
const [sortLabel] = useRecoilState(DriveItemSort);
|
||||||
const [parentId, _setParentId] = useRecoilState(
|
const [parentId, _setParentId] = useRecoilState(
|
||||||
DriveCurrentFolderAtom({
|
DriveCurrentFolderAtom({
|
||||||
context: context,
|
context: context,
|
||||||
@@ -94,8 +94,6 @@ export default memo(
|
|||||||
history.push(RouterServices.generateRouteFromState({ viewId: parentId }));
|
history.push(RouterServices.generateRouteFromState({ viewId: parentId }));
|
||||||
}, [viewId, dirId]);
|
}, [viewId, dirId]);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const [loadingParentChange, setLoadingParentChange] = useState(false);
|
const [loadingParentChange, setLoadingParentChange] = useState(false);
|
||||||
const {
|
const {
|
||||||
sharedWithMe,
|
sharedWithMe,
|
||||||
@@ -108,6 +106,7 @@ export default memo(
|
|||||||
loading: loadingParent,
|
loading: loadingParent,
|
||||||
path,
|
path,
|
||||||
loadNextPage,
|
loadNextPage,
|
||||||
|
paginateItem,
|
||||||
} = useDriveItem(parentId);
|
} = useDriveItem(parentId);
|
||||||
const { uploadTree } = useDriveUpload();
|
const { uploadTree } = useDriveUpload();
|
||||||
|
|
||||||
@@ -145,12 +144,13 @@ export default memo(
|
|||||||
if (item?.id) setUploadModalState({ open: true, parent_id: item.id });
|
if (item?.id) setUploadModalState({ open: true, parent_id: item.id });
|
||||||
}, [item?.id, setUploadModalState]);
|
}, [item?.id, setUploadModalState]);
|
||||||
|
|
||||||
const items = item?.is_directory === false
|
const items =
|
||||||
|
item?.is_directory === false
|
||||||
? //We use this hack for public shared single file
|
? //We use this hack for public shared single file
|
||||||
item
|
item
|
||||||
? [item]
|
? [item]
|
||||||
: []
|
: []
|
||||||
: children
|
: children;
|
||||||
|
|
||||||
const documents = items.filter(i => !i.is_directory);
|
const documents = items.filter(i => !i.is_directory);
|
||||||
|
|
||||||
@@ -241,32 +241,33 @@ export default memo(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Infinite scroll
|
// Infinite scroll
|
||||||
const scrollViwer = useRef<HTMLDivElement>(null);
|
const scrollViewer = useRef<HTMLDivElement>(null);
|
||||||
|
|
||||||
const handleScroll = async () => {
|
const handleScroll = async () => {
|
||||||
const scrollTop = scrollViwer.current?.scrollTop || 0;
|
const scrollTop = scrollViewer.current?.scrollTop || 0;
|
||||||
const scrollHeight = scrollViwer.current?.scrollHeight || 0;
|
const scrollHeight = scrollViewer.current?.scrollHeight || 0;
|
||||||
const clientHeight = scrollViwer.current?.clientHeight || 0;
|
const clientHeight = scrollViewer.current?.clientHeight || 0;
|
||||||
if (scrollTop > 0 && scrollTop + clientHeight >= scrollHeight) {
|
if (scrollTop > 0 && scrollTop + clientHeight >= scrollHeight) {
|
||||||
await loadNextPage(parentId);
|
await loadNextPage(parentId);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(!loading) scrollViwer.current?.addEventListener('scroll', handleScroll, { passive: true });
|
if (!loading)
|
||||||
|
scrollViewer.current?.addEventListener('scroll', handleScroll, { passive: true });
|
||||||
return () => {
|
return () => {
|
||||||
scrollViwer.current?.removeEventListener('scroll', handleScroll);
|
scrollViewer.current?.removeEventListener('scroll', handleScroll);
|
||||||
};
|
};
|
||||||
}, [parentId, loading]);
|
}, [parentId, loading]);
|
||||||
|
|
||||||
// Scroll to item in view
|
// Scroll to item in view
|
||||||
const scrollTillItemInView = itemId && itemId?.length > 0;
|
const scrollTillItemInView = itemId && itemId?.length > 0;
|
||||||
const scrollItemId = itemId || '';
|
const scrollItemId = itemId || '';
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const itemInChildren = children.find(item => item.id === scrollItemId);
|
const itemInChildren = children.find(item => item.id === scrollItemId);
|
||||||
if (!loading && scrollTillItemInView && !itemInChildren) {
|
if (!loading && scrollTillItemInView && !itemInChildren) {
|
||||||
scrollViwer.current?.scrollTo(0, scrollViwer.current?.scrollHeight);
|
scrollViewer.current?.scrollTo(0, scrollViewer.current?.scrollHeight);
|
||||||
} else {
|
} else {
|
||||||
if (!loading && itemInChildren) {
|
if (!loading && itemInChildren) {
|
||||||
// scroll to preview item using id for current preview routes
|
// scroll to preview item using id for current preview routes
|
||||||
@@ -279,6 +280,35 @@ export default memo(
|
|||||||
}
|
}
|
||||||
}, [loading, children]);
|
}, [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 (
|
return (
|
||||||
<>
|
<>
|
||||||
{viewId == 'shared-with-me' ? (
|
{viewId == 'shared-with-me' ? (
|
||||||
@@ -418,7 +448,9 @@ export default memo(
|
|||||||
sortLabel.order === 'asc' ? 'transform rotate-180' : ''
|
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" />
|
<ChevronDownIcon className="h-4 w-4 ml-2 -mr-1" />
|
||||||
</Button>
|
</Button>
|
||||||
</Menu>
|
</Menu>
|
||||||
@@ -439,7 +471,7 @@ export default memo(
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<DndContext sensors={sensors} onDragEnd={handleDragEnd} onDragStart={handleDragStart}>
|
<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 && (
|
{items.length === 0 && !loading && (
|
||||||
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
|
<div className="mt-4 text-center border-2 border-dashed rounded-md p-8">
|
||||||
<Subtitle className="block mb-2">
|
<Subtitle className="block mb-2">
|
||||||
|
|||||||
Reference in New Issue
Block a user