Skip to content

Commit

Permalink
- #1419 first batch of fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
boriskovar-m2ms committed Jun 17, 2024
1 parent 978bb88 commit 71f075f
Show file tree
Hide file tree
Showing 11 changed files with 246 additions and 7 deletions.
1 change: 0 additions & 1 deletion js/components/preview/compounds/redux/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,4 +42,3 @@ export const compoundsColors = {
};

export const AUX_VECTOR_SELECTOR_DATASET_ID = 'vector_selector';

1 change: 1 addition & 0 deletions js/components/preview/molecule/moleculeList.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@ import GroupNglControlButtonsContext from './groupNglControlButtonsContext';
import { extractTargetFromURLParam } from '../utils';
import { LoadingContext } from '../../loading';
import { DJANGO_CONTEXT } from '../../../utils/djangoContext';
import { useScrollToSelectedPose } from './useScrollToSelectedPose';

const useStyles = makeStyles(theme => ({
container: {
Expand Down
7 changes: 7 additions & 0 deletions js/components/preview/molecule/observationCmpList.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ import { LoadingContext } from '../../loading';
import { DJANGO_CONTEXT } from '../../../utils/djangoContext';
import ObservationCmpView from './observationCmpView';
import { ObservationsDialog } from './observationsDialog';
import { useScrollToSelectedPose } from './useScrollToSelectedPose';

const useStyles = makeStyles(theme => ({
container: {
Expand Down Expand Up @@ -331,6 +332,11 @@ export const ObservationCmpList = memo(({ hideProjects }) => {
target = directDisplay.target;
}

const { addMoleculeViewRef, setScrollToMoleculeId, getNode } = useScrollToSelectedPose(
moleculesPerPage,
setCurrentPage
);

let selectedMolecule = [];
// TODO: Reset Infinity scroll
/*useEffect(() => {
Expand Down Expand Up @@ -1220,6 +1226,7 @@ export const ObservationCmpList = memo(({ hideProjects }) => {

return (
<ObservationCmpView
ref={addMoleculeViewRef}
key={data.id}
imageHeight={imgHeight}
imageWidth={imgWidth}
Expand Down
142 changes: 142 additions & 0 deletions js/components/preview/molecule/useScrollToSelectedPose.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,142 @@
import { useCallback, useEffect, useState } from 'react';
import { useDispatch, useSelector, shallowEqual } from 'react-redux';
import { setScrollFiredForLHS } from '../../../reducers/selection/actions';
import { getLHSCompoundsList } from './redux/selectors';

/**
* A hook which scrolls to the first selected pose when a snapshot is loaded.
*/
export const useScrollToSelectedPose = (moleculesPerPage, setCurrentPage) => {
const dispatch = useDispatch();

const poses = useSelector(state => getLHSCompoundsList(state), shallowEqual);
const ligands = useSelector(state => state.selectionReducers.fragmentDisplayList);
const proteins = useSelector(state => state.selectionReducers.proteinList);
const complexes = useSelector(state => state.selectionReducers.complexList);
const surfaces = useSelector(state => state.selectionReducers.surfaceList);
const densityList = useSelector(state => state.selectionReducers.densityList);
const densityListCustom = useSelector(state => state.selectionReducers.densityListCustom);
const vectorOnList = useSelector(state => state.selectionReducers.vectorOnList);

const scrollFired = useSelector(state => state.selectionReducers.isScrollFiredForLHS);

const [moleculeViewRefs, setMoleculeViewRefs] = useState({});
const [scrollToMoleculeId, setScrollToMoleculeId] = useState(null);

// First pass, iterates over all the molecules and checks if any of them is selected. If it is,
// it saves the ID of the molecule and determines how many pages of molecules should be displayed.
// This is done only once and only if right hand side is open.
// This also gets reset on snapshot change.
useEffect(() => {
if (!scrollFired) {
if (
ligands?.length ||
proteins?.length ||
complexes?.length ||
surfaces?.length ||
densityList?.length ||
densityListCustom?.length ||
vectorOnList?.length
) {
for (let i = 0; i < poses.length; i++) {
const pose = poses[i];
const molsForCmp = pose.associatedObs;

if (
containsAtLeastOne(ligands, molsForCmp) ||
containsAtLeastOne(proteins, molsForCmp) ||
containsAtLeastOne(complexes, molsForCmp) ||
containsAtLeastOne(surfaces, molsForCmp) ||
containsAtLeastOne(densityList, molsForCmp) ||
containsAtLeastOne(densityListCustom, molsForCmp) ||
containsAtLeastOne(vectorOnList, molsForCmp)
) {
setCurrentPage(i / moleculesPerPage + 1);
setScrollToMoleculeId(pose.id);
break;
}
}
}
}

dispatch(setScrollFiredForLHS(true));
}, [
dispatch,
poses,
moleculesPerPage,
scrollFired,
setCurrentPage,
ligands,
proteins,
complexes,
surfaces,
densityList.length,
densityListCustom.length,
densityList,
densityListCustom,
vectorOnList
]);

// Second pass, once the list of molecules is displayed and the refs to their DOM nodes have been
// obtained, scroll to the the saved molecule from the first pass.
// setTimeout might be necessary for the scrolling to happen.
useEffect(() => {
if (scrollToMoleculeId !== null) {
const node = moleculeViewRefs[scrollToMoleculeId];
if (node) {
setScrollToMoleculeId(null);
if (!elementIsVisibleInViewport(node)) {
setTimeout(() => {
node.scrollIntoView();
});
}
}
}
}, [moleculeViewRefs, scrollToMoleculeId]);

const elementIsVisibleInViewport = (el, partiallyVisible = false) => {
const { top, left, bottom, right } = el.getBoundingClientRect();
const { innerHeight, innerWidth } = window;
return partiallyVisible
? ((top > 0 && top < innerHeight) || (bottom > 0 && bottom < innerHeight)) &&
((left > 0 && left < innerWidth) || (right > 0 && right < innerWidth))
: top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth;
};

// Used to attach the ref of DOM nodes.
// const addMoleculeViewRef = useCallback((moleculeId, node) => {
// setMoleculeViewRefs(prevRefs => ({
// ...prevRefs,
// [moleculeId]: node
// }));
// }, []);

const addMoleculeViewRef = useCallback((moleculeId, node) => {
setMoleculeViewRefs(prevRefs => {
if (prevRefs.hasOwnProperty(moleculeId)) return prevRefs;
return {
...prevRefs,
[moleculeId]: node
};
});
}, []);

const getNode = useCallback(
molId => {
return moleculeViewRefs[molId];
},
[moleculeViewRefs]
);

const containsAtLeastOne = (list, molsList) => {
for (const mol in molsList) {
if (list.includes(mol.id)) {
return true;
}
}

return false;
};

return { addMoleculeViewRef, setScrollToMoleculeId, getNode };
};
7 changes: 7 additions & 0 deletions js/reducers/selection/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -560,3 +560,10 @@ export const setActualRhsWidth = rhsWidth => {
payload: rhsWidth
};
};

export const setScrollFiredForLHS = isFired => {
return {
type: constants.SET_SCROLL_FIRED_FOR_LHS,
isFired: isFired
};
};
4 changes: 3 additions & 1 deletion js/reducers/selection/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,9 @@ export const constants = {

SET_RHS_WIDTH: prefix + 'SET_RHS_WIDTH',

SET_LHS_COMPOUNDS_INITIALIZED: prefix + 'SET_LHS_COMPOUNDS_INITIALIZED'
SET_LHS_COMPOUNDS_INITIALIZED: prefix + 'SET_LHS_COMPOUNDS_INITIALIZED',

SET_SCROLL_FIRED_FOR_LHS: prefix + 'SET_SCROLL_FIRED_FOR_LHS'
};

export const PREDEFINED_FILTERS = {
Expand Down
6 changes: 5 additions & 1 deletion js/reducers/selection/selectionReducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,11 +53,15 @@ export const INITIAL_STATE = {
observationsForLHSCmp: [],
poseIdForObservationsDialog: 0,

areLSHCompoundsInitialized: false
areLSHCompoundsInitialized: false,
isScrollFiredForLHS: false
};

export function selectionReducers(state = INITIAL_STATE, action = {}) {
switch (action.type) {
case constants.SET_SCROLL_FIRED_FOR_LHS: {
return { ...state, isScrollFiredForLHS: action.isFired };
}
case constants.SET_TO_BUY_LIST:
return Object.assign({}, state, {
to_buy_list: action.to_buy_list
Expand Down
2 changes: 2 additions & 0 deletions js/reducers/tracking/constants.js
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ export const actionType = {
QUALITY_TURNED_OFF: 'QUALITY_TURNED_OFF',
VECTORS_TURNED_ON: 'VECTORS_TURNED_ON',
VECTORS_TURNED_OFF: 'VECTORS_TURNED_OFF',
COLOR_FILTER_TURNED_ON: 'COLOR_FILTER_TURNED_ON',
COLOR_FILTER_TURNED_OFF: 'COLOR_FILTER_TURNED_OFF',
CLASS_SELECTED: 'CLASS_SELECTED',
CLASS_UPDATED: 'CLASS_UPDATED',
VECTOR_SELECTED: 'VECTOR_SELECTED',
Expand Down
48 changes: 45 additions & 3 deletions js/reducers/tracking/dispatchActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ import {
appendToMolListToEdit,
removeFromMolListToEdit,
setNextXMolecules,
setTagDetailView
setTagDetailView,
setScrollFiredForLHS
} from '../selection/actions';
import {
resetReducersForRestoringActions,
Expand Down Expand Up @@ -95,7 +96,9 @@ import {
appendCompoundColorOfDataset,
removeCompoundColorOfDataset,
setCompoundToSelectedCompoundsByDataset,
setSelectAllButtonForDataset
setSelectAllButtonForDataset,
appendColorToSelectedColorFilter,
removeColorFromSelectedColorFilter
} from '../../components/datasets/redux/actions';
import {
removeComponentRepresentation,
Expand Down Expand Up @@ -480,7 +483,7 @@ const saveActionsList = (project, snapshot, actionList, nglViewList, isAnonymous
getCommonLastActionByType(orderedActionList, actionType.DATASET_INDEX, currentActions);
getCommonLastActionByType(orderedActionList, actionType.DATASET_FILTER, currentActions);
getCommonLastActionByType(orderedActionList, actionType.DATASET_FILTER_SCORE, currentActions);
getCommonLastActionByType(orderedActionList, actionType.CLASS_SELECTED, currentActions);
getLastColorFilterActions(orderedActionList, actionType.COLOR_FILTER_TURNED_OFF, currentActions);
getCommonLastActionByType(orderedActionList, actionType.CLASS_UPDATED, currentActions);
getCommonLastActionByType(orderedActionList, actionType.ISO_LEVEL_EVENT, currentActions);
getCommonLastActionByType(orderedActionList, actionType.BOX_SIZE_EVENT, currentActions);
Expand Down Expand Up @@ -658,6 +661,16 @@ const getCurrentActionListDensity = (orderedActionList, type, collection, curren
}
};

const getLastColorFilterActions = (orderedActionList, type, currentActions) => {
let actionList = orderedActionList.filter(action => action.type === type);
Object.keys(compoundsColors).forEach(color => {
let action = actionList.find(action => action.value === color);
if (action) {
currentActions.push({ ...action });
}
});
};

const getCommonLastActionByType = (orderedActionList, type, currentActions) => {
let action = orderedActionList.find(action => action.type === type);
if (action) {
Expand Down Expand Up @@ -1035,6 +1048,7 @@ export const restoreAfterTargetActions = (stages, projectId, snapshotId) => asyn
dispatch(setIsActionsRestoring(false, true));
dispatch(restoreViewerControlActions(orderedActionList));
dispatch(resetDatasetScrolledMap()); // Have a look at useScrollToSelected.js
dispatch(setScrollFiredForLHS(false));
dispatch(setIsSnapshotDirty(false));
dispatch(restoreSearchString(orderedActionList));
dispatch(restoreSearchStringHitNavigator(orderedActionList));
Expand Down Expand Up @@ -1553,6 +1567,11 @@ export const restoreCartActions = (orderedActionList, majorViewStage) => async (
dispatch(setCompoundClasses(newValue, oldValue, value, id));
}

let colorFilterRemoveActions = orderedActionList.filter(action => action.type === actionType.COLOR_FILTER_TURNED_OFF);
colorFilterRemoveActions?.forEach(action => {
dispatch(handleColorFilterAction(action, false));
});

let vectorCompoundActions = orderedActionList.filter(action => action.type === actionType.VECTOR_COUMPOUND_ADDED);
if (vectorCompoundActions) {
vectorCompoundActions.forEach(action => {
Expand Down Expand Up @@ -2360,6 +2379,12 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.CLASS_UPDATED:
dispatch(handleClassUpdatedAction(action, false));
break;
case actionType.COLOR_FILTER_TURNED_ON:
dispatch(handleColorFilterAction(action, false));
break;
case actionType.COLOR_FILTER_TURNED_OFF:
dispatch(handleColorFilterAction(action, true));
break;
case actionType.TARGET_LOADED:
dispatch(handleTargetAction(action, false));
break;
Expand Down Expand Up @@ -2635,6 +2660,12 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.CLASS_UPDATED:
dispatch(handleClassUpdatedAction(action, true));
break;
case actionType.COLOR_FILTER_TURNED_ON:
dispatch(handleColorFilterAction(action, true));
break;
case actionType.COLOR_FILTER_TURNED_OFF:
dispatch(handleColorFilterAction(action, false));
break;
case actionType.TARGET_LOADED:
dispatch(handleTargetAction(action, true));
break;
Expand Down Expand Up @@ -3039,6 +3070,17 @@ const handleClassUpdatedAction = (action, isAdd) => (dispatch, getState) => {
}
};

const handleColorFilterAction = (action, isSelected) => (dispatch, getState) => {
if (action) {
const color = action.value;
if (isSelected) {
dispatch(appendColorToSelectedColorFilter(color));
} else {
dispatch(removeColorFromSelectedColorFilter(color));
}
}
};

const handleTargetAction = (action, isSelected, stages) => (dispatch, getState) => {
const state = getState();
if (action) {
Expand Down
3 changes: 2 additions & 1 deletion js/reducers/tracking/dispatchActionsSwitchSnapshot.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {
setIsSnapshotDirty,
setSnapshotActionsDownloaded
} from './actions';
import { resetSelectionState } from '../selection/actions';
import { resetSelectionState, setScrollFiredForLHS } from '../selection/actions';
import { resetDatasetsStateOnSnapshotChange, resetDatasetScrolledMap } from '../../components/datasets/redux/actions';
import { resetViewerControlsState } from '../../components/preview/viewerControls/redux/actions';
import { resetNglTrackingState } from '../nglTracking/dispatchActions';
Expand Down Expand Up @@ -132,6 +132,7 @@ export const restoreAfterSnapshotChange = (stages, projectId) => async (dispatch
// console.count(`AFTER restoration orientation from snapshot`);

dispatch(resetDatasetScrolledMap()); // Have a look at useScrollToSelected.js
dispatch(setScrollFiredForLHS(false));
dispatch(setIsActionsRestoring(false, true));

console.count(`restoreAfterSnapshotChange end`);
Expand Down
32 changes: 32 additions & 0 deletions js/reducers/tracking/trackingActions.js
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,38 @@ export const findTrackAction = (action, state) => (dispatch, getState) => {
text: `${actionDescription.VECTOR} ${objectName} ${actionDescription.SELECTED}`
};
}
} else if (action.type === customDatasetConstants.APPEND_COLOR_TO_SELECTED_COLOR_FILTERS) {
if (action.payload) {
let objectType = actionObjectType.COMPOUND;

trackAction = {
type: actionType.COLOR_FILTER_TURNED_ON,
annotation: actionAnnotation.CHECK,
timestamp: Date.now(),
username: username,
object_type: objectType,
object_name: 'color_filter',
object_id: 'color_filter',
value: action.payload.colorClass,
text: `${actionDescription.CLASS} filter turned on for color: ${action.payload.colorClass} `
};
}
} else if (action.type === customDatasetConstants.REMOVE_COLOR_FROM_SELECTED_COLOR_FILTERS) {
if (action.payload) {
let objectType = actionObjectType.COMPOUND;

trackAction = {
type: actionType.COLOR_FILTER_TURNED_OFF,
annotation: actionAnnotation.CLEAR,
timestamp: Date.now(),
username: username,
object_type: objectType,
object_name: 'color_filter',
object_id: 'color_filter',
value: action.payload.colorClass,
text: `${actionDescription.CLASS} filter turned off for color: ${action.payload.colorClass} `
};
}
} else if (action.type === previewCompoundConstants.SET_CURRENT_COMPOUND_CLASS) {
if (action.payload) {
let objectType = actionObjectType.COMPOUND;
Expand Down

0 comments on commit 71f075f

Please sign in to comment.