diff --git a/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx b/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx index 7146845333f042541e3111ecb7949d84a5631e82..9c2f1e7316a83b36b15e28fdbfea3da9d9f7436e 100644 --- a/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx +++ b/frontend/src/metabase/dashboard/components/AddSeriesModal.jsx @@ -81,7 +81,7 @@ export default class AddSeriesModal extends Component { if (e.target.checked) { if (getIn(dashcardData, [dashcard.id, card.id]) === undefined) { this.setState({ state: "loading" }); - await this.props.fetchCardData(card, dashcard); + await this.props.fetchCardData(card, dashcard, { reload: false, clear: true }); } let sourceDataset = getIn(this.props.dashcardData, [dashcard.id, dashcard.card.id]); let seriesDataset = getIn(this.props.dashcardData, [dashcard.id, card.id]); diff --git a/frontend/src/metabase/dashboard/components/Dashboard.jsx b/frontend/src/metabase/dashboard/components/Dashboard.jsx index 458170679f216ca31baa1d44bf37e83a5b8951fd..c14a429386bffc0106a8c58ea53498bac124c884 100644 --- a/frontend/src/metabase/dashboard/components/Dashboard.jsx +++ b/frontend/src/metabase/dashboard/components/Dashboard.jsx @@ -75,7 +75,7 @@ export default class Dashboard extends Component { if (this.props.params.dashboardId !== nextProps.params.dashboardId) { this.loadDashboard(nextProps.params.dashboardId); } else if (!_.isEqual(this.props.parameterValues, nextProps.parameterValues) || !this.props.dashboard) { - this.fetchDashboardCardData(nextProps, true); + this.fetchDashboardCardData(nextProps, { reload: false, clear: true }); } } @@ -275,12 +275,12 @@ export default class Dashboard extends Component { } // we don't call this initially because DashCards initiate their own fetchCardData - fetchDashboardCardData(props, clearExisting) { + fetchDashboardCardData(props, options) { if (props.dashboard) { for (const dashcard of props.dashboard.ordered_cards) { const cards = [dashcard.card].concat(dashcard.series || []); for (const card of cards) { - props.fetchCardData(card, dashcard, clearExisting); + props.fetchCardData(card, dashcard, options); } } } @@ -292,7 +292,7 @@ export default class Dashboard extends Component { refreshElapsed = 0; await this.props.fetchDashboard(this.props.params.dashboardId, this.props.location.query); - this.fetchDashboardCardData(this.props); + this.fetchDashboardCardData(this.props, { reload: true, clear: false }); } this.setState({ refreshElapsed }); } @@ -304,6 +304,7 @@ export default class Dashboard extends Component { let parameters = dashboard && dashboard.parameters && dashboard.parameters.map(parameter => <ParameterWidget + key={parameter.id} className="ml1" isEditing={isEditing} isFullscreen={isFullscreen} diff --git a/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx b/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx index f57a6755b96d97fff6334b83aee71d9e5c568f6d..56e9828c255a7600ef0bbedce94723311ea0f6b7 100644 --- a/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx +++ b/frontend/src/metabase/dashboard/components/parameters/widgets/TextWidget.jsx @@ -12,7 +12,6 @@ export default class TextWidget extends Component { static propTypes = { value: PropTypes.any, setValue: PropTypes.func.isRequired, - onClose: PropTypes.func.isRequired, className: PropTypes.string, isEditing: PropTypes.bool, commitImmediately: PropTypes.bool, diff --git a/frontend/src/metabase/dashboard/dashboard.js b/frontend/src/metabase/dashboard/dashboard.js index 705b5031aeb9fe29560267517d49dafc883b323e..3801399842cb44b9c3cf60a2ac52debb9910ccf6 100644 --- a/frontend/src/metabase/dashboard/dashboard.js +++ b/frontend/src/metabase/dashboard/dashboard.js @@ -115,7 +115,7 @@ export const addCardToDashboard = function({ dashId, cardId }) { ...getPositionForNewDashCard(existingCards) }; dispatch(createAction(ADD_CARD_TO_DASH)(dashcard)); - dispatch(fetchCardData(card, dashcard)); + dispatch(fetchCardData(card, dashcard, { reload: true, clear: true })); }; } @@ -134,12 +134,8 @@ export async function fetchDataOrError(dataPromise) { } } -export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, dashcard, clearExisting = false) { +export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, dashcard, { reload, clear } = {}) { return async function(dispatch, getState) { - if (clearExisting) { - dispatch(clearCardData(card.id, dashcard.id)); - } - // If the dataset_query was filtered then we don't have permisison to view this card, so // shortcircuit and return a fake 403 if (!card.dataset_query) { @@ -150,26 +146,49 @@ export const fetchCardData = createThunkAction(FETCH_CARD_DATA, function(card, d }; } - let result = null; + const { dashboardId, dashboards, parameterValues, dashcardData } = getState().dashboard; + const dashboard = dashboards[dashboardId]; // if we have a parameter, apply it to the card query before we execute - let { dashboardId } = getState().dashboard; - let { dashboards, parameterValues } = getState().dashboard; + const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings); - let dashboard = dashboards[dashboardId]; + if (!reload) { + // if reload not set, check to see if the last result has the same query dict and return that + const lastResult = i.getIn(dashcardData, [dashcard.id, card.id]); + // "constraints" is added by the backend, remove it when comparing + if (lastResult && angular.equals(_.omit(lastResult.json_query, "constraints"), datasetQuery)) { + return { + dashcard_id: dashcard.id, + card_id: card.id, + result: lastResult + }; + } + } - const datasetQuery = applyParameters(card, dashboard.parameters, parameterValues, dashcard && dashcard.parameter_mappings); + if (clear) { + // clears the card data to indicate the card is reloading + dispatch(clearCardData(card.id, dashcard.id)); + } + + let result = null; + // start a timer that will fetch the expected card duration if the query takes too long let slowCardTimer = setTimeout(() => { if (result === null) { dispatch(fetchCardDuration(card, datasetQuery)); } }, DATASET_SLOW_TIMEOUT); + // make the actual request result = await fetchDataOrError(CardApi.query({cardID: card.id, parameters: datasetQuery.parameters})); clearTimeout(slowCardTimer); - return { dashcard_id: dashcard.id, card_id: card.id, result }; + + return { + dashcard_id: dashcard.id, + card_id: card.id, + result: result + }; }; });