Skip to content

Commit

Permalink
inifinite scroll
Browse files Browse the repository at this point in the history
  • Loading branch information
yaodingyd committed Jun 7, 2024
1 parent 4012e42 commit 75bcb30
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 15 deletions.
73 changes: 71 additions & 2 deletions src/actions/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { getSegmentFetchRange, hasRoutesData } from '../timeline/segments';
import { getDeviceFromState, deviceVersionAtLeast } from '../utils';

let routesRequest = null;
let routesRequestPromise = null;

export function checkRoutesData() {
return (dispatch, getState) => {
Expand All @@ -22,7 +23,8 @@ export function checkRoutesData() {
return;
}
if (routesRequest && routesRequest.dongleId === state.dongleId) {
return;
// there is already an pending request
return routesRequestPromise;
}
console.debug('We need to update the segment metadata...');
const { dongleId } = state;
Expand All @@ -33,7 +35,7 @@ export function checkRoutesData() {
dongleId,
};

routesRequest.req.then((routesData) => {
routesRequestPromise = routesRequest.req.then((routesData) => {
state = getState();
const currentRange = getSegmentFetchRange(state);
if (currentRange.start !== fetchRange.start
Expand Down Expand Up @@ -83,11 +85,15 @@ export function checkRoutesData() {
});

routesRequest = null;

return routes
}).catch((err) => {
console.error('Failure fetching routes metadata', err);
Sentry.captureException(err, { fingerprint: 'timeline_fetch_routes' });
routesRequest = null;
});

return routesRequestPromise
};
}

Expand Down Expand Up @@ -389,3 +395,66 @@ export function updateRoute(fullname, route) {
route,
};
}

const ONE_DAY = 1000 * 60 * 60 * 24
const ONE_WEEK = ONE_DAY * 7
const TWO_WEEK = ONE_WEEK * 2
const ONE_MONTH = ONE_WEEK * 4
const TWO_MONTH = ONE_MONTH * 2
const SIX_MONTH = ONE_MONTH * 6
const ONE_YEAR = SIX_MONTH * 2
const lookBackIntervalArray = [
ONE_DAY, ONE_DAY * 2, ONE_DAY * 3, ONE_DAY * 4, ONE_DAY * 5,
ONE_WEEK,
TWO_WEEK,
ONE_MONTH,
TWO_MONTH,
SIX_MONTH,
ONE_YEAR,
]
let checkRoutesDataWithLookBackInProgress = false

// check routes and keep extending the start time to 10 days earlier
// until we have more routes
export function checkRoutesDataWithLookBack() {
if (checkRoutesDataWithLookBackInProgress) {
return
}

return async (dispatch, getState) => {
checkRoutesDataWithLookBackInProgress = true
const state = getState()
const routes = state.routes;
let {start, end} = state.filter
let counter = 0

// eslint-disable-next-line no-constant-condition
while (true) {
console.info('fetch with window: ',new Date(start).toDateString())

if (counter >= lookBackIntervalArray.length) {
break
}

try {
dispatch(selectTimeFilter(start, end));
// eslint-disable-next-line no-await-in-loop
const data = await dispatch(checkRoutesData());
if (data && (
!routes && data.length > 1 ||
routes && data.length > routes.length)
) {
checkRoutesDataWithLookBackInProgress = false
break;
} else {
start = start - lookBackIntervalArray[counter];
counter = counter + 1
}
} catch (error) {
checkRoutesDataWithLookBackInProgress = false
console.debug('Fetching data failed: ', error);
break;
}
}
};
}
9 changes: 6 additions & 3 deletions src/components/AppHeader/TimeFilter.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import dayjs from 'dayjs';
import { Button, Divider, FormControl, MenuItem, Modal, Paper, Select, Typography, withStyles } from '@material-ui/core';

import Colors from '../../colors';
import { selectTimeFilter } from '../../actions';
import { selectTimeFilter, checkRoutesDataWithLookBack } from '../../actions';
import { getDefaultFilter } from '../../initialState';
import VisibilityHandler from '../VisibilityHandler';

Expand Down Expand Up @@ -140,6 +140,8 @@ class TimeSelect extends Component {
return '1-week';
} if (timeRange === 1000 * 60 * 60 * 24) {
return '24-hours';
} if (timeRange % (1000 * 60 * 60 * 24 * 10) === 0) {
return 'last'
}
}

Expand Down Expand Up @@ -167,7 +169,7 @@ class TimeSelect extends Component {

onVisible() {
const filter = getDefaultFilter();
this.props.dispatch(selectTimeFilter(filter.start, filter.end));
this.props.dispatch(checkRoutesDataWithLookBack(filter.start, filter.end));
}

render() {
Expand All @@ -179,7 +181,7 @@ class TimeSelect extends Component {

return (
<>
<VisibilityHandler onVisible={ this.onVisible } minInterval={ 300 } resetOnHidden />
<VisibilityHandler onVisible={ this.onVisible } minInterval={ 300 } resetOnHidden onInit />
<FormControl>
<Select
name="timerange"
Expand All @@ -191,6 +193,7 @@ class TimeSelect extends Component {
<MenuItem value="24-hours">Last 24 Hours</MenuItem>
<MenuItem value="1-week">{this.lastWeekText()}</MenuItem>
<MenuItem value="2-weeks">{this.last2WeeksText()}</MenuItem>
<MenuItem value="last">Last Rides</MenuItem>
</Select>
</FormControl>
<Modal
Expand Down
31 changes: 22 additions & 9 deletions src/components/Dashboard/DriveList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@ import { connect } from 'react-redux';
import Obstruction from 'obstruction';
import { withStyles } from '@material-ui/core';

import { checkRoutesData } from '../../actions';
import { checkRoutesData, checkRoutesDataWithLookBack } from '../../actions';
import VisibilityHandler from '../VisibilityHandler';

import DriveListEmpty from './DriveListEmpty';
import DriveListItem from './DriveListItem';
import ScrollIntoView from '../ScrollIntoView'

const styles = () => ({
drivesTable: {
Expand All @@ -22,22 +23,32 @@ const styles = () => ({
});

const DriveList = (props) => {
const { dispatch, classes, device, routes } = props;

const { dispatch, classes, device, routes, lastRoutes } = props;
let emptyContent;
let content;
if (!routes || routes.length === 0) {
content = <DriveListEmpty device={device} routes={routes} />;
} else {
emptyContent = <DriveListEmpty device={device} routes={routes} />;
}

// we clean up routes during data fetching, fallback to using lastRoutes to display current data
const displayRoutes = routes || lastRoutes
if (displayRoutes && displayRoutes.length){
// sort routes by start_time_utc_millis with the latest drive first
// Workaround upstream sorting issue for now
// possibly from https://github.com/commaai/connect/issues/451
routes.sort((a, b) => b.start_time_utc_millis - a.start_time_utc_millis);
displayRoutes.sort((a, b) => b.start_time_utc_millis - a.start_time_utc_millis);
const routesSize = displayRoutes.length

content = (
<div className={`${classes.drives} DriveList`}>
{routes.map((drive) => (
<DriveListItem key={drive.fullname} drive={drive} />
))}
{displayRoutes.map((drive, index) => {
// when the second to last item is in view, we fetch the next routes
return (index === routesSize - 2 ?
<ScrollIntoView key={drive.fullname} onInView={() => dispatch(checkRoutesDataWithLookBack())}>
<DriveListItem drive={drive} />
</ScrollIntoView> :
<DriveListItem key={drive.fullname} drive={drive} />)
})}
</div>
);
}
Expand All @@ -46,12 +57,14 @@ const DriveList = (props) => {
<div className={classes.drivesTable}>
<VisibilityHandler onVisible={() => dispatch(checkRoutesData())} minInterval={60} />
{content}
{emptyContent}
</div>
);
};

const stateToProps = Obstruction({
routes: 'routes',
lastRoutes : 'lastRoutes',
device: 'device',
});

Expand Down
39 changes: 39 additions & 0 deletions src/components/ScrollIntoView/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import React, { useEffect, useRef } from 'react';

const ScrollIntoView = ({ onInView, children, key }) => {
const elementRef = useRef(null);

useEffect(() => {
const options = {
root: null, // relative to the viewport
rootMargin: '0px',
threshold: 0.1 // 10% of the target's visibility
};

const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
onInView();
}
});
}, options);

if (elementRef.current) {
observer.observe(elementRef.current);
}

return () => {
if (observer && elementRef.current) {
observer.unobserve(elementRef.current);
}
};
}, [onInView]);

return (
<div ref={elementRef}>
{children}
</div>
);
};

export default ScrollIntoView;
3 changes: 2 additions & 1 deletion src/initialState.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export function getDefaultFilter() {
d.setHours(d.getHours() + 1, 0, 0, 0);

return {
start: (new Date(d.getTime() - 1000 * 60 * 60 * 24 * 14)).getTime(),
start: (new Date(d.getTime() - 1000 * 60 * 60 * 24 * 10)).getTime(),
end: d.getTime(),
};
}
Expand Down Expand Up @@ -39,6 +39,7 @@ export default {
end: null,
},
currentRoute: null,
lastRoutes: null,

profile: null,
devices: null,
Expand Down
1 change: 1 addition & 0 deletions src/reducers/globalState.js
Original file line number Diff line number Diff line change
Expand Up @@ -319,6 +319,7 @@ export default function reducer(_state, action) {
.reduce((obj, id) => { obj[id] = state.filesUploading[id]; return obj; }, {});
break;
case Types.ACTION_ROUTES_METADATA:
state.lastRoutes = action.routes
state.routes = action.routes;
state.routesMeta = {
dongleId: action.dongleId,
Expand Down

0 comments on commit 75bcb30

Please sign in to comment.