ChartTimeseries.loadLines = statePath => async(dispatch, getState) => { METRICS.startLoadChart(); const state = get(getState(), statePath); const generator = generateTimeseries( state.lineDescriptors.slice(0, ChartTimeseries.MAX_LINES), {minRevision: state.minRevision, maxRevision: state.maxRevision}, state.levelOfDetail); for await (const {timeseriesesByLine, errors} of generator) { if (!layoutTimeseries.isReady) await layoutTimeseries.readyPromise; const state = get(getState(), statePath); if (!state) { // This chart is no longer in the redux store. return; } dispatch({ type: ChartTimeseries.reducers.layout.name, timeseriesesByLine, errors, statePath, }); ChartTimeseries.actions.measureYTicks(statePath)(dispatch, getState); if (timeseriesesByLine.length) METRICS.endLoadChart(); } };
buildColumns: statePath => async(dispatch, getState) => { if (!get(getState(), statePath)) return; dispatch({ type: MemoryComponents.reducers.buildColumns.name, statePath, }); },
loadTimeseries: statePath => async(dispatch, getState) => { dispatch(CHAIN( {type: ChartSection.reducers.loadTimeseries.name, statePath}, { type: SparklineCompound.reducers.buildRelatedTabs.name, statePath, })); const state = get(getState(), statePath); if (state.selectedLineDescriptorHash) { // Restore from URL. for (const lineDescriptor of state.lineDescriptors) { const lineDescriptorHash = await sha( ChartTimeseries.stringifyDescriptor(lineDescriptor)); if (!lineDescriptorHash.startsWith( state.selectedLineDescriptorHash)) { continue; } dispatch(UPDATE(statePath, { lineDescriptors: [lineDescriptor], })); break; } } },
updateLegendColors: statePath => async(dispatch, getState) => { const state = get(getState(), statePath); if (!state || !state.legend) return; dispatch({ type: ChartSection.reducers.updateLegendColors.name, statePath, }); },
measureYTicks: statePath => async(dispatch, getState) => { const ticks = collectYAxisTicks(get(getState(), statePath)); if (ticks.length === 0) return; dispatch({ type: ChartTimeseries.reducers.yAxisWidth.name, statePath, rects: await Promise.all(ticks.map(tick => measureText(tick))), }); },
load: statePath => async(dispatch, getState) => { let state = get(getState(), statePath); if (!state) return; dispatch(UPDATE(statePath, { isLoading: true, lines: [], errors: new Set(), })); await ChartTimeseries.loadLines(statePath)(dispatch, getState); state = get(getState(), statePath); if (!state) { // User closed the chart before it could finish loading return; } dispatch(UPDATE(statePath, {isLoading: false})); },
maybeLoadTimeseries: statePath => async(dispatch, getState) => { // If the first 3 components are filled, then load the timeseries. const state = get(getState(), statePath); if (state.descriptor.suite.selectedOptions.length && state.descriptor.measurement.selectedOptions.length && state.statistic.selectedOptions.length) { METRICS.endChartAction(); ChartSection.actions.loadTimeseries(statePath)(dispatch, getState); } else { dispatch(UPDATE(statePath, {lineDescriptors: []})); } },
updateState_() { const state = this.getState(); let propertiesChanged = false; for (const [name, prop] of Object.entries(this.constructor.properties)) { const {statePath} = this.constructor.properties[name]; if (!statePath) continue; const value = (typeof statePath === 'function') ? statePath.call(this, state) : get(state, statePath); const changed = this._setPendingPropertyOrPath(name, value, true); propertiesChanged = propertiesChanged || changed; } if (propertiesChanged) this._invalidateProperties(); }
async(dispatch, getState) => { const chartPath = statePath + '.chartLayout'; const state = get(getState(), statePath); lineDescriptor = JSON.stringify(lineDescriptor); for (let lineIndex = 0; lineIndex < state.chartLayout.lines.length; ++lineIndex) { const line = state.chartLayout.lines[lineIndex]; if (JSON.stringify(line.descriptor) === lineDescriptor) { dispatch(CHAIN( { type: ChartTimeseries.reducers.mouseYTicks.name, statePath: chartPath, line, }, { type: ChartBase.reducers.boldLine.name, statePath: chartPath, lineIndex, }, )); break; } } },