From 9bc8b29a9dcc7b41994478b2c6ea2b0d8cd3d792 Mon Sep 17 00:00:00 2001 From: Montassar Ghanmy Date: Mon, 9 Dec 2024 16:27:09 +0100 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Load=20pages=20and=20ensure=20scrol?= =?UTF-8?q?lbar=20is=20always=20visible=20(#760)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../features/drive/hooks/use-drive-item.tsx | 3 +- .../app/views/client/body/drive/browser.tsx | 66 ++++++++++++++----- 2 files changed, 51 insertions(+), 18 deletions(-) diff --git a/tdrive/frontend/src/app/features/drive/hooks/use-drive-item.tsx b/tdrive/frontend/src/app/features/drive/hooks/use-drive-item.tsx index bb8c9167..106ea111 100644 --- a/tdrive/frontend/src/app/features/drive/hooks/use-drive-item.tsx +++ b/tdrive/frontend/src/app/features/drive/hooks/use-drive-item.tsx @@ -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, @@ -172,6 +172,7 @@ export const useDriveItem = (id: string) => { remove, refresh, restore, + paginateItem, loadNextPage, }; }; diff --git a/tdrive/frontend/src/app/views/client/body/drive/browser.tsx b/tdrive/frontend/src/app/views/client/body/drive/browser.tsx index 07bd1241..fc34fa1b 100644 --- a/tdrive/frontend/src/app/views/client/body/drive/browser.tsx +++ b/tdrive/frontend/src/app/views/client/body/drive/browser.tsx @@ -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'; @@ -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, @@ -94,8 +94,6 @@ export default memo( history.push(RouterServices.generateRouteFromState({ viewId: parentId })); }, [viewId, dirId]); - - const [loadingParentChange, setLoadingParentChange] = useState(false); const { sharedWithMe, @@ -108,6 +106,7 @@ export default memo( loading: loadingParent, path, loadNextPage, + paginateItem, } = useDriveItem(parentId); const { uploadTree } = useDriveUpload(); @@ -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); @@ -241,32 +241,33 @@ export default memo( } // Infinite scroll - const scrollViwer = useRef(null); + const scrollViewer = useRef(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 @@ -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' ? ( @@ -418,7 +448,9 @@ export default memo( sortLabel.order === 'asc' ? 'transform rotate-180' : '' }`} /> - {Languages.t('components.item_context_menu.sorting.selected.' + sortLabel.by)} + + {Languages.t('components.item_context_menu.sorting.selected.' + sortLabel.by)} + @@ -439,7 +471,7 @@ export default memo( -
+
{items.length === 0 && !loading && (