diff --git a/superset-frontend/src/dashboard/actions/dashboardState.js b/superset-frontend/src/dashboard/actions/dashboardState.js index afbbe74a0ab8..0afe42e063f7 100644 --- a/superset-frontend/src/dashboard/actions/dashboardState.js +++ b/superset-frontend/src/dashboard/actions/dashboardState.js @@ -422,6 +422,16 @@ const refreshCharts = (chartList, force, interval, dashboardId, dispatch) => resolve(); }); +export const ON_FILTERS_REFRESH = 'ON_FILTERS_REFRESH'; +export function onFiltersRefresh() { + return { type: ON_FILTERS_REFRESH }; +} + +export const ON_FILTERS_REFRESH_SUCCESS = 'ON_FILTERS_REFRESH_SUCCESS'; +export function onFiltersRefreshSuccess() { + return { type: ON_FILTERS_REFRESH_SUCCESS }; +} + export const ON_REFRESH_SUCCESS = 'ON_REFRESH_SUCCESS'; export function onRefreshSuccess() { return { type: ON_REFRESH_SUCCESS }; @@ -436,8 +446,11 @@ export function onRefresh( ) { return dispatch => { dispatch({ type: ON_REFRESH }); - refreshCharts(chartList, force, interval, dashboardId, dispatch).then(() => - dispatch({ type: ON_REFRESH_SUCCESS }), + refreshCharts(chartList, force, interval, dashboardId, dispatch).then( + () => { + dispatch(onRefreshSuccess()); + dispatch(onFiltersRefresh()); + }, ); }; } diff --git a/superset-frontend/src/dashboard/actions/hydrate.js b/superset-frontend/src/dashboard/actions/hydrate.js index 8e6ce5d7b415..4c8a978e9638 100644 --- a/superset-frontend/src/dashboard/actions/hydrate.js +++ b/superset-frontend/src/dashboard/actions/hydrate.js @@ -406,6 +406,7 @@ export const hydrateDashboard = maxUndoHistoryExceeded: false, lastModifiedTime: dashboardData.changed_on, isRefreshing: false, + isFiltersRefreshing: false, activeTabs: dashboardState?.activeTabs || [], filterboxMigrationState, }, diff --git a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx index 0a12d1cd956f..4337d59ed86f 100644 --- a/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx +++ b/superset-frontend/src/dashboard/components/nativeFilters/FilterBar/FilterControls/FilterValue.tsx @@ -43,6 +43,7 @@ import { FeatureFlag, isFeatureEnabled } from 'src/featureFlags'; import { waitForAsyncData } from 'src/middleware/asyncEvent'; import { ClientErrorObject } from 'src/utils/getClientErrorObject'; import { RootState } from 'src/dashboard/types'; +import { onFiltersRefreshSuccess } from 'src/dashboard/actions/dashboardState'; import { dispatchFocusAction } from './utils'; import { FilterProps } from './types'; import { getFormData } from '../../utils'; @@ -62,6 +63,18 @@ const StyledDiv = styled.div` const queriesDataPlaceholder = [{ data: [{}] }]; const behaviors = [Behavior.NATIVE_FILTER]; +const useShouldFilterRefresh = () => { + const isDashboardRefreshing = useSelector( + state => state.dashboardState.isRefreshing, + ); + const isFilterRefreshing = useSelector( + state => state.dashboardState.isFiltersRefreshing, + ); + + // trigger filter requests only after charts requests were triggered + return !isDashboardRefreshing && isFilterRefreshing; +}; + const FilterValue: React.FC = ({ dataMaskSelected, filter, @@ -75,9 +88,7 @@ const FilterValue: React.FC = ({ const { id, targets, filterType, adhoc_filters, time_range } = filter; const metadata = getChartMetadataRegistry().get(filterType); const dependencies = useFilterDependencies(id, dataMaskSelected); - const isDashboardRefreshing = useSelector( - state => state.dashboardState.isRefreshing, - ); + const shouldRefresh = useShouldFilterRefresh(); const [state, setState] = useState([]); const [error, setError] = useState(''); const [formData, setFormData] = useState>({ @@ -97,6 +108,14 @@ const FilterValue: React.FC = ({ const [isRefreshing, setIsRefreshing] = useState(false); const dispatch = useDispatch(); + const handleFilterLoadFinish = useCallback(() => { + setIsRefreshing(false); + setIsLoading(false); + if (shouldRefresh) { + dispatch(onFiltersRefreshSuccess()); + } + }, [dispatch, shouldRefresh]); + useEffect(() => { if (!inViewFirstTime && inView) { setInViewFirstTime(true); @@ -127,7 +146,7 @@ const FilterValue: React.FC = ({ !isRefreshing && (!isEqualWith(formData, newFormData, customizer) || !isEqual(ownState, filterOwnState) || - isDashboardRefreshing) + shouldRefresh) ) { setFormData(newFormData); setOwnState(filterOwnState); @@ -147,22 +166,19 @@ const FilterValue: React.FC = ({ const result = 'result' in json ? json.result[0] : json; if (response.status === 200) { - setIsRefreshing(false); - setIsLoading(false); setState([result]); + handleFilterLoadFinish(); } else if (response.status === 202) { waitForAsyncData(result) .then((asyncResult: ChartDataResponseResult[]) => { - setIsRefreshing(false); - setIsLoading(false); setState(asyncResult); + handleFilterLoadFinish(); }) .catch((error: ClientErrorObject) => { setError( error.message || error.error || t('Check configuration'), ); - setIsRefreshing(false); - setIsLoading(false); + handleFilterLoadFinish(); }); } else { throw new Error( @@ -172,14 +188,12 @@ const FilterValue: React.FC = ({ } else { setState(json.result); setError(''); - setIsRefreshing(false); - setIsLoading(false); + handleFilterLoadFinish(); } }) .catch((error: Response) => { setError(error.statusText); - setIsRefreshing(false); - setIsLoading(false); + handleFilterLoadFinish(); }); } }, [ @@ -187,10 +201,11 @@ const FilterValue: React.FC = ({ dependencies, datasetId, groupby, + handleFilterLoadFinish, JSON.stringify(filter), hasDataSource, isRefreshing, - isDashboardRefreshing, + shouldRefresh, ]); useEffect(() => { diff --git a/superset-frontend/src/dashboard/reducers/dashboardState.js b/superset-frontend/src/dashboard/reducers/dashboardState.js index 0f1d78418fa0..64c794af9331 100644 --- a/superset-frontend/src/dashboard/reducers/dashboardState.js +++ b/superset-frontend/src/dashboard/reducers/dashboardState.js @@ -39,6 +39,8 @@ import { UNSET_FOCUSED_FILTER_FIELD, SET_ACTIVE_TABS, SET_FULL_SIZE_CHART_ID, + ON_FILTERS_REFRESH, + ON_FILTERS_REFRESH_SUCCESS, } from '../actions/dashboardState'; import { HYDRATE_DASHBOARD } from '../actions/hydrate'; @@ -136,6 +138,18 @@ export default function dashboardStateReducer(state = {}, action) { isRefreshing: true, }; }, + [ON_FILTERS_REFRESH]() { + return { + ...state, + isFiltersRefreshing: true, + }; + }, + [ON_FILTERS_REFRESH_SUCCESS]() { + return { + ...state, + isFiltersRefreshing: false, + }; + }, [ON_REFRESH_SUCCESS]() { return { ...state, diff --git a/superset-frontend/src/dashboard/types.ts b/superset-frontend/src/dashboard/types.ts index 426fa03d0fe7..fbdf362eea70 100644 --- a/superset-frontend/src/dashboard/types.ts +++ b/superset-frontend/src/dashboard/types.ts @@ -63,6 +63,7 @@ export type DashboardState = { activeTabs: ActiveTabs; fullSizeChartId: number | null; isRefreshing: boolean; + isFiltersRefreshing: boolean; hasUnsavedChanges: boolean; }; export type DashboardInfo = {