Skip to content

Commit

Permalink
chore: Move QueryViewNavigator to hooks (#1140)
Browse files Browse the repository at this point in the history
  • Loading branch information
czgu authored Feb 1, 2023
1 parent dfb210b commit c3673df
Showing 1 changed file with 135 additions and 144 deletions.
279 changes: 135 additions & 144 deletions querybook/webapp/components/QueryViewNavigator/QueryViewNavigator.tsx
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
import { bind, debounce } from 'lodash-decorators';
import React from 'react';
import { connect } from 'react-redux';
import { debounce } from 'lodash';
import React, { useCallback, useEffect, useRef } from 'react';
import { useDispatch } from 'react-redux';

import { IQueryExecution, QueryExecutionStatus } from 'const/queryExecution';
import { useShallowSelector } from 'hooks/redux/useShallowSelector';
import {
queryEngineByIdEnvSelector,
queryEngineSelector,
} from 'redux/queryEngine/selector';
import * as queryExecutionsActions from 'redux/queryExecutions/action';
import * as queryViewActions from 'redux/queryView/action';
import { queryExecutionResultSelector } from 'redux/queryView/selector';
import { Dispatch, IStoreState } from 'redux/store/types';
import { IStoreState } from 'redux/store/types';
import { Icon } from 'ui/Icon/Icon';
import { AccentText } from 'ui/StyledText/StyledText';

Expand All @@ -19,148 +20,138 @@ import { QueryViewFilter } from './QueryViewFilter';

import './QueryViewNavigator.scss';

type StateProps = ReturnType<typeof mapStateToProps>;
type DispatchProps = ReturnType<typeof mapDispatchToProps>;

export type IProps = StateProps & DispatchProps;

class QueryViewNavigatorComponent extends React.PureComponent<IProps> {
private navigatorScrollRef = React.createRef<HTMLDivElement>();

public constructor(props) {
super(props);

this.state = {
showQueryViewModalForId: null,
};
}

@bind
public setupPolling(queryExecutions: IQueryExecution[]) {
for (const queryExecution of queryExecutions) {
if (queryExecution.status < QueryExecutionStatus.DONE) {
this.props.pollQueryExecution(queryExecution.id);
export const QueryViewNavigator: React.FC = () => {
const {
queryResults,
isLoadingQueries,
queryViewFilters,
queryEngines,
queryEngineById,
} = useShallowSelector((state: IStoreState) => ({
queryResults: queryExecutionResultSelector(state),
isLoadingQueries: state.queryView.isLoading,
queryViewFilters: state.queryView.filters,
queryEngines: queryEngineSelector(state),
queryEngineById: queryEngineByIdEnvSelector(state),
}));

const dispatch = useDispatch();
const updateFilter = useCallback(
(filterKey: string, filterValue: any) => {
dispatch(queryViewActions.updateFilter(filterKey, filterValue));
},
[dispatch]
);

const initializeFromQueryParam = useCallback(
() => dispatch(queryViewActions.mapQueryParamToState()),
[dispatch]
);

const loadQueries = useCallback(
() => dispatch(queryViewActions.searchQueries()),
[dispatch]
);

const pollQueryExecution = useCallback(
(queryExecutionId: number) => {
dispatch(
queryExecutionsActions.pollQueryExecution(queryExecutionId)
);
},
[dispatch]
);

const navigatorScrollRef = useRef<HTMLDivElement>();

const setupPolling = useCallback(
(queryExecutions: IQueryExecution[]) => {
for (const queryExecution of queryExecutions) {
if (queryExecution.status < QueryExecutionStatus.DONE) {
pollQueryExecution(queryExecution.id);
}
}
}
}

@bind
public onNavigatorScroll(event) {
if (event.target === this.navigatorScrollRef.current) {
this.loadMoreIfScrolledToBottom();
}
}

@bind
@debounce(500)
public loadMoreIfScrolledToBottom() {
const { isLoadingQueries, loadQueries } = this.props;

if (!isLoadingQueries && this.navigatorScrollRef) {
const el = this.navigatorScrollRef.current;
if (el.scrollHeight - el.scrollTop === el.clientHeight) {
// Scrolled to bottom
loadQueries();
},
[pollQueryExecution]
);

// eslint-disable-next-line react-hooks/exhaustive-deps
const loadMoreIfScrolledToBottom = useCallback(
debounce(() => {
if (!isLoadingQueries && navigatorScrollRef) {
const el = navigatorScrollRef.current;
if (el.scrollHeight - el.scrollTop === el.clientHeight) {
// Scrolled to bottom
loadQueries();
}
}
}
}

public componentDidMount() {
this.props.initializeFromQueryParam();
this.setupPolling(this.props.queryResults);
}

public componentDidUpdate(prevProps) {
if (this.props.queryResults !== prevProps.queryResults) {
this.setupPolling(this.props.queryResults);
}
}

public render() {
const {
queryResults,
isLoadingQueries,
queryViewFilters,
queryEngines,
queryEngineById,

initializeFromQueryParam,
updateFilter,
} = this.props;

const queryViewFilterDOM = (
<QueryViewFilter
filters={queryViewFilters}
updateFilter={updateFilter}
onRefresh={initializeFromQueryParam}
queryEngines={queryEngines}
queryEngineById={queryEngineById}
/>
);

const queryResultsListDOM = queryResults.map((queryResult) => (
<QueryResult
key={queryResult.id}
queryExecution={queryResult}
queryEngineById={queryEngineById}
/>
));

const loadingDOM = isLoadingQueries ? (
<div className="flex-column m24">
<Icon name="Loading" className="mb16" />
<AccentText color="light" weight="bold">
Loading Executions
</AccentText>
</div>
}, 500),
[loadQueries, isLoadingQueries]
);

const handleNavigatorScroll = useCallback(
(event) => {
if (event.target === navigatorScrollRef.current) {
loadMoreIfScrolledToBottom();
}
},
[loadMoreIfScrolledToBottom]
);

useEffect(() => {
initializeFromQueryParam();
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

useEffect(() => {
setupPolling(queryResults);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [queryResults]);

const queryViewFilterDOM = (
<QueryViewFilter
filters={queryViewFilters}
updateFilter={updateFilter}
onRefresh={initializeFromQueryParam}
queryEngines={queryEngines}
queryEngineById={queryEngineById}
/>
);

const queryResultsListDOM = queryResults.map((queryResult) => (
<QueryResult
key={queryResult.id}
queryExecution={queryResult}
queryEngineById={queryEngineById}
/>
));

const loadingDOM = isLoadingQueries ? (
<div className="flex-column m24">
<Icon name="Loading" className="mb16" />
<AccentText color="light" weight="bold">
Loading Executions
</AccentText>
</div>
) : null;

const noResultDOM =
queryResults.length === 0 && !loadingDOM ? (
<div className="empty-section-message">No Executions</div>
) : null;

const noResultDOM =
queryResults.length === 0 && !loadingDOM ? (
<div className="empty-section-message">No Executions</div>
) : null;

return (
<div className="QueryViewNavigator SidebarNavigator">
<div className="list-header">{queryViewFilterDOM}</div>
<div
ref={this.navigatorScrollRef}
className="list-content scroll-wrapper"
onScroll={this.onNavigatorScroll}
>
{queryResultsListDOM}
{loadingDOM}
{noResultDOM}
</div>
return (
<div className="QueryViewNavigator SidebarNavigator">
<div className="list-header">{queryViewFilterDOM}</div>
<div
ref={navigatorScrollRef}
className="list-content scroll-wrapper"
onScroll={handleNavigatorScroll}
>
{queryResultsListDOM}
{loadingDOM}
{noResultDOM}
</div>
);
}
}

const mapStateToProps = (state: IStoreState) => ({
queryResults: queryExecutionResultSelector(state),
isLoadingQueries: state.queryView.isLoading,
queryViewFilters: state.queryView.filters,
queryEngines: queryEngineSelector(state),
queryEngineById: queryEngineByIdEnvSelector(state),
});

const mapDispatchToProps = (dispatch: Dispatch) => ({
updateFilter: (filterKey: string, filterValue: any) => {
dispatch(queryViewActions.updateFilter(filterKey, filterValue));
},

initializeFromQueryParam: () =>
dispatch(queryViewActions.mapQueryParamToState()),

loadQueries: () => dispatch(queryViewActions.searchQueries()),

pollQueryExecution: (queryExecutionId) => {
dispatch(queryExecutionsActions.pollQueryExecution(queryExecutionId));
},
});

export const QueryViewNavigator = connect(
mapStateToProps,
mapDispatchToProps
)(QueryViewNavigatorComponent);
</div>
);
};

0 comments on commit c3673df

Please sign in to comment.