Skip to content

Commit

Permalink
Merge pull request #38 from zimmerman-team/feat/infinity-scroll
Browse files Browse the repository at this point in the history
Feat/infinity scroll  - DX-555
  • Loading branch information
stephanoshadjipetrou committed Sep 22, 2023
2 parents 00e7981 + 7d31287 commit b32e3a0
Show file tree
Hide file tree
Showing 9 changed files with 174 additions and 31 deletions.
32 changes: 32 additions & 0 deletions src/app/hooks/useInfinityScroll.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { useState, useEffect } from "react";

type LoadDataFunction = (searchStr: string, sortBy: string) => void;

export const useInfinityScroll = (
callback: LoadDataFunction,
observerTarget: React.MutableRefObject<null>,
searchStr: string,
sortByStr: string
) => {
useEffect(() => {
//handle infinity scroll with IntersectionObserver api

const observer = new IntersectionObserver(
(entries) => {
if (entries[0].isIntersecting) {
callback(searchStr, sortByStr);
}
},
{ threshold: 1 }
);

if (observerTarget.current) {
observer.observe(observerTarget.current);
}
return () => {
if (observerTarget.current) {
observer.unobserve(observerTarget.current);
}
};
}, []);
};
32 changes: 28 additions & 4 deletions src/app/modules/home-module/components/Charts/chartsGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useRef } from "react";
import axios from "axios";
import find from "lodash/find";
import Box from "@material-ui/core/Box";
Expand All @@ -10,6 +10,7 @@ import { HomepageTable } from "app/modules/home-module/components/Table";
import { coloredEchartTypes } from "app/modules/chart-module/routes/chart-type/data";
import ReformedGridItem from "app/modules/home-module/components/Charts/reformedGridItem";
import ChartAddnewCard from "./chartAddNewCard";
import { useInfinityScroll } from "app/hooks/useInfinityScroll";

interface Props {
sortBy: string;
Expand All @@ -19,17 +20,25 @@ interface Props {
}

export default function ChartsGrid(props: Props) {
const observerTarget = useRef(null);
const [cardId, setCardId] = React.useState<number>(0);
const [modalDisplay, setModalDisplay] = React.useState<boolean>(false);
const [enableButton, setEnableButton] = React.useState<boolean>(false);
const [loadedCharts, setLoadedCharts] = React.useState<any[]>([]);

const limit = 15;
//used over usestate to get current offset value in the IntersectionObserver api, as it is not updated in usestate.
const offset = useRef(0);
const charts = useStoreState(
(state) => (state.charts.ChartGetList.crudData ?? []) as any[]
);

const loadCharts = useStoreActions(
(actions) => actions.charts.ChartGetList.fetch
);
const chartsLoadSuccess = useStoreState(
(state) => state.charts.ChartGetList.success
);

const handleDelete = (index?: number) => {
setModalDisplay(false);
Expand Down Expand Up @@ -94,15 +103,27 @@ export default function ChartsGrid(props: Props) {
: "";
loadCharts({
storeInCrudData: true,
filterString: `filter={${value}"order":"${sortByStr} desc"}`,
filterString: `filter={${value}"order":"${sortByStr} desc","limit":${limit},"offset":${offset.current}}`,
});
offset.current = offset.current + limit;
}
useInfinityScroll(loadData, observerTarget, props.searchStr, props.sortBy);

React.useEffect(() => {
if (props.searchStr.length === 0) {
loadData(props.searchStr, props.sortBy);
}
}, [props.sortBy]);
React.useEffect(() => {
if (!chartsLoadSuccess) {
return;
}
//update the loaded reports
setLoadedCharts((prevCharts) => {
const f = charts.filter((report, i) => prevCharts[i]?.id !== report.id);
return [...prevCharts, ...f];
});
}, [chartsLoadSuccess]);

const [,] = useDebounce(
() => {
Expand All @@ -119,7 +140,7 @@ export default function ChartsGrid(props: Props) {
{!props.tableView && (
<Grid container spacing={2}>
{props.addCard && <ChartAddnewCard />}
{charts.map((c, index) => (
{loadedCharts.map((c, index) => (
<Grid item key={c.id} xs={12} sm={6} md={6} lg={3}>
<ReformedGridItem
id={c.id}
Expand All @@ -137,14 +158,17 @@ export default function ChartsGrid(props: Props) {
)}
{props.tableView && (
<HomepageTable
data={charts.map((data) => ({
data={loadedCharts.map((data) => ({
id: data.id,
name: data.name,
description: data.title,
createdDate: data.createdDate,
}))}
/>
)}
<Box height={100} />

<div ref={observerTarget} />
<DeleteChartDialog
cardId={cardId}
modalDisplay={modalDisplay}
Expand Down
73 changes: 63 additions & 10 deletions src/app/modules/home-module/components/Datasets/datasetsGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useRef } from "react";
import axios from "axios";
import get from "lodash/get";
import Box from "@material-ui/core/Box";
Expand All @@ -10,6 +10,7 @@ import DeleteDatasetDialog from "app/components/Dialogs/deleteDatasetDialog";
import { DatasetListItemAPIModel } from "app/modules/data-themes-module/sub-modules/list";
import ReformedGridItem from "app/modules/home-module/components/Datasets/reformedGridItem";
import DatasetAddnewCard from "./datasetAddNewCard";
import { useInfinityScroll } from "app/hooks/useInfinityScroll";

interface Props {
sortBy: string;
Expand All @@ -19,9 +20,32 @@ interface Props {
}

export default function DatasetsGrid(props: Props) {
const observerTarget = useRef(null);
const [cardId, setCardId] = React.useState<string>("");
const [enableButton, setEnableButton] = React.useState<boolean>(false);
const [modalDisplay, setModalDisplay] = React.useState<boolean>(false);
const limit = 15;
//used over usestate to get current offset value in the IntersectionObserver api, as it is not updated in usestate.
const offset = useRef(0);
const [loadedDatasets, setLoadedDatasets] = React.useState<
DatasetListItemAPIModel[]
>([]);

const loadDatasets = useStoreActions(
(actions) => actions.dataThemes.DatasetGetList.fetch
);
const clearDatasets = useStoreActions(
(actions) => actions.dataThemes.DatasetGetList.clear
);
const loadDatasetCount = useStoreActions(
(actions) => actions.dataThemes.DatasetCount.fetch
);
const datasetLoadSuccess = useStoreState(
(state) => state.dataThemes.DatasetGetList.success
);
const datasetCount = useStoreState(
(state) => get(state, "dataThemes.DatasetCount.data.count", 0) as number
);

const handleDelete = (id: string) => {
deleteDataset(id);
Expand All @@ -42,10 +66,6 @@ export default function DatasetsGrid(props: Props) {
setModalDisplay(true);
};

const loadDatasets = useStoreActions(
(actions) => actions.dataThemes.DatasetGetList.fetch
);

const datasets = useStoreState(
(state) =>
get(
Expand All @@ -67,23 +87,46 @@ export default function DatasetsGrid(props: Props) {
.catch((error) => console.log(error));
}

function loadData(searchStr: string, sortByStr: string) {
const loadData = (searchStr: string, sortByStr: string) => {
const value =
searchStr.length > 0
? `"where":{"name":{"like":"${searchStr}.*","options":"i"}},`
: "";
loadDatasets({
storeInCrudData: true,
filterString: `filter={${value}"order":"${sortByStr} desc"}`,
filterString: `filter={${value}"order":"${sortByStr} desc","limit":${limit},"offset":${offset.current}}`,
});
}
offset.current = offset.current + limit;
};
useInfinityScroll(loadData, observerTarget, props.searchStr, props.sortBy);

React.useEffect(() => {
loadDatasetCount({});
if (props.searchStr.length === 0) {
loadData(props.searchStr, props.sortBy);
}
}, [props.searchStr, props.sortBy]);

React.useEffect(() => {
clearDatasets();
setLoadedDatasets([]);
}, []);
React.useEffect(() => {
if (datasets === null) {
setLoadedDatasets([]);
}
}, [datasets]);

React.useEffect(() => {
if (!datasetLoadSuccess) {
return;
}
//update the loaded datasets
setLoadedDatasets((prevDatasets) => {
return [...prevDatasets, ...datasets];
});
}, [datasetLoadSuccess]);

const [,] = useDebounce(
() => {
if (props.searchStr.length > 0) {
Expand All @@ -99,7 +142,7 @@ export default function DatasetsGrid(props: Props) {
{!props.tableView && (
<Grid container spacing={2}>
{props.addCard && <DatasetAddnewCard />}
{(datasets || []).map((data, index) => (
{(loadedDatasets || []).map((data, index) => (
<Grid item key={data.id} xs={12} sm={6} md={4} lg={3}>
<ReformedGridItem
date={data.createdDate}
Expand All @@ -110,21 +153,31 @@ export default function DatasetsGrid(props: Props) {
handleDuplicate={() => {}}
handleDelete={() => {}}
/>

<Box height={16} />
</Grid>
))}
</Grid>
)}

{props.tableView && (
<HomepageTable
data={datasets.map((data) => ({
data={loadedDatasets.map((data) => ({
id: data.id,
name: data.name,
description: data.description,
createdDate: data.createdDate,
}))}
/>
)}
<Box height={100} />

<div
ref={observerTarget}
css={`
height: 10px;
`}
/>
<DeleteDatasetDialog
cardId={cardId}
enableButton={enableButton}
Expand Down
35 changes: 29 additions & 6 deletions src/app/modules/home-module/components/Reports/reportsGrid.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import React from "react";
import React, { useRef } from "react";
import axios from "axios";
import get from "lodash/get";
import Box from "@material-ui/core/Box";
Expand All @@ -11,6 +11,7 @@ import { HomepageTable } from "app/modules/home-module/components/Table";
import DeleteReportDialog from "app/components/Dialogs/deleteReportDialog";
import ReformedGridItem from "app/modules/home-module/components/Reports/reformedGridItem";
import ReportAddnewCard from "./reportAddNewCard";
import { useInfinityScroll } from "app/hooks/useInfinityScroll";

interface Props {
sortBy: string;
Expand All @@ -21,17 +22,23 @@ interface Props {
}

export default function ReportsGrid(props: Props) {
const observerTarget = useRef(null);
const [cardId, setCardId] = React.useState<number>(0);
const [modalDisplay, setModalDisplay] = React.useState<boolean>(false);
const [enableButton, setEnableButton] = React.useState<boolean>(false);

const [loadedReports, setLoadedReports] = React.useState<ReportModel[]>([]);
const limit = 15;
//used over usestate to get current offset value in the IntersectionObserver api, as it is not updated in usestate.
const offset = useRef(0);
const reports = useStoreState(
(state) => (state.reports.ReportGetList.crudData ?? []) as ReportModel[]
);

const loadReports = useStoreActions(
(actions) => actions.reports.ReportGetList.fetch
);
const reportsLoadSuccess = useStoreState(
(state) => state.reports.ReportGetList.success
);

const handleDelete = (index?: number) => {
setModalDisplay(false);
Expand Down Expand Up @@ -87,16 +94,29 @@ export default function ReportsGrid(props: Props) {
: "";
loadReports({
storeInCrudData: true,
filterString: `filter={${value}"order":"${sortByStr} desc"}`,
filterString: `filter={${value}"order":"${sortByStr} desc","limit":${limit},"offset":${offset.current}}`,
});
offset.current = offset.current + limit;
}
useInfinityScroll(loadData, observerTarget, props.searchStr, props.sortBy);

React.useEffect(() => {
if (props.searchStr.length === 0) {
loadData(props.searchStr, props.sortBy);
}
}, [props.searchStr, props.sortBy]);

React.useEffect(() => {
if (!reportsLoadSuccess) {
return;
}
//update the loaded reports
setLoadedReports((prevReports) => {
const f = reports.filter((report, i) => prevReports[i]?.id !== report.id);
return [...prevReports, ...f];
});
}, [reportsLoadSuccess]);

const [,] = useDebounce(
() => {
if (props.searchStr.length > 0) {
Expand All @@ -112,7 +132,7 @@ export default function ReportsGrid(props: Props) {
{!props.tableView && (
<Grid container spacing={2}>
{props.addCard && <ReportAddnewCard />}
{reports.map((data, index) => (
{loadedReports.map((data, index) => (
<Grid item key={data.id} xs={12} sm={6} md={4} lg={3}>
<ReformedGridItem
id={data.id}
Expand All @@ -135,14 +155,17 @@ export default function ReportsGrid(props: Props) {
)}
{props.tableView && (
<HomepageTable
data={reports.map((data) => ({
data={loadedReports.map((data) => ({
id: data.id,
name: data.name,
description: data.title,
createdDate: data.createdDate,
}))}
/>
)}
<Box height={100} />

<div ref={observerTarget} />
<DeleteReportDialog
cardId={cardId}
modalDisplay={modalDisplay}
Expand Down
Loading

0 comments on commit b32e3a0

Please sign in to comment.