Skip to content

Commit

Permalink
Address Unserializable Values In State/Actions.
Browse files Browse the repository at this point in the history
 - queryTimeIntervalString
 - rangeSliderIntervalString
 - barDuration
 - mapsBarDuration
 - compareTimeIntervalString
 - Re-Enable DevModeChecks
  • Loading branch information
ChrisMart21 committed Aug 14, 2024
1 parent a8237ba commit 04ba6a5
Show file tree
Hide file tree
Showing 12 changed files with 126 additions and 77 deletions.
5 changes: 3 additions & 2 deletions src/client/app/components/BarChartComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -115,14 +115,15 @@ export default function BarChartComponent() {
const startTS = utc(e['xaxis.range[0]']);
const endTS = utc(e['xaxis.range[1]']);
const workingTimeInterval = new TimeInterval(startTS, endTS);
dispatch(updateSliderRange(workingTimeInterval));
dispatch(updateSliderRange(workingTimeInterval.toString()));
}
else if (e['xaxis.range']) {
// this case is when the slider knobs are dragged.
const range = e['xaxis.range']!;
const startTS = range && range[0];
const endTS = range && range[1];
dispatch(updateSliderRange(new TimeInterval(utc(startTS), utc(endTS))));
const interval = new TimeInterval(utc(startTS), utc(endTS)).toString();
dispatch(updateSliderRange(interval));

}
}, 500, { leading: false, trailing: true })}
Expand Down
4 changes: 2 additions & 2 deletions src/client/app/components/DateRangeComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,8 +28,8 @@ export default function DateRangeComponent() {
const datePickerVisible = chartType !== ChartTypes.compare;

const handleChange = (value: Value) => {
dispatch(updateTimeInterval(dateRangeToTimeInterval(value)));
dispatch(changeSliderRange(dateRangeToTimeInterval(value)));
dispatch(updateTimeInterval(dateRangeToTimeInterval(value).toString()));
dispatch(changeSliderRange(dateRangeToTimeInterval(value).toString()));
};


Expand Down
5 changes: 3 additions & 2 deletions src/client/app/components/LineChartComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -101,14 +101,15 @@ export default function LineChartComponent() {
const startTS = utc(e['xaxis.range[0]']);
const endTS = utc(e['xaxis.range[1]']);
const workingTimeInterval = new TimeInterval(startTS, endTS);
dispatch(updateSliderRange(workingTimeInterval));
dispatch(updateSliderRange(workingTimeInterval.toString()));
}
else if (e['xaxis.range']) {
// this case is when the slider knobs are dragged.
const range = e['xaxis.range']!;
const startTS = range && range[0];
const endTS = range && range[1];
dispatch(updateSliderRange(new TimeInterval(utc(startTS), utc(endTS))));
const interval = new TimeInterval(utc(startTS), utc(endTS));
dispatch(updateSliderRange(interval.toString()));

}
}, 500, { leading: false, trailing: true })
Expand Down
4 changes: 2 additions & 2 deletions src/client/app/components/PlotNavComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const ExpandComponent = () => {
const dispatch = useAppDispatch();
return (
<img src='./expand.png' style={{ height: '25px' }}
onClick={() => { dispatch(changeSliderRange(TimeInterval.unbounded())); }}
onClick={() => { dispatch(changeSliderRange(TimeInterval.unbounded().toString())); }}
/>
);
};
Expand Down Expand Up @@ -70,7 +70,7 @@ export const RefreshGraphComponent = () => {
<img
src='./refresh.png'
style={{ height: '25px', transform: `rotate(${time}deg)`, visibility: iconVisible ? 'visible' : 'hidden' }}
onClick={() => { !somethingFetching && dispatch(updateTimeInterval(sliderInterval)); }}
onClick={() => { !somethingFetching && dispatch(updateTimeInterval(sliderInterval.toString())); }}
/>
);
};
5 changes: 3 additions & 2 deletions src/client/app/components/PlotOED.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -44,14 +44,15 @@ export const PlotOED = (props: OEDPlotProps) => {
const startTS = moment.utc(e['xaxis.range[0]']);
const endTS = moment.utc(e['xaxis.range[1]']);
const workingTimeInterval = new TimeInterval(startTS, endTS);
dispatch(changeSliderRange(workingTimeInterval));
dispatch(changeSliderRange(workingTimeInterval.toString()));
}
else if (e['xaxis.range']) {
// this case is when the slider knobs are dragged.
const range = figure.current.layout?.xaxis?.range;
const startTS = range && range[0];
const endTS = range && range[1];
dispatch(changeSliderRange(new TimeInterval(startTS, endTS)));
const interval = new TimeInterval(startTS, endTS).toString();
dispatch(changeSliderRange(interval));

}
}, 500, { leading: false, trailing: true });
Expand Down
5 changes: 3 additions & 2 deletions src/client/app/components/ThreeDComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { selectUnitDataById } from '../redux/api/unitsApi';
import { useAppSelector } from '../redux/reduxHooks';
import { selectThreeDQueryArgs } from '../redux/selectors/chartQuerySelectors';
import { selectThreeDComponentInfo } from '../redux/selectors/threeDSelectors';
import { selectGraphState } from '../redux/slices/graphSlice';
import { selectGraphState, selectQueryTimeInterval } from '../redux/slices/graphSlice';
import { ThreeDReading } from '../types/readings';
import { GraphState, MeterOrGroup } from '../types/redux/graph';
import { GroupDataByID } from '../types/redux/groups';
Expand All @@ -38,6 +38,7 @@ export default function ThreeDComponent() {
const groupDataById = useAppSelector(selectGroupDataById);
const unitDataById = useAppSelector(selectUnitDataById);
const graphState = useAppSelector(selectGraphState);
const queryTimeInterval = useAppSelector(selectQueryTimeInterval);
const locale = useAppSelector(selectSelectedLanguage);
const { meterOrGroupID, meterOrGroupName, isAreaCompatible } = useAppSelector(selectThreeDComponentInfo);

Expand All @@ -53,7 +54,7 @@ export default function ThreeDComponent() {
layout = setHelpLayout(translate('select.meter.group'));
} else if (graphState.areaNormalization && !isAreaCompatible) {
layout = setHelpLayout(`${meterOrGroupName}${translate('threeD.area.incompatible')}`);
} else if (!isValidThreeDInterval(roundTimeIntervalForFetch(graphState.queryTimeInterval))) {
} else if (!isValidThreeDInterval(roundTimeIntervalForFetch(queryTimeInterval))) {
// Not a valid time interval. ThreeD can only support up to 1 year of readings
layout = setHelpLayout(translate('threeD.date.range.too.long'));
} else if (!threeDData) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { selectSelectedLanguage } from '../../redux/slices/appStateSlice';
import { localEditsSlice } from '../../redux/slices/localEditsSlice';
import Locales from '../../types/locales';
import { CalibrationSettings } from '../../types/redux/map';
import { Dimensions, normalizeImageDimensions } from '../../utils/calibration';
import { CartesianPoint, Dimensions, normalizeImageDimensions } from '../../utils/calibration';

/**
* @returns TODO DO ME
Expand Down Expand Up @@ -93,8 +93,26 @@ export default function MapCalibrationChartDisplayContainer() {
locale: currentLanguange
}}
onClick={(event: PlotMouseEvent) => {
// trace 0 keeps a transparent trace of closely positioned points used for calibration(backgroundTrace),
// trace 1 keeps the data points used for calibration are automatically added to the same trace(dataPointTrace),
// event.points will include all points near a mouse click, including those in the backgroundTrace and the dataPointTrace,
// so the algorithm only looks at trace 0 since points from trace 1 are already put into the data set used for calibration.
event.event.preventDefault();
dispatch(localEditsSlice.actions.updateCurrentCartesian(event));
const eligiblePoints = [];
for (const point of event.points) {
const traceNumber = point.curveNumber;
if (traceNumber === 0) {
eligiblePoints.push(point);
}
}
// TODO VERIFY
const xValue = eligiblePoints[0].x as number;
const yValue = eligiblePoints[0].y as number;
const clickedPoint: CartesianPoint = {
x: Number(xValue.toFixed(6)),
y: Number(yValue.toFixed(6))
};
dispatch(localEditsSlice.actions.updateCurrentCartesian(clickedPoint));
}}
/>;
}
Expand Down
10 changes: 10 additions & 0 deletions src/client/app/redux/api/mapsApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { pick } from 'lodash';
import * as moment from 'moment';
import { MapDataState, mapsAdapter, mapsInitialState } from '../../redux/entityAdapters';
import { createAppSelector } from '../../redux/selectors/selectors';
import { setGraphSliceState } from '../../redux/slices/graphSlice';
import { emtpyMapMetadata, localEditsSlice } from '../../redux/slices/localEditsSlice';
import { RootState } from '../../store';
import { MapData, MapMetadata } from '../../types/redux/map';
Expand Down Expand Up @@ -125,12 +126,21 @@ export const mapsApi = baseApi.injectEndpoints({
body: { id }
}),
onQueryStarted: (arg, api) => {
const s = api.getState() as RootState;
api.queryFulfilled
//Cleanup Local Edits if any for deleted entity
.then(() => {
// set current to 0 if current selected is arg
const updatedCurrent = s.graph.current.selectedMap === arg ? { ...s.graph.current, selectedMap: 0 } : s.graph.current;
// filter entries with this id
const filteredPrev = s.graph.prev.filter(graphState => graphState.selectedMap === arg);
const filteredNext = s.graph.next.filter(graphState => graphState.selectedMap === arg);
api.dispatch(setGraphSliceState({ prev: filteredPrev, current: updatedCurrent, next: filteredNext }));
api.dispatch(localEditsSlice.actions.removeOneEdit(arg));
})
.catch();


},
invalidatesTags: ['MapsData']
}),
Expand Down
5 changes: 3 additions & 2 deletions src/client/app/redux/selectors/uiSelectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ import {
import { selectVisibleMetersAndGroups, selectVisibleUnitOrSuffixState } from './authVisibilitySelectors';
import { selectDefaultGraphicUnitFromEntity, selectMeterOrGroupFromEntity, selectNameFromEntity } from './entitySelectors';
import { createAppSelector } from './selectors';
import moment from 'moment';

export const selectCurrentUnitCompatibility = createAppSelector(
[
Expand Down Expand Up @@ -459,10 +460,10 @@ export const selectChartLink = createAppSelector(
}
linkText += `chartType=${current.chartToRender}`;
// weeklyLink = linkText + '&serverRange=7dfp'; // dfp: days from present;
linkText += `&serverRange=${current.queryTimeInterval.toString()}`;
linkText += `&serverRange=${current.queryTimeIntervalString.toString()}`;
switch (current.chartToRender) {
case ChartTypes.bar:
linkText += `&barDuration=${current.barDuration.asDays()}`;
linkText += `&barDuration=${moment.duration(current.barDuration).asDays()}`;
linkText += `&barStacking=${current.barStacking}`;
break;
case ChartTypes.line:
Expand Down
96 changes: 66 additions & 30 deletions src/client/app/redux/slices/graphSlice.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */

import { PayloadAction, createAction, createSlice } from '@reduxjs/toolkit';
import { PayloadAction, createAction, createSelector, createSlice } from '@reduxjs/toolkit';
import { cloneDeep } from 'lodash';
import * as moment from 'moment';
import { ActionMeta } from 'react-select';
Expand All @@ -21,12 +21,12 @@ const defaultState: GraphState = {
selectedAreaUnit: AreaUnitType.none,
// TODO appropriate default value?
selectedMap: 0,
queryTimeInterval: TimeInterval.unbounded(),
rangeSliderInterval: TimeInterval.unbounded(),
barDuration: moment.duration(4, 'weeks'),
mapsBarDuration: moment.duration(4, 'weeks'),
queryTimeIntervalString: TimeInterval.unbounded().toString(),
rangeSliderIntervalString: TimeInterval.unbounded().toString(),
barDuration: moment.duration(4, 'weeks').toISOString(),
mapsBarDuration: moment.duration(4, 'weeks').toISOString(),
compareTimeIntervalString: calculateCompareTimeInterval(ComparePeriod.Week, moment()).toString(),
comparePeriod: ComparePeriod.Week,
compareTimeInterval: calculateCompareTimeInterval(ComparePeriod.Week, moment()),
compareSortingOrder: SortingOrder.Descending,
chartToRender: ChartTypes.line,
barStacking: false,
Expand Down Expand Up @@ -83,29 +83,29 @@ export const graphSlice = createSlice({
state.current.selectedAreaUnit = action.payload;
},
updateBarDuration: (state, action: PayloadAction<moment.Duration>) => {
state.current.barDuration = action.payload;
state.current.barDuration = action.payload.toISOString();
},
updateMapsBarDuration: (state, action: PayloadAction<moment.Duration>) => {
state.current.mapsBarDuration = action.payload;
state.current.mapsBarDuration = action.payload.toISOString();
},
updateTimeInterval: (state, action: PayloadAction<TimeInterval>) => {
updateTimeInterval: (state, action: PayloadAction<string>) => {
// always update if action is bounded, else only set unbounded if current isn't already unbounded.
// clearing when already unbounded should be a no-op
if (action.payload.getIsBounded() || state.current.queryTimeInterval.getIsBounded()) {
state.current.queryTimeInterval = action.payload;
if (TimeInterval.fromString(action.payload).getIsBounded() || TimeInterval.fromString(state.current.queryTimeIntervalString).getIsBounded()) {
state.current.queryTimeIntervalString = action.payload.toString();
}
},
changeSliderRange: (state, action: PayloadAction<TimeInterval>) => {
if (action.payload.getIsBounded() || state.current.rangeSliderInterval.getIsBounded()) {
state.current.rangeSliderInterval = action.payload;
changeSliderRange: (state, action: PayloadAction<string>) => {
if (TimeInterval.fromString(action.payload).getIsBounded() || TimeInterval.fromString(state.current.rangeSliderIntervalString).getIsBounded()) {
state.current.rangeSliderIntervalString = action.payload.toString();
}
},
resetRangeSliderStack: state => {
state.current.rangeSliderInterval = TimeInterval.unbounded();
state.current.rangeSliderIntervalString = TimeInterval.unbounded().toString();
},
updateComparePeriod: (state, action: PayloadAction<{ comparePeriod: ComparePeriod, currentTime: moment.Moment }>) => {
state.current.comparePeriod = action.payload.comparePeriod;
state.current.compareTimeInterval = calculateCompareTimeInterval(action.payload.comparePeriod, action.payload.currentTime);
state.current.compareTimeIntervalString = calculateCompareTimeInterval(action.payload.comparePeriod, action.payload.currentTime).toString();
},
changeChartToRender: (state, action: PayloadAction<ChartTypes>) => {
state.current.chartToRender = action.payload;
Expand Down Expand Up @@ -234,8 +234,8 @@ export const graphSlice = createSlice({
}
},
resetTimeInterval: state => {
if (!state.current.queryTimeInterval.equals(TimeInterval.unbounded())) {
state.current.queryTimeInterval = TimeInterval.unbounded();
if (!TimeInterval.fromString(state.current.queryTimeIntervalString).equals(TimeInterval.unbounded())) {
state.current.queryTimeIntervalString = TimeInterval.unbounded().toString();
}
},
setGraphState: (state, action: PayloadAction<GraphState>) => {
Expand All @@ -247,6 +247,10 @@ export const graphSlice = createSlice({
// Current History Implementation tracks ANY action defined in 'reducers'
// To update graphState without causing a history entry to be created, utilize the 'Extra Reducers' property
builder
.addCase(
setGraphSliceState,
(_state, action) => action.payload
)
.addCase(
updateHistory,
(state, action) => {
Expand Down Expand Up @@ -285,7 +289,7 @@ export const graphSlice = createSlice({
.addCase(
updateSliderRange,
(state, { payload }) => {
state.current.rangeSliderInterval = payload;
state.current.rangeSliderIntervalString = payload;
}
)
.addCase(
Expand All @@ -304,7 +308,7 @@ export const graphSlice = createSlice({
current.selectedAreaUnit = value as AreaUnitType;
break;
case 'barDuration':
current.barDuration = moment.duration(parseInt(value), 'days');
current.barDuration = moment.duration(parseInt(value), 'days').toISOString();
break;
case 'barStacking':
current.barStacking = value === 'true';
Expand All @@ -315,7 +319,7 @@ export const graphSlice = createSlice({
case 'comparePeriod':
{
current.comparePeriod = validateComparePeriod(value);
current.compareTimeInterval = calculateCompareTimeInterval(validateComparePeriod(value), moment());
current.compareTimeIntervalString = calculateCompareTimeInterval(validateComparePeriod(value), moment()).toString();
}
break;
case 'compareSortingOrder':
Expand Down Expand Up @@ -347,7 +351,7 @@ export const graphSlice = createSlice({
current.threeD.readingInterval = parseInt(value);
break;
case 'serverRange':
current.queryTimeInterval = TimeInterval.fromString(value);
current.queryTimeIntervalString = value;
break;
case 'sliderRange':
// TODO omitted for now re-implement later.
Expand Down Expand Up @@ -387,8 +391,6 @@ export const graphSlice = createSlice({
selectThreeDState: state => state.current.threeD,
selectShowMinMax: state => state.current.showMinMax,
selectBarStacking: state => state.current.barStacking,
selectBarWidthDays: state => state.current.barDuration,
selectMapBarWidthDays: state => state.current.mapsBarDuration,
selectSelectedMap: state => state.current.selectedMap,
selectAreaUnit: state => state.current.selectedAreaUnit,
selectSelectedUnit: state => state.current.selectedUnit,
Expand All @@ -398,17 +400,50 @@ export const graphSlice = createSlice({
selectSelectedMeters: state => state.current.selectedMeters,
selectSelectedGroups: state => state.current.selectedGroups,
selectSortingOrder: state => state.current.compareSortingOrder,
selectQueryTimeInterval: state => state.current.queryTimeInterval,
selectThreeDMeterOrGroup: state => state.current.threeD.meterOrGroup,
selectCompareTimeInterval: state => state.current.compareTimeInterval,
selectGraphAreaNormalization: state => state.current.areaNormalization,
selectThreeDMeterOrGroupID: state => state.current.threeD.meterOrGroupID,
selectThreeDReadingInterval: state => state.current.threeD.readingInterval,
selectDefaultGraphState: () => defaultState,
selectHistoryIsDirty: state => state.prev.length > 0 || state.next.length > 0,
selectSliderRangeInterval: state => state.current.rangeSliderInterval,
selectPlotlySliderMin: state => state.current.rangeSliderInterval.getStartTimestamp()?.utc().toDate().toISOString(),
selectPlotlySliderMax: state => state.current.rangeSliderInterval.getEndTimestamp()?.utc().toDate().toISOString()
selectPlotlySliderMin: state => TimeInterval.fromString(state.current.rangeSliderIntervalString).getStartTimestamp()?.utc().toDate().toISOString(),
selectPlotlySliderMax: state => TimeInterval.fromString(state.current.rangeSliderIntervalString).getEndTimestamp()?.utc().toDate().toISOString(),
selectQueryTimeIntervalString: state => state.current.queryTimeIntervalString,
selectCompareTimeIntervalString: state => state.current.compareTimeIntervalString,
selectSliderRangeIntervalString: state => state.current.rangeSliderIntervalString,

// Memoized selector(s) becuase creating new TimeInterval.fromString(), each execution leads to unnecessary re-renders
// Avoids Saving Un-serializable objects (TimeIntervals) in store.
selectQueryTimeInterval: createSelector(
(sliceState: History<GraphState>) => sliceState.current.queryTimeIntervalString,
timeIntervalString => {
return TimeInterval.fromString(timeIntervalString);
}
),
selectSliderRangeInterval: createSelector(
(sliceState: History<GraphState>) => sliceState.current.rangeSliderIntervalString,
timeIntervalString => {
return TimeInterval.fromString(timeIntervalString);
}
),
selectCompareTimeInterval: createSelector(
(sliceState: History<GraphState>) => sliceState.current.compareTimeIntervalString,
timeIntervalString => {
return TimeInterval.fromString(timeIntervalString);
}
),
selectBarWidthDays: createSelector(
(sliceState: History<GraphState>) => sliceState.current.barDuration,
durationString => {
return moment.duration(durationString);
}
),
selectMapBarWidthDays: createSelector(
(sliceState: History<GraphState>) => sliceState.current.mapsBarDuration,
durationString => {
return moment.duration(durationString);
}
)
}
});

Expand Down Expand Up @@ -456,6 +491,7 @@ export const historyStepForward = createAction('graph/historyStepForward');
export const updateHistory = createAction<GraphState>('graph/updateHistory');
export const processGraphLink = createAction<URLSearchParams>('graph/graphLink');
export const clearGraphHistory = createAction('graph/clearHistory');
export const updateSliderRange = createAction<TimeInterval>('graph/UpdateSliderRange');
export const updateSliderRange = createAction<string>('graph/UpdateSliderRange');
export const setGraphSliceState = createAction<History<GraphState>>('graph/SetGraphSliceState');


Loading

0 comments on commit 04ba6a5

Please sign in to comment.