Redux refactor (#1571)

* refactor categorical controls state

* lint

* fix race condition in tests

* fix typo

* add missing update on subset

* remove obsolete code

* update jest and puppeteer major version; update all minors

* update when label changes

* remove lint from tests; increase timeouts in e2e tests

* initial refactoring to new async annomatrix

* refine error handling

* fix bad merge

* add continuous legend

* lint

* fix memoization in color table creators

* partial implementation of user defined annotations

* add new annotations action creator file

* first pass at user annotations

* additional user annotation bug fixes

* user annotation auto-save

* unit test cleanup

* lint

* refactor into multiple files

* cleanup

* add column GC

* fix several bugs in user annotations

* remove debug code

* no anonymous functions

* undo redo cleanup

* file cleanup

* scatterplot

* performance

* cleanup

* remove old code

* render in parallel with load

* fix race condition

* simply graph rendering

* render throttle DRY

* fix category label order

* fix typo in e2e test setup

* re-fix the e2e test setup

* be more tolerant of races

* anno matrix unit tests

* temp disable reembedding

* pilot port continuous histo to react-async

* name change

* lint

* fix repaint bug

* typo fix

* update snap to match new ids

* world/universe name cleanup

* move annoMatrix to src dir

* use private underscore naming convention

* fix corner case in all selected

* name cleanup

* add layout control

* init edge case

* lint

* port scatterplot

* fix label indexing bug and improve tests

* port category to react-async

* fix user annotation labelling while subset

* select all of prev layout on layout switch

* fix race with crossfilter update

* prettier lint

* fix misleading comment

* fix url composition in loader

* first pass at crossfilter tests

* lint

* lint

* fix typo

* improved error handling for network errors

* fix memoization bug

* add memo

* refactor for performnce

* add missing single-value handling in select exact parser

* small bugs discovered by tests

* lint

* additional crossfilter unit tests

* remove extraneous comment

* add support for automatic category determination

* lint

* fix render bug in category

* take advantage of schema categories guarantee

* lint

* do not clear history when resetting

* enhanced annomatrix gc

* lint

* finish renaming to follow conventions; fix clone race bug

* lint

* add priority based loading to improve initial data load UX

* crossfilter cache perf

* perf tuning

* remove timers

* documentation

* PR review changes

* PR review changes

* more PR review edits

* improve clarity of comment

* more PR review fixes

* port centroidLabels to use react-async

* remove dead code

* pr review updates

* oops, remove logging
This commit is contained in:
Bruce Martin
2020-07-14 13:53:33 -07:00
committed by GitHub
parent f69d141336
commit 1269e188be
95 changed files with 18051 additions and 5382 deletions
+211
View File
@@ -0,0 +1,211 @@
/*
Action creators for selection
*/
export const selectContinuousMetadataAction = (
type,
query,
range,
oldProps = {}
) => async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = range
? {
mode: "range",
lo: range[0],
hi: range[1],
inclusive: true, // [lo, hi] incluisve selection
}
: { mode: "all" };
const obsCrossfilter = await prevObsCrossfilter.select(...query, selection);
dispatch({
type,
obsCrossfilter,
range,
...oldProps,
});
};
export const selectCategoricalMetadataAction = (
type, // action type
metadataField, // annotation category name
labels,
label, // the label being selected/deselected
isSelected, // bool
oldProps = {}
) => async (dispatch, getState) => {
const {
obsCrossfilter: prevObsCrossfilter,
categoricalSelection,
} = getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
labels.forEach(
(l) => labelSelectionState.has(l) || labelSelectionState.set(l, true)
);
labelSelectionState.set(label, isSelected);
const values = Array.from(labelSelectionState.keys()).filter((k) =>
labelSelectionState.get(k)
);
const selection = {
mode: "exact",
values,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
export const selectCategoricalAllMetadataAction = (
type, // action type
metadataField, // annotation category name
labels,
isSelected, // bool, select all or none
oldProps = {}
) => async (dispatch, getState) => {
const {
obsCrossfilter: prevObsCrossfilter,
categoricalSelection,
} = getState();
const labelSelectionState = new Map(categoricalSelection[metadataField]);
labels.forEach((label) => labelSelectionState.set(label, isSelected));
const selection = { mode: isSelected ? "all" : "none" };
const obsCrossfilter = await prevObsCrossfilter.select(
"obs",
metadataField,
selection
);
dispatch({
type,
obsCrossfilter,
metadataField,
labelSelectionState,
...oldProps,
});
};
/**
** Graph selection-related actions
**/
export const graphBrushStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph brush start" });
const _graphBrushWithinRectAction = (type, embName, brushCoords) => async (
dispatch,
getState
) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = { mode: "within-rect", ...brushCoords };
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type,
obsCrossfilter,
brushCoords,
});
};
const _graphAllAction = (type, embName) => async (dispatch, getState) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const obsCrossfilter = await prevObsCrossfilter.select("emb", embName, {
mode: "all",
});
dispatch({
type,
obsCrossfilter,
});
};
export const graphBrushChangeAction = (embName, brushCoords) =>
_graphBrushWithinRectAction("graph brush change", embName, brushCoords);
export const graphBrushEndAction = (embName, brushCoords) =>
_graphBrushWithinRectAction("graph brush end", embName, brushCoords);
export const graphBrushCancelAction = (embName) =>
_graphAllAction("graph brush cancel", embName);
export const graphBrushDeselectAction = (embName) =>
_graphAllAction("graph brush deselect", embName);
export const graphLassoStartAction = () =>
/* no change to crossfilter until a change fires */
({ type: "graph lasso start" });
export const graphLassoCancelAction = (embName) =>
_graphAllAction("graph lasso cancel", embName);
export const graphLassoDeselectAction = (embName) =>
_graphAllAction("graph lasso cancel", embName);
export const graphLassoEndAction = (embName, polygon) => async (
dispatch,
getState
) => {
const { obsCrossfilter: prevObsCrossfilter } = getState();
const selection = {
mode: "within-polygon",
polygon,
};
const obsCrossfilter = await prevObsCrossfilter.select(
"emb",
embName,
selection
);
dispatch({
type: "graph lasso end",
obsCrossfilter,
polygon,
});
};
export const layoutChoiceAction = (newLayoutChoice) => async (
dispatch,
getState
) => {
/*
On layout choice, make sure we have selected all on the previous layout, AND the new
layout.
*/
const { obsCrossfilter: prevObsCrossfilter, layoutChoice } = getState();
let obsCrossfilter = await prevObsCrossfilter.select(
"emb",
layoutChoice.current,
{ mode: "all" }
);
obsCrossfilter = await obsCrossfilter.select("emb", newLayoutChoice, {
mode: "all",
});
dispatch({
type: "set layout choice",
layoutChoice: newLayoutChoice,
obsCrossfilter,
});
};