fix: infinity scrolling of bank account transactions

This commit is contained in:
Ahmed Bouhuolia
2024-08-05 20:57:13 +02:00
parent 8e99a31455
commit 64c0732e5f
8 changed files with 124 additions and 65 deletions

View File

@@ -1,5 +1,9 @@
import React from 'react';
import { AppShellProvider, useAppShellContext } from './AppContentShellProvider';
// @ts-nocheck
import React, { forwardRef, Ref } from 'react';
import {
AppShellProvider,
useAppShellContext,
} from './AppContentShellProvider';
import { Box, BoxProps } from '../../Layout';
import styles from './AppContentShell.module.scss';
@@ -12,50 +16,73 @@ interface AppContentShellProps {
hideMain?: boolean;
}
export function AppContentShell({
asideProps,
mainProps,
topbarOffset = 0,
hideAside = false,
hideMain = false,
...restProps
}: AppContentShellProps) {
return (
<AppShellProvider
mainProps={mainProps}
asideProps={asideProps}
topbarOffset={topbarOffset}
hideAside={hideAside}
hideMain={hideMain}
>
<Box {...restProps} className={styles.root} />
</AppShellProvider>
);
}
export const AppContentShell = forwardRef(
(
{
asideProps,
mainProps,
topbarOffset = 0,
hideAside = false,
hideMain = false,
...restProps
}: AppContentShellProps,
ref: Ref<HTMLDivElement>,
) => {
return (
<AppShellProvider
mainProps={mainProps}
asideProps={asideProps}
topbarOffset={topbarOffset}
hideAside={hideAside}
hideMain={hideMain}
>
<Box {...restProps} className={styles.root} ref={ref} />
</AppShellProvider>
);
},
);
AppContentShell.displayName = 'AppContentShell';
interface AppContentShellMainProps extends BoxProps {}
function AppContentShellMain({ ...props }: AppContentShellMainProps) {
const { hideMain } = useAppShellContext();
/**
* Main content of the app shell.
* @param {AppContentShellMainProps} props -
* @returns {React.ReactNode}
*/
const AppContentShellMain = forwardRef(
({ ...props }: AppContentShellMainProps, ref: Ref<HTMLDivElement>) => {
const { hideMain } = useAppShellContext();
if (hideMain === true) {
return null;
}
return <Box {...props} className={styles.main} />;
}
if (hideMain === true) {
return null;
}
return <Box {...props} className={styles.main} ref={ref} />;
},
);
AppContentShellMain.displayName = 'AppContentShellMain';
interface AppContentShellAsideProps extends BoxProps {
children: React.ReactNode;
}
function AppContentShellAside({ ...props }: AppContentShellAsideProps) {
const { hideAside } = useAppShellContext();
/**
* Aside content of the app shell.
* @param {AppContentShellAsideProps} props
* @returns {React.ReactNode}
*/
const AppContentShellAside = forwardRef(
({ ...props }: AppContentShellAsideProps, ref: Ref<HTMLDivElement>) => {
const { hideAside } = useAppShellContext();
if (hideAside === true) {
return null;
}
return <Box {...props} className={styles.aside} />;
}
if (hideAside === true) {
return null;
}
return <Box {...props} className={styles.aside} ref={ref} />;
},
);
AppContentShellAside.displayName = 'AppContentShellAside';
AppContentShell.Main = AppContentShellMain;
AppContentShell.Aside = AppContentShellAside;

View File

@@ -25,14 +25,13 @@ function TableVirtualizedListRow({ index, isScrolling, isVisible, style }) {
export function TableVirtualizedListRows() {
const {
table: { page },
props: { vListrowHeight, vListOverscanRowCount },
props: { vListrowHeight, vListOverscanRowCount, windowScrollerProps },
} = useContext(TableContext);
// Dashboard content pane.
const dashboardContentPane = React.useMemo(
() => document.querySelector(`.${CLASSES.DASHBOARD_CONTENT_PANE}`),
[],
);
const scrollElement =
windowScrollerProps?.scrollElement ||
document.querySelector(`.${CLASSES.DASHBOARD_CONTENT_PANE}`);
const rowRenderer = React.useCallback(
({ key, ...args }) => <TableVirtualizedListRow {...args} key={key} />,
@@ -40,7 +39,7 @@ export function TableVirtualizedListRows() {
);
return (
<WindowScroller scrollElement={dashboardContentPane}>
<WindowScroller scrollElement={scrollElement}>
{({ height, isScrolling, onChildScroll, scrollTop }) => (
<AutoSizer disableHeight>
{({ width }) => (

View File

@@ -1,12 +1,15 @@
import React from 'react';
import React, { forwardRef, Ref } from 'react';
import { HTMLDivProps, Props } from '@blueprintjs/core';
export interface BoxProps extends Props, HTMLDivProps {
className?: string;
}
export function Box({ className, ...rest }: BoxProps) {
const Element = 'div';
export const Box = forwardRef(
({ className, ...rest }: BoxProps, ref: Ref<HTMLDivElement>) => {
const Element = 'div';
return <Element className={className} {...rest} />;
}
return <Element className={className} ref={ref} {...rest} />;
},
);
Box.displayName = '@bigcapital/Box';

View File

@@ -29,28 +29,41 @@ function AccountTransactionsListRoot({
return (
<AccountTransactionsProvider>
<AppContentShell hideAside={!openMatchingTransactionAside}>
<AppContentShell.Main>
<AccountTransactionsActionsBar />
<AccountTransactionsDetailsBar />
<AccountTransactionsProgressBar />
<DashboardPageContent>
<AccountTransactionsFilterTabs />
<Suspense fallback={<Spinner size={30} />}>
<AccountTransactionsContent />
</Suspense>
</DashboardPageContent>
</AppContentShell.Main>
<AppContentShell.Aside>
<CategorizeTransactionAside />
</AppContentShell.Aside>
<AccountTransactionsMain />
<AccountTransactionsAside />
</AppContentShell>
</AccountTransactionsProvider>
);
}
function AccountTransactionsMain() {
const { setScrollableRef } = useAccountTransactionsContext();
return (
<AppContentShell.Main ref={(e) => setScrollableRef(e)}>
<AccountTransactionsActionsBar />
<AccountTransactionsDetailsBar />
<AccountTransactionsProgressBar />
<DashboardPageContent>
<AccountTransactionsFilterTabs />
<Suspense fallback={<Spinner size={30} />}>
<AccountTransactionsContent />
</Suspense>
</DashboardPageContent>
</AppContentShell.Main>
);
}
function AccountTransactionsAside() {
return (
<AppContentShell.Aside>
<CategorizeTransactionAside />
</AppContentShell.Aside>
);
}
export default R.compose(
withBanking(
({ selectedUncategorizedTransactionId, openMatchingTransactionAside }) => ({

View File

@@ -1,5 +1,5 @@
// @ts-nocheck
import React from 'react';
import React, { useRef, useState } from 'react';
import { useParams } from 'react-router-dom';
import { DashboardInsider } from '@/components';
import { useCashflowAccounts, useAccount } from '@/hooks/query';
@@ -41,6 +41,8 @@ function AccountTransactionsProvider({ query, ...props }) {
isLoading: isBankAccountMetaSummaryLoading,
} = useGetBankAccountSummaryMeta(accountId);
const [scrollableRef, setScrollableRef] = useState();
// Provider payload.
const provider = {
accountId,
@@ -56,6 +58,9 @@ function AccountTransactionsProvider({ query, ...props }) {
filterTab,
setFilterTab,
scrollableRef,
setScrollableRef
};
return (

View File

@@ -16,6 +16,7 @@ import { TABLES } from '@/constants/tables';
import { useMemorizedColumnsWidths } from '@/hooks';
import { useExcludedTransactionsColumns } from './_utils';
import { useExcludedTransactionsBoot } from './ExcludedTransactionsTableBoot';
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
import { ActionsMenu } from './_components';
import { useUnexcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
@@ -37,6 +38,8 @@ function ExcludedTransactionsTableRoot({
const { mutateAsync: unexcludeBankTransaction } =
useUnexcludeUncategorizedTransaction();
const { scrollableRef } = useAccountTransactionsContext();
// Retrieve table columns.
const columns = useExcludedTransactionsColumns();
@@ -97,6 +100,7 @@ function ExcludedTransactionsTableRoot({
className="table-constrant"
selectionColumn={true}
onSelectedRowsChange={handleSelectedRowsChange}
windowScrollerProps={{ scrollElement: scrollableRef }}
payload={{
onRestore: handleRestoreClick,
}}

View File

@@ -20,6 +20,7 @@ import { useRecognizedTransactionsBoot } from './RecognizedTransactionsTableBoot
import { ActionsMenu } from './_components';
import { compose } from '@/utils';
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
import { useExcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
import {
WithBankingActionsProps,
@@ -49,6 +50,8 @@ function RecognizedTransactionsTableRoot({
const [initialColumnsWidths, , handleColumnResizing] =
useMemorizedColumnsWidths(TABLES.UNCATEGORIZED_ACCOUNT_TRANSACTIONS);
const { scrollableRef } = useAccountTransactionsContext();
// Handle cell click.
const handleCellClick = (cell, event) => {
setUncategorizedTransactionIdForMatching(
@@ -102,6 +105,7 @@ function RecognizedTransactionsTableRoot({
vListOverscanRowCount={0}
initialColumnsWidths={initialColumnsWidths}
onColumnResizing={handleColumnResizing}
windowScrollerProps={{ scrollElement: scrollableRef }}
noResults={<RecognizedTransactionsTableNoResults />}
className="table-constrant"
payload={{

View File

@@ -22,6 +22,7 @@ import { useMemorizedColumnsWidths } from '@/hooks';
import { useAccountUncategorizedTransactionsContext } from '../AllTransactionsUncategorizedBoot';
import { useExcludeUncategorizedTransaction } from '@/hooks/query/bank-rules';
import { useAccountUncategorizedTransactionsColumns } from './hooks';
import { useAccountTransactionsContext } from '../AccountTransactionsProvider';
import { compose } from '@/utils';
import { withBanking } from '../../withBanking';
@@ -48,6 +49,8 @@ function AccountTransactionsDataTable({
// Retrieve table columns.
const columns = useAccountUncategorizedTransactionsColumns();
const { scrollableRef } = useAccountTransactionsContext();
// Retrieve list context.
const { uncategorizedTransactions, isUncategorizedTransactionsLoading } =
useAccountUncategorizedTransactionsContext();
@@ -124,6 +127,7 @@ function AccountTransactionsDataTable({
onCategorize: handleCategorizeBtnClick,
}}
onSelectedRowsChange={handleSelectedRowsChange}
windowScrollerProps={{ scrollElement: scrollableRef }}
className={clsx('table-constrant', styles.table, {
[styles.showCategorizeColumn]: enableMultipleCategorization,
})}