Commit bd4c82b1 authored by tom's avatar tom

disable pagination buttons when page is loading

parent ae968845
import React from 'react';
export default function useIsInitialLoading(isLoading: boolean | undefined) {
const [ isInitialLoading, setIsInitialLoading ] = React.useState(Boolean(isLoading));
React.useEffect(() => {
if (!isLoading) {
setIsInitialLoading(false);
}
}, [ isLoading ]);
return isInitialLoading;
}
...@@ -6,6 +6,7 @@ import type { CsvExportType } from 'types/client/address'; ...@@ -6,6 +6,7 @@ import type { CsvExportType } from 'types/client/address';
import appConfig from 'configs/app/config'; import appConfig from 'configs/app/config';
import svgFileIcon from 'icons/files/csv.svg'; import svgFileIcon from 'icons/files/csv.svg';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import useIsMobile from 'lib/hooks/useIsMobile'; import useIsMobile from 'lib/hooks/useIsMobile';
import LinkInternal from 'ui/shared/LinkInternal'; import LinkInternal from 'ui/shared/LinkInternal';
...@@ -18,12 +19,13 @@ interface Props { ...@@ -18,12 +19,13 @@ interface Props {
const AddressCsvExportLink = ({ className, address, type, isLoading }: Props) => { const AddressCsvExportLink = ({ className, address, type, isLoading }: Props) => {
const isMobile = useIsMobile(); const isMobile = useIsMobile();
const isInitialLoading = useIsInitialLoading(isLoading);
if (!appConfig.reCaptcha.siteKey) { if (!appConfig.reCaptcha.siteKey) {
return null; return null;
} }
if (isLoading) { if (isInitialLoading) {
return ( return (
<Flex className={ className } flexShrink={ 0 } alignItems="center"> <Flex className={ className } flexShrink={ 0 } alignItems="center">
<Skeleton boxSize={{ base: '32px', lg: 6 }} borderRadius="base"/> <Skeleton boxSize={{ base: '32px', lg: 6 }} borderRadius="base"/>
......
...@@ -10,6 +10,7 @@ import React from 'react'; ...@@ -10,6 +10,7 @@ import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import FilterButton from 'ui/shared/filters/FilterButton'; import FilterButton from 'ui/shared/filters/FilterButton';
interface Props { interface Props {
...@@ -21,13 +22,14 @@ interface Props { ...@@ -21,13 +22,14 @@ interface Props {
const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => { const AddressTxsFilter = ({ onFilterChange, defaultFilter, isActive, isLoading }: Props) => {
const { isOpen, onToggle } = useDisclosure(); const { isOpen, onToggle } = useDisclosure();
const isInitialLoading = useIsInitialLoading(isLoading);
return ( return (
<Menu> <Menu>
<MenuButton> <MenuButton>
<FilterButton <FilterButton
isActive={ isOpen || isActive } isActive={ isOpen || isActive }
isLoading={ isLoading } isLoading={ isInitialLoading }
onClick={ onToggle } onClick={ onToggle }
as="div" as="div"
/> />
......
...@@ -9,6 +9,7 @@ import React from 'react'; ...@@ -9,6 +9,7 @@ import React from 'react';
import type { AddressFromToFilter } from 'types/api/address'; import type { AddressFromToFilter } from 'types/api/address';
import type { TokenType } from 'types/api/token'; import type { TokenType } from 'types/api/token';
import useIsInitialLoading from 'lib/hooks/useIsInitialLoading';
import PopoverFilter from 'ui/shared/filters/PopoverFilter'; import PopoverFilter from 'ui/shared/filters/PopoverFilter';
import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter'; import TokenTypeFilter from 'ui/shared/filters/TokenTypeFilter';
...@@ -31,9 +32,10 @@ const TokenTransferFilter = ({ ...@@ -31,9 +32,10 @@ const TokenTransferFilter = ({
defaultAddressFilter, defaultAddressFilter,
isLoading, isLoading,
}: Props) => { }: Props) => {
const isInitialLoading = useIsInitialLoading(isLoading);
return ( return (
<PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }} isLoading={ isLoading }> <PopoverFilter appliedFiltersNum={ appliedFiltersNum } contentProps={{ w: '200px' }} isLoading={ isInitialLoading }>
{ withAddressFilter && ( { withAddressFilter && (
<> <>
<Text variant="secondary" fontWeight={ 600 }>Address</Text> <Text variant="secondary" fontWeight={ 600 }>Address</Text>
......
...@@ -9,29 +9,31 @@ interface Props extends PaginationParams { ...@@ -9,29 +9,31 @@ interface Props extends PaginationParams {
className?: string; className?: string;
} }
const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNextPage, className, canGoBackwards, isLoading, isVisible }: Props) => { const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasPages, hasNextPage, className, canGoBackwards, isLoading, isVisible }: Props) => {
if (!isVisible) { if (!isVisible) {
return null; return null;
} }
const showSkeleton = page === 1 && !hasPages && isLoading;
return ( return (
<Flex <Flex
className={ className } className={ className }
fontSize="sm" fontSize="sm"
alignItems="center" alignItems="center"
> >
<Skeleton isLoaded={ !isLoading } display="inline-block" mr={ 4 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 4 } borderRadius="base">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
onClick={ resetPage } onClick={ resetPage }
isDisabled={ page === 1 } isDisabled={ page === 1 || isLoading }
> >
First First
</Button> </Button>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" mr={ 3 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" mr={ 3 } borderRadius="base">
<IconButton <IconButton
variant="outline" variant="outline"
onClick={ onPrevPageClick } onClick={ onPrevPageClick }
...@@ -39,10 +41,10 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -39,10 +41,10 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
aria-label="Prev page" aria-label="Prev page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 }/> }
isDisabled={ !canGoBackwards || page === 1 } isDisabled={ !canGoBackwards || page === 1 || isLoading }
/> />
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" borderRadius="base">
<Button <Button
variant="outline" variant="outline"
size="sm" size="sm"
...@@ -50,12 +52,13 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -50,12 +52,13 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
borderWidth="1px" borderWidth="1px"
fontWeight={ 400 } fontWeight={ 400 }
h={ 8 } h={ 8 }
minW="36px"
cursor="unset" cursor="unset"
> >
{ page } { page }
</Button> </Button>
</Skeleton> </Skeleton>
<Skeleton isLoaded={ !isLoading } display="inline-block" ml={ 3 } borderRadius="base"> <Skeleton isLoaded={ !showSkeleton } display="inline-block" ml={ 3 } borderRadius="base">
<IconButton <IconButton
variant="outline" variant="outline"
onClick={ onNextPageClick } onClick={ onNextPageClick }
...@@ -63,7 +66,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext ...@@ -63,7 +66,7 @@ const Pagination = ({ page, onNextPageClick, onPrevPageClick, resetPage, hasNext
aria-label="Next page" aria-label="Next page"
w="36px" w="36px"
icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> } icon={ <Icon as={ arrowIcon } w={ 5 } h={ 5 } transform="rotate(180deg)"/> }
isDisabled={ !hasNextPage } isDisabled={ !hasNextPage || isLoading }
/> />
</Skeleton> </Skeleton>
{ /* not implemented yet */ } { /* not implemented yet */ }
......
...@@ -3,6 +3,7 @@ export interface PaginationParams { ...@@ -3,6 +3,7 @@ export interface PaginationParams {
onNextPageClick: () => void; onNextPageClick: () => void;
onPrevPageClick: () => void; onPrevPageClick: () => void;
resetPage: () => void; resetPage: () => void;
hasPages: boolean;
hasNextPage: boolean; hasNextPage: boolean;
canGoBackwards: boolean; canGoBackwards: boolean;
isLoading: boolean; isLoading: boolean;
......
...@@ -68,6 +68,7 @@ it('returns correct data if there is only one page', async() => { ...@@ -68,6 +68,7 @@ it('returns correct data if there is only one page', async() => {
hasNextPage: false, hasNextPage: false,
isLoading: false, isLoading: false,
isVisible: false, isVisible: false,
hasPages: false,
}); });
}); });
...@@ -127,6 +128,7 @@ describe('if there are multiple pages', () => { ...@@ -127,6 +128,7 @@ describe('if there are multiple pages', () => {
hasNextPage: true, hasNextPage: true,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(1); expect(routerPush).toHaveBeenCalledTimes(1);
...@@ -164,6 +166,7 @@ describe('if there are multiple pages', () => { ...@@ -164,6 +166,7 @@ describe('if there are multiple pages', () => {
hasNextPage: false, hasNextPage: false,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(2); expect(routerPush).toHaveBeenCalledTimes(2);
...@@ -207,6 +210,7 @@ describe('if there are multiple pages', () => { ...@@ -207,6 +210,7 @@ describe('if there are multiple pages', () => {
hasNextPage: true, hasNextPage: true,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(3); expect(routerPush).toHaveBeenCalledTimes(3);
...@@ -254,6 +258,7 @@ describe('if there are multiple pages', () => { ...@@ -254,6 +258,7 @@ describe('if there are multiple pages', () => {
hasNextPage: true, hasNextPage: true,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(4); expect(routerPush).toHaveBeenCalledTimes(4);
...@@ -305,6 +310,7 @@ describe('if there are multiple pages', () => { ...@@ -305,6 +310,7 @@ describe('if there are multiple pages', () => {
hasNextPage: true, hasNextPage: true,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(3); expect(routerPush).toHaveBeenCalledTimes(3);
...@@ -368,6 +374,7 @@ describe('if there is page query param in URL', () => { ...@@ -368,6 +374,7 @@ describe('if there is page query param in URL', () => {
hasNextPage: false, hasNextPage: false,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
}); });
...@@ -397,6 +404,7 @@ describe('if there is page query param in URL', () => { ...@@ -397,6 +404,7 @@ describe('if there is page query param in URL', () => {
hasNextPage: false, hasNextPage: false,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: true,
}); });
expect(routerPush).toHaveBeenCalledTimes(1); expect(routerPush).toHaveBeenCalledTimes(1);
...@@ -447,6 +455,7 @@ describe('queries with filters', () => { ...@@ -447,6 +455,7 @@ describe('queries with filters', () => {
hasNextPage: true, hasNextPage: true,
isLoading: false, isLoading: false,
isVisible: true, isVisible: true,
hasPages: false,
}); });
expect(routerPush).toHaveBeenCalledTimes(2); expect(routerPush).toHaveBeenCalledTimes(2);
......
...@@ -55,7 +55,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -55,7 +55,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({ const [ pageParams, setPageParams ] = React.useState<Record<number, NextPageParams>>({
[page]: getPaginationParamsFromQuery(router.query.next_page_params), [page]: getPaginationParamsFromQuery(router.query.next_page_params),
}); });
const [ hasPagination, setHasPagination ] = React.useState(page > 1); const [ hasPages, setHasPages ] = React.useState(page > 1);
const isMounted = React.useRef(false); const isMounted = React.useRef(false);
const canGoBackwards = React.useRef(!router.query.page); const canGoBackwards = React.useRef(!router.query.page);
...@@ -93,7 +93,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -93,7 +93,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
next_page_params: encodeURIComponent(JSON.stringify(data.next_page_params)), next_page_params: encodeURIComponent(JSON.stringify(data.next_page_params)),
}; };
setHasPagination(true); setHasPages(true);
scrollToTop(); scrollToTop();
router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true }); router.push({ pathname: router.pathname, query: nextPageQuery }, undefined, { shallow: true });
}, [ data?.next_page_params, page, router, scrollToTop ]); }, [ data?.next_page_params, page, router, scrollToTop ]);
...@@ -116,7 +116,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -116,7 +116,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
setPage(prev => prev - 1); setPage(prev => prev - 1);
page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] }); page === 2 && queryClient.removeQueries({ queryKey: [ resourceName ] });
}); });
setHasPagination(true);
}, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]); }, [ router, page, pageParams, scrollToTop, queryClient, resourceName ]);
const resetPage = useCallback(() => { const resetPage = useCallback(() => {
...@@ -135,8 +134,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -135,8 +134,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
queryClient.removeQueries({ queryKey: [ resourceName ], type: 'inactive' }); queryClient.removeQueries({ queryKey: [ resourceName ], type: 'inactive' });
}, 100); }, 100);
}); });
setHasPagination(true);
}, [ queryClient, resourceName, router, scrollToTop ]); }, [ queryClient, resourceName, router, scrollToTop ]);
const onFilterChange = useCallback((newFilters: PaginationFilters<Resource> | undefined) => { const onFilterChange = useCallback((newFilters: PaginationFilters<Resource> | undefined) => {
...@@ -148,7 +145,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -148,7 +145,6 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
} }
}); });
} }
setHasPagination(false);
scrollToTop(); scrollToTop();
router.push( router.push(
{ {
...@@ -158,6 +154,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -158,6 +154,7 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
undefined, undefined,
{ shallow: true }, { shallow: true },
).then(() => { ).then(() => {
setHasPages(false);
setPage(1); setPage(1);
setPageParams({}); setPageParams({});
}); });
...@@ -171,10 +168,11 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({ ...@@ -171,10 +168,11 @@ export default function useQueryWithPages<Resource extends PaginatedResources>({
onNextPageClick, onNextPageClick,
onPrevPageClick, onPrevPageClick,
resetPage, resetPage,
hasPages,
hasNextPage, hasNextPage,
canGoBackwards: canGoBackwards.current, canGoBackwards: canGoBackwards.current,
isLoading: queryResult.isPlaceholderData && !hasPagination, isLoading: queryResult.isPlaceholderData,
isVisible: hasPagination || (!queryResult.isLoading && !queryResult.isError && hasNextPage), isVisible: hasPages || hasNextPage,
}; };
React.useEffect(() => { React.useEffect(() => {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment