{
- const result = e.target.checked;
- if (result) {
- if (observations?.length > 0) {
- const mainObs = getMainObservation();
- mainObs && dispatch(appendToMolListToEdit(mainObs.id));
- }
- // dispatch(appendToObsCmpListToEdit(currentID));
- } else {
- observations?.forEach(obs => {
- dispatch(removeFromMolListToEdit(obs.id));
- });
- // dispatch(removeFromObsCmpListToEdit(currentID));
+ const onVector = () => {
+ setLoadingVector(true);
+ if (isVectorOn === false) {
+ addNewVector();
+ } else {
+ removeSelectedVector();
+ }
+ setLoadingVector(false);
+ };
+
+ const setCalledFromAll = () => {
+ let isSelected = selectedAll.current === true;
+ if (isSelected) {
+ dispatch(setSelectedAll(data, true, true, true));
+ } else {
+ dispatch(setDeselectedAll(data, isLigandOn, isProteinOn, isComplexOn));
+ }
+ };
+
+ /**
+ * Check if given molecule is matching current filter
+ * @param Object item - item.name is attribute name, item.value is its value
+ * @return boolean
+ */
+ const isMatchingValue = item => {
+ let match = false;
+ if (!(item.value < filter.filter[item.name].minValue || item.value > filter.filter[item.name].maxValue)) {
+ match = true;
+ }
+ return match;
+ };
+
+ /**
+ * Get css class for value regarding to its filter match
+ * @param Object item - item.name is attribute name, item.value is its value
+ * @return string - css class
+ */
+ const getValueMatchingClass = item => {
+ let cssClass = '';
+ if (filter && filter.predefined !== 'none') {
+ cssClass = isMatchingValue(item) ? classes.matchingValue : classes.unmatchingValue;
+ }
+ return cssClass;
+ };
+
+ const scrollToElement = element => {
+ element.scrollIntoView({
+ behavior: 'auto',
+ block: 'nearest',
+ inline: 'nearest'
+ });
+ };
+
+ // let moleculeTitle = data?.code.replace(new RegExp(`${target_on_name}-`, 'i'), '');
+ // let moleculeTitle = data.code;
+ let moleculeTitle = data?.code.replaceAll(`${target_on_name}-`, '');
+ if (observations?.length > 0 && observations[0].compound_code) {
+ moleculeTitle += ` - ${observations[0].compound_code}`;
+ }
+ const moleculeTitleTruncated = moleculeTitle.substring(0, 20) + (moleculeTitle.length > 20 ? '...' : '');
+
+ const [isNameCopied, setNameCopied] = useClipboard(moleculeTitle, { successDuration: 5000 });
+
+ const moleculeLPCControlButtonDisabled = ['ligand', 'protein', 'complex'].some(
+ type => disableMoleculeNglControlButtons[type]
+ );
+
+ const groupMoleculeLPCControlButtonDisabled = disableL || disableP || disableC;
+
+ return (
+ <>
+
+ {/* Site number */}
+
+
+ {
+ const result = e.target.checked;
+ if (result) {
+ if (observations?.length > 0) {
+ const mainObs = getMainObservation();
+ mainObs && dispatch(appendToMolListToEdit(mainObs.id));
}
- }}
- />
-
-
- {index + 1}.
-
+ // dispatch(appendToObsCmpListToEdit(currentID));
+ } else {
+ observations?.forEach(obs => {
+ dispatch(removeFromMolListToEdit(obs.id));
+ });
+ // dispatch(removeFromObsCmpListToEdit(currentID));
+ }
+ }}
+ />
-
- {/* Title label */}
-
- {
- e.preventDefault();
- setNameCopied(moleculeTitle);
- }}
- className={classes.moleculeTitleLabel}
- >
- {data?.code.replaceAll(`${target_on_name}-`, '')}
-
- {data?.main_site_observation_cmpd_code}
-
-
- {/* "Filtered"/calculated props
+
+ {index + 1}.
+
+
+
+ {/* Title label */}
+
+ {
+ e.preventDefault();
+ setNameCopied(moleculeTitle);
+ }}
+ className={classes.moleculeTitleLabel}
+ >
+ {data?.code.replaceAll(`${target_on_name}-`, '')}
+
+ {data?.main_site_observation_cmpd_code}
+
+
+ {/* "Filtered"/calculated props
*/}
-
- {/* Tags */}
-
- {/* Control Buttons A, L, C, V */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- {/* C stands for contacts now */}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+ {/* Tags */}
+
+ {/* Control Buttons A, L, C, V */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {/* C stands for contacts now */}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
- {generateTagPopover()}
- {/* Image */}
- setMoleculeTooltipOpen(true)}
- onMouseLeave={() => setMoleculeTooltipOpen(false)}
- ref={moleculeImgRef}
- >
- {svg_image}
-
- {moleculeTooltipOpen && (
-
-
- {!isCopied ? : }
-
-
- )}
- {warningIconVisible && (
-
- onQuality()}>
-
-
-
- )}
-
+ {generateTagPopover()}
+
+ {/* Image */}
+
setMoleculeTooltipOpen(true)}
+ onMouseLeave={() => setMoleculeTooltipOpen(false)}
+ ref={moleculeImgRef}
+ >
+ {svg_image}
+
+ {moleculeTooltipOpen && (
+
+
+ {!isCopied ? : }
+
+
+ )}
+ {warningIconVisible && (
+
+ onQuality()}>
+
+
+
+ )}
+
- {/* CanonSites */}
-
+ {/* CanonSites */}
+
+ CanonSites - {getCanonSitesTag().upload_name} }
+ >
+
+ {getCanonSitesTag().tag_prefix}
+
+
+
+ {/* ConformerSites */}
+
+ {getConformerSites().map((conformerSite, i, sites) => (
CanonSites - {getCanonSitesTag().upload_name}}
+ key={conformerSite.id + i}
+ title={ConformerSites - {conformerSite.upload_name}
}
>
= 3
+ })}
style={{
- backgroundColor: resolveTagBackgroundColor(getCanonSitesTag()),
- color: resolveTagForegroundColor(getCanonSitesTag())
+ backgroundColor: resolveTagBackgroundColor(conformerSite),
+ color: resolveTagForegroundColor(conformerSite),
+ borderBottom: i === sites.length - 1 ? 1 : 0,
+ borderRight: 0
}}
>
- {getCanonSitesTag().tag_prefix}
+ {conformerSite.tag_prefix.replace(getCanonSitesTag().tag_prefix, '')}
-
- {/* ConformerSites */}
-
- {getConformerSites().map((conformerSite, i, sites) => (
- ConformerSites - {conformerSite.upload_name}}
+ ))}
+
+ {/* Observations*/}
+
+
+ Show Observations}>
+
- ))}
-
- {/* Observations*/}
-
-
- Show Observations}>
-
-
-
+ {observations?.length}
+
+
-
-
- >
- );
- }
- )
+
+
+
+ >
+ );
+ }
);
ObservationCmpView.displayName = 'ObservationCmpView';
diff --git a/js/components/preview/molecule/observationsDialog.js b/js/components/preview/molecule/observationsDialog.js
index a2c7c01f1..86607750d 100644
--- a/js/components/preview/molecule/observationsDialog.js
+++ b/js/components/preview/molecule/observationsDialog.js
@@ -30,7 +30,10 @@ import {
import { colourList } from './utils/color';
import {
setDeselectedAllByType,
+ setObservationDialogAction,
+ setObservationsForLHSCmp,
setOpenObservationsDialog,
+ setPoseIdForObservationsDialog,
setSelectedAllByType,
setTagEditorOpenObs
} from '../../../reducers/selection/actions';
@@ -179,6 +182,7 @@ export const ObservationsDialog = memo(
state => state.datasetsReducers.isLoadingInspirationListOfMolecules
);
const observationsDataList = useSelector(state => state.selectionReducers.observationsForLHSCmp);
+ const poseIdForDialog = useSelector(state => state.selectionReducers.poseIdForObservationsDialog);
const ligandList = useSelector(state => state.selectionReducers.fragmentDisplayList);
const proteinList = useSelector(state => state.selectionReducers.proteinList);
@@ -460,6 +464,10 @@ export const ObservationsDialog = memo(
} else {
if (sourcePose.site_observations.length === 1) {
dispatch(setOpenObservationsDialog(false));
+ dispatch(setObservationsForLHSCmp([]));
+ dispatch(setPoseIdForObservationsDialog(0));
+
+ // dispatch(setObservationDialogAction(0, [], false));
}
sourcePose = dispatch(removeObservationsFromPose(sourcePose, moleculesToEditIds));
destinationPose = await dispatch(addObservationsToPose(destinationPose, moleculesToEditIds));
@@ -515,7 +523,13 @@ export const ObservationsDialog = memo(
dispatch(setOpenObservationsDialog(false))}
+ onClick={() => {
+ dispatch(setOpenObservationsDialog(false));
+ dispatch(setObservationsForLHSCmp([]));
+ dispatch(setPoseIdForObservationsDialog(0));
+
+ dispatch(setObservationDialogAction(0, [], false, poseIdForDialog, observationsDataList));
+ }}
>
diff --git a/js/components/preview/molecule/redux/dispatchActions.js b/js/components/preview/molecule/redux/dispatchActions.js
index a01597465..8a41f73a9 100644
--- a/js/components/preview/molecule/redux/dispatchActions.js
+++ b/js/components/preview/molecule/redux/dispatchActions.js
@@ -32,7 +32,9 @@ import {
setSelectAllMolecules,
setUnselectAllMolecules,
setTagEditorOpen,
- setMoleculeForTagEdit
+ setMoleculeForTagEdit,
+ setSelectVisiblePoses,
+ setUnselectVisiblePoses
} from '../../../../reducers/selection/actions';
import { base_url } from '../../../routes/constants';
import {
@@ -1258,6 +1260,20 @@ export const selectAllHits = (allFilteredLhsCompounds, setNextXMolecules, unsele
}
};
+export const selectAllVisibleObservations = (visibleObservations, setNextXMolecules, unselect) => (dispatch, getState) => {
+ if (setNextXMolecules) {
+ dispatch(setNextXMolecules(visibleObservations?.length || 0));
+ }
+ const listOfIds = visibleObservations.map(o => o.id);
+ if (!unselect) {
+ dispatch(setMolListToEdit(listOfIds));
+ dispatch(setSelectVisiblePoses(visibleObservations));
+ } else {
+ dispatch(setMolListToEdit([]));
+ dispatch(setUnselectVisiblePoses(visibleObservations));
+ }
+};
+
export const getAllCompatiblePoses = (compoundId, canonSite, poseToSkip = 0) => (dispatch, getState) => {
let result = [];
const state = getState();
diff --git a/js/components/preview/molecule/useScrollToSelectedPose.js b/js/components/preview/molecule/useScrollToSelectedPose.js
index 703b0445e..3d315e7a4 100644
--- a/js/components/preview/molecule/useScrollToSelectedPose.js
+++ b/js/components/preview/molecule/useScrollToSelectedPose.js
@@ -18,6 +18,10 @@ export const useScrollToSelectedPose = (moleculesPerPage, setCurrentPage) => {
const densityListCustom = useSelector(state => state.selectionReducers.densityListCustom);
const vectorOnList = useSelector(state => state.selectionReducers.vectorOnList);
+ const isObservationsDialogOpen = useSelector(state => state.selectionReducers.isObservationDialogOpen);
+
+ const poseIdForObservationsDialog = useSelector(state => state.selectionReducers.poseIdForObservationsDialog);
+
const scrollFired = useSelector(state => state.selectionReducers.isScrollFiredForLHS);
const [moleculeViewRefs, setMoleculeViewRefs] = useState({});
@@ -25,11 +29,20 @@ export const useScrollToSelectedPose = (moleculesPerPage, setCurrentPage) => {
// 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 is done only once.
// This also gets reset on snapshot change.
useEffect(() => {
if (!scrollFired) {
- if (
+ if (isObservationsDialogOpen && poseIdForObservationsDialog) {
+ for (let i = 0; i < poses.length; i++) {
+ const pose = poses[i];
+ if (pose.id === poseIdForObservationsDialog) {
+ setCurrentPage(i / moleculesPerPage + 1);
+ setScrollToMoleculeId(poseIdForObservationsDialog);
+ break;
+ }
+ }
+ } else if (
ligands?.length ||
proteins?.length ||
complexes?.length ||
@@ -74,7 +87,9 @@ export const useScrollToSelectedPose = (moleculesPerPage, setCurrentPage) => {
densityListCustom.length,
densityList,
densityListCustom,
- vectorOnList
+ vectorOnList,
+ poseIdForObservationsDialog,
+ isObservationsDialogOpen
]);
// Second pass, once the list of molecules is displayed and the refs to their DOM nodes have been
@@ -113,7 +128,7 @@ export const useScrollToSelectedPose = (moleculesPerPage, setCurrentPage) => {
const addMoleculeViewRef = useCallback((moleculeId, node) => {
setMoleculeViewRefs(prevRefs => {
- if (prevRefs.hasOwnProperty(moleculeId)) return prevRefs;
+ if (prevRefs.hasOwnProperty(moleculeId) || !node) return prevRefs;
return {
...prevRefs,
[moleculeId]: node
diff --git a/js/components/preview/tags/details/tagDetails.js b/js/components/preview/tags/details/tagDetails.js
index a68e59f5a..b7778189b 100644
--- a/js/components/preview/tags/details/tagDetails.js
+++ b/js/components/preview/tags/details/tagDetails.js
@@ -168,14 +168,25 @@ const TagDetails = memo(() => {
let tagDetailView = useSelector(state => state.selectionReducers.tagDetailView);
const resizableLayout = useSelector(state => state.selectionReducers.resizableLayout);
const tagCategories = useSelector(state => state.apiReducers.categoryList);
+ const selectedTagList = useSelector(state => state.selectionReducers.selectedTagList);
const [tagList, setTagList] = useState([]);
const [selectAll, setSelectAll] = useState(true);
const [showEditTagsModal, setShowEditTagsModal] = useState(false);
const [searchString, setSearchString] = useState(null);
+ const [allTagsAreSelected, setAllTagsAreSelected] = useState(false);
+
tagDetailView = tagDetailView?.tagDetailView === undefined ? tagDetailView : tagDetailView.tagDetailView;
+ useEffect(() => {
+ if (tagList.length === selectedTagList.length) {
+ setAllTagsAreSelected(true);
+ } else {
+ setAllTagsAreSelected(false);
+ }
+ }, [tagList, selectedTagList]);
+
const filteredTagList = useMemo(() => {
if (searchString) {
return tagList.filter(tag => tag.tag.toLowerCase().includes(searchString.toLowerCase()));
@@ -201,8 +212,8 @@ const TagDetails = memo(() => {
const moleculesToEditIds = useSelector(state => state.selectionReducers.moleculesToEdit);
const moleculesToEdit =
moleculesToEditIds &&
- moleculesToEditIds.length > 0 &&
- !(moleculesToEditIds.length === 1 && moleculesToEditIds[0] === null)
+ moleculesToEditIds.length > 0 &&
+ !(moleculesToEditIds.length === 1 && moleculesToEditIds[0] === null)
? moleculesToEditIds.map(id => dispatch(getMoleculeForId(id)))
: [];
@@ -330,10 +341,10 @@ const TagDetails = memo(() => {
dispatch(setDisplayUntaggedMolecules(!displayUntaggedMolecules));
};
- const handleSelectionButton = () => {
+ const handleSelectionButton = tagsToSelect => {
dispatch(setDisplayUntaggedMolecules(false));
if (selectAll) {
- dispatch(selectAllTags());
+ dispatch(selectAllTags(tagsToSelect));
} else {
dispatch(clearAllTags());
}
@@ -435,19 +446,18 @@ const TagDetails = memo(() => {
- {DJANGO_CONTEXT.pk && ([
+ {DJANGO_CONTEXT.pk && [
,
- ])}
+ ]}
diff --git a/js/components/preview/tags/redux/dispatchActions.js b/js/components/preview/tags/redux/dispatchActions.js
index 25d301379..4ec6820c4 100644
--- a/js/components/preview/tags/redux/dispatchActions.js
+++ b/js/components/preview/tags/redux/dispatchActions.js
@@ -63,9 +63,9 @@ export const removeSelectedTag = tagItem => dispatch => {
dispatch(removeFromSelectedTagList(tagItem));
};
-export const selectAllTags = () => (dispatch, getState) => {
+export const selectAllTags = (tagsToSelect = null) => (dispatch, getState) => {
const state = getState();
- let tagList = state.apiReducers.tagList;
+ let tagList = tagsToSelect ? tagsToSelect : state.apiReducers.tagList;
tagList.forEach(t => dispatch(appendSelectedTagList(t)));
};
diff --git a/js/reducers/selection/actions.js b/js/reducers/selection/actions.js
index c7d1ab2c8..05c4e9bd9 100644
--- a/js/reducers/selection/actions.js
+++ b/js/reducers/selection/actions.js
@@ -426,6 +426,17 @@ export const setPoseIdForObservationsDialog = poseId => {
};
};
+export const setObservationDialogAction = (poseId, observations, open, prevPoseId, prevObservations) => {
+ return {
+ type: constants.SET_OBSERVATION_DIALOG_ACTION,
+ poseId: poseId,
+ observations: observations,
+ open: open,
+ prevPoseId: prevPoseId,
+ prevObservations: prevObservations
+ };
+};
+
export const setObservationsForLHSCmp = observations => {
return {
type: constants.SET_OBSERVATIONS_FOR_LHS_CMP,
@@ -528,6 +539,24 @@ export const setUnselectAllMolecules = listOfNames => {
};
};
+//this is dummy action because we just need to record given action by tracking reducer
+//so we can undo, redo it and also restore from snapshot
+export const setSelectVisiblePoses = listOfItems => {
+ return {
+ type: constants.SET_SELECT_VISIBLE_POSES,
+ items: listOfItems
+ };
+};
+
+//this is dummy action because we just need to record given action by tracking reducer
+//so we can undo, redo it and also restore from snapshot
+export const setUnselectVisiblePoses = listOfItems => {
+ return {
+ type: constants.SET_UNSELECT_VISIBLE_POSES,
+ items: listOfItems
+ };
+};
+
export const setNextXMolecules = nextXMolecules => {
return {
type: constants.SET_NEXT_X_MOLECULES,
diff --git a/js/reducers/selection/constants.js b/js/reducers/selection/constants.js
index c6d5b7b18..5c57253ad 100644
--- a/js/reducers/selection/constants.js
+++ b/js/reducers/selection/constants.js
@@ -96,7 +96,12 @@ export const constants = {
SET_LHS_COMPOUNDS_INITIALIZED: prefix + 'SET_LHS_COMPOUNDS_INITIALIZED',
- SET_SCROLL_FIRED_FOR_LHS: prefix + 'SET_SCROLL_FIRED_FOR_LHS'
+ SET_SCROLL_FIRED_FOR_LHS: prefix + 'SET_SCROLL_FIRED_FOR_LHS',
+
+ SET_SELECT_VISIBLE_POSES: prefix + 'SET_SELECT_VISIBLE_POSES',
+ SET_UNSELECT_VISIBLE_POSES: prefix + 'SET_UNSELECT_VISIBLE_POSES',
+
+ SET_OBSERVATION_DIALOG_ACTION: prefix + 'SET_OBSERVATION_DIALOG_ACTION'
};
export const PREDEFINED_FILTERS = {
diff --git a/js/reducers/selection/selectionReducers.js b/js/reducers/selection/selectionReducers.js
index 6d6ee64c0..9af0f8996 100644
--- a/js/reducers/selection/selectionReducers.js
+++ b/js/reducers/selection/selectionReducers.js
@@ -167,7 +167,10 @@ export function selectionReducers(state = INITIAL_STATE, action = {}) {
return { ...state, isObservationDialogOpen: action.isOpen };
case constants.SET_POSE_ID_FOR_OBSERVATIONS_DIALOG:
- return { ...state, poseIdForObservationsDialog: action.poseId };
+ return {
+ ...state,
+ poseIdForObservationsDialog: action.poseId
+ };
case constants.SET_OBSERVATIONS_FOR_LHS_CMP:
return { ...state, observationsForLHSCmp: [...action.observations] };
diff --git a/js/reducers/tracking/constants.js b/js/reducers/tracking/constants.js
index 63b8831f9..b5bf429d9 100644
--- a/js/reducers/tracking/constants.js
+++ b/js/reducers/tracking/constants.js
@@ -112,6 +112,8 @@ export const actionType = {
MOLECULE_UNSELECTED: 'MOLECULE_UNSELECTED',
ALL_MOLECULES_SELECTED: 'ALL_MOLECULES_SELECTED',
ALL_MOLECULES_UNSELECTED: 'ALL_MOLECULES_UNSELECTED',
+ ALL_VISIBLE_HITS_SELECTED: 'ALL_VISIBLE_HITS_SELECTED',
+ ALL_VISIBLE_HITS_UNSELECTED: 'ALL_VISIBLE_HITS_UNSELECTED',
SEARCH_STRING: 'SEARCH_STRING',
SEARCH_STRING_HIT_NAVIGATOR: 'SEARCH_STRING_HIT_NAVIGATOR',
COMPOUND_LOCKED: 'COMPOUND_LOCKED',
@@ -122,7 +124,9 @@ export const actionType = {
SELECT_ALL_DATASET_COMPOUNDS: 'SELECT_ALL_DATASET_COMPOUNDS',
SELECTED_SELECT_ALL_BUTTON_FOR_DATASET: 'SELECTED_SELECT_ALL_BUTTON_FOR_DATASET',
ALL_COMPOUNDS_ADDED_TO_COLOR_GROUP: 'ALL_COMPOUNDS_ADDED_TO_COLOR_GROUP',
- ALL_COMPOUNDS_REMOVED_FROM_COLOR_GROUP: 'ALL_COMPOUNDS_REMOVED_FROM_COLOR_GROUP'
+ ALL_COMPOUNDS_REMOVED_FROM_COLOR_GROUP: 'ALL_COMPOUNDS_REMOVED_FROM_COLOR_GROUP',
+ POSE_WINDOW_ACTION: 'POSE_WINDOW_ACTION',
+ INSPIRATION_WINDOW_ACTION: 'INSPIRATION_WINDOW_ACTION'
};
export const snapshotSwitchManualActions = [
diff --git a/js/reducers/tracking/dispatchActions.js b/js/reducers/tracking/dispatchActions.js
index 5a5c80b54..8a166b949 100644
--- a/js/reducers/tracking/dispatchActions.js
+++ b/js/reducers/tracking/dispatchActions.js
@@ -49,7 +49,8 @@ import {
addDensityCustomView,
removeDensity,
getProteinData,
- selectAllHits
+ selectAllHits,
+ selectAllVisibleObservations
} from '../../components/preview/molecule/redux/dispatchActions';
import { setSortDialogOpen, setSearchStringOfHitNavigator } from '../../components/preview/molecule/redux/actions';
import {
@@ -98,7 +99,11 @@ import {
setCompoundToSelectedCompoundsByDataset,
setSelectAllButtonForDataset,
appendColorToSelectedColorFilter,
- removeColorFromSelectedColorFilter
+ removeColorFromSelectedColorFilter,
+ setIsOpenInspirationDialog,
+ setInspirationList,
+ setInspirationDialogOpenedForSelectedCompounds,
+ setSelectedDatasetScrolled
} from '../../components/datasets/redux/actions';
import {
removeComponentRepresentation,
@@ -150,7 +155,10 @@ import {
setSelectedAll,
setDeselectedAll,
setSelectedAllByType,
- setDeselectedAllByType
+ setDeselectedAllByType,
+ setObservationsForLHSCmp,
+ setOpenObservationsDialog,
+ setPoseIdForObservationsDialog
} from '../../../js/reducers/selection/actions';
import {
setSelectedAll as setSelectedAllOfDataset,
@@ -239,7 +247,10 @@ const saveActionsList = (project, snapshot, actionList, nglViewList, isAnonymous
const currentTargets = (currentTargetOn && [currentTargetOn]) || [];
const currentVectorSmiles = (currentVector && [currentVector]) || [];
- let orderedActionList = actionList.reverse((a, b) => a.timestamp - b.timestamp);
+ //filter out empty actions - don't know why but they sometimes appear in between activating ligand and protein...
+ const filteredActionList = actionList.filter(a => Object.keys(a).length > 0);
+
+ let orderedActionList = filteredActionList.reverse((a, b) => a.timestamp - b.timestamp);
let currentActions = [];
@@ -323,6 +334,13 @@ const saveActionsList = (project, snapshot, actionList, nglViewList, isAnonymous
currentActions
);
+ getCurrentActionListOfSelectAllVisibleHits(
+ orderedActionList,
+ actionType.ALL_VISIBLE_HITS_SELECTED,
+ getCollection(selectedMolecules),
+ currentActions
+ );
+
getCurrentActionList(
orderedActionList,
actionType.SIDECHAINS_TURNED_ON,
@@ -479,6 +497,8 @@ const saveActionsList = (project, snapshot, actionList, nglViewList, isAnonymous
currentActions
);
+ getCommonLastActionByType(orderedActionList, actionType.POSE_WINDOW_ACTION, currentActions);
+ getCommonLastActionByType(orderedActionList, actionType.INSPIRATION_WINDOW_ACTION, currentActions);
getCommonLastActionByType(orderedActionList, actionType.TAB, currentActions);
getCommonLastActionByType(orderedActionList, actionType.DATASET_INDEX, currentActions);
getCommonLastActionByType(orderedActionList, actionType.DATASET_FILTER, currentActions);
@@ -772,13 +792,12 @@ const getCurrentActionListOfAllSelectionByTypeOfDataset = (
const getCurrentActionListOfSelectAllMolecules = (orderedActionList, type, collection, currentActions) => {
let action = orderedActionList.find(action => action.type === type);
- if (action && collection) {
+ if (action) {
let actionItems = action.items;
let items = [];
- collection.forEach(data => {
- let item = actionItems.find(ai => ai.id === data.id);
- if (item) {
- items.push(item);
+ actionItems?.forEach(ai => {
+ if (ai.associatedObs && ai.associatedObs.length > 0) {
+ items.push(...ai.associatedObs);
}
});
@@ -786,6 +805,13 @@ const getCurrentActionListOfSelectAllMolecules = (orderedActionList, type, colle
}
};
+const getCurrentActionListOfSelectAllVisibleHits = (orderedActionList, type, collection, currentActions) => {
+ let action = orderedActionList.find(action => action.type === type);
+ if (action) {
+ currentActions.push(Object.assign({ ...action }));
+ }
+};
+
const getCurrentActionListOfPaintAllMolecules = (orderedActionList, type, collection, currentActions) => {
const definedColors = Object.keys(compoundsColors);
definedColors.forEach(color => {
@@ -1007,8 +1033,11 @@ export const restoreAfterTargetActions = (stages, projectId, snapshotId) => asyn
currentActionList = state.trackingReducers.current_actions_list;
}
+ //for some reason we have empty actions here... sometimes
+ currentActionList = currentActionList.filter(a => Object.keys(a).length > 0);
+
// const currentActionList = state.trackingReducers.current_actions_list;
- const orderedActionList = currentActionList.sort((a, b) => a.timestamp - b.timestamp);
+ let orderedActionList = currentActionList.sort((a, b) => a.timestamp - b.timestamp);
// console.log(`snapshotDebug - restoreAfterTargetActions - no. of actions: ${orderedActionList.length}`);
const targetId = state.apiReducers.target_on;
@@ -1045,10 +1074,11 @@ export const restoreAfterTargetActions = (stages, projectId, snapshotId) => asyn
dispatch(restoreSnapshotImageActions(projectId));
dispatch(restoreNglStateAction(orderedActionList, stages));
dispatch(restoreNglSettingsAction(orderedActionList, majorView.stage));
- dispatch(setIsActionsRestoring(false, true));
dispatch(restoreViewerControlActions(orderedActionList));
+ dispatch(setIsActionsRestoring(false, true));
dispatch(resetDatasetScrolledMap()); // Have a look at useScrollToSelected.js
dispatch(setScrollFiredForLHS(false));
+ dispatch(setSelectedDatasetScrolled(false));
dispatch(setIsSnapshotDirty(false));
dispatch(restoreSearchString(orderedActionList));
dispatch(restoreSearchStringHitNavigator(orderedActionList));
@@ -1506,6 +1536,8 @@ export const restoreMoleculesActions = (orderedActionList, stage) => async (disp
await dispatch(addNewType(moleculesAction, actionType.DENSITY_TYPE_ON, 'density', stage, state));
await dispatch(addNewType(moleculesAction, actionType.DENSITY_CUSTOM_TURNED_ON, 'densityCustom', stage, state));
await dispatch(restoreSelectAllMolecules(moleculesAction));
+ await dispatch(restoreSelectAllVisibleHits(moleculesAction));
+ await dispatch(restorePoseWindow(moleculesAction));
}
dispatch(restoreAllSelectionActions(orderedActionList, stage, true));
@@ -1649,6 +1681,35 @@ const restoreSelectAllMolecules = moleculesAction => (dispatch, getState) => {
}
};
+const restoreSelectAllVisibleHits = moleculesAction => (dispatch, getState) => {
+ let action = moleculesAction.find(ma => ma.type === actionType.ALL_VISIBLE_HITS_SELECTED);
+ if (action && action.items) {
+ const state = getState();
+ const allMolecules = state.apiReducers.all_mol_lists;
+ dispatch(setNextXMolecules(allMolecules.length));
+ action.items.forEach(m => {
+ dispatch(handleSelectMoleculeByName(m.code, true));
+ });
+ }
+};
+
+const restorePoseWindow = moleculesAction => (dispatch, getState) => {
+ let action = moleculesAction.find(ma => ma.type === actionType.POSE_WINDOW_ACTION);
+ if (action && action.isOpen && action.items?.length > 0) {
+ dispatch(handlePoseWindowAction(action, false));
+ }
+};
+
+const restoreInspirationsWindow = customDatasetAction => (dispatch, getState) => {
+ let action = customDatasetAction.find(ma => ma.type === actionType.INSPIRATION_WINDOW_ACTION);
+ if (action && action.isOpen && action.items?.length > 0) {
+ if (action.isSelectedList) {
+ dispatch(setInspirationDialogOpenedForSelectedCompounds(true));
+ }
+ dispatch(handleInspirationsWindowAction(action, false));
+ }
+};
+
const restorePaintAllCompounds = compoundActions => (dispatch, getState) => {
let actions = compoundActions?.filter(ma => ma.type === actionType.ALL_COMPOUNDS_ADDED_TO_COLOR_GROUP);
actions?.forEach(action => {
@@ -2001,6 +2062,8 @@ export const restoreCompoundsActions = (orderedActionList, stage) => (dispatch,
dispatch(restoreAllSelectionActions(orderedActionList, stage, false));
dispatch(restoreAllSelectionByTypeActions(orderedActionList, stage, false));
+ dispatch(restoreInspirationsWindow(compoundsAction));
+
dispatch(setIsTrackingCompoundsRestoring(false));
};
@@ -2361,6 +2424,12 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.ARROW_NAVIGATION:
dispatch(handleArrowNavigationAction(action, false, majorViewStage));
break;
+ case actionType.POSE_WINDOW_ACTION:
+ dispatch(handlePoseWindowAction(action, true));
+ break;
+ case actionType.INSPIRATION_WINDOW_ACTION:
+ dispatch(handleInspirationsWindowAction(action, true));
+ break;
case actionType.VECTOR_SELECTED:
dispatch(handleVectorAction(action, false));
break;
@@ -2448,6 +2517,12 @@ const handleUndoAction = (action, stages) => (dispatch, getState) => {
case actionType.ALL_MOLECULES_UNSELECTED:
dispatch(handleSelectAllMolecules(action, true));
break;
+ case actionType.ALL_VISIBLE_HITS_SELECTED:
+ dispatch(handleSelectAllVisibleHits(action, false));
+ break;
+ case actionType.ALL_VISIBLE_HITS_UNSELECTED:
+ dispatch(handleSelectAllVisibleHits(action, true));
+ break;
case actionType.TAB:
dispatch(handleTabAction(action, false));
break;
@@ -2600,6 +2675,12 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.SURFACE_TURNED_ON:
dispatch(handleMoleculeAction(action, 'surface', true, majorViewStage, state));
break;
+ case actionType.POSE_WINDOW_ACTION:
+ dispatch(handlePoseWindowAction(action, false));
+ break;
+ case actionType.INSPIRATION_WINDOW_ACTION:
+ dispatch(handleInspirationsWindowAction(action, false));
+ break;
case actionType.QUALITY_TURNED_ON:
dispatch(handleMoleculeAction(action, 'quality', true, majorViewStage, state));
break;
@@ -2729,6 +2810,12 @@ const handleRedoAction = (action, stages) => (dispatch, getState) => {
case actionType.ALL_MOLECULES_UNSELECTED:
dispatch(handleSelectAllMolecules(action, false));
break;
+ case actionType.ALL_VISIBLE_HITS_SELECTED:
+ dispatch(handleSelectAllVisibleHits(action, true));
+ break;
+ case actionType.ALL_VISIBLE_HITS_UNSELECTED:
+ dispatch(handleSelectAllVisibleHits(action, false));
+ break;
case actionType.TAB:
dispatch(handleTabAction(action, true));
break;
@@ -3234,6 +3321,12 @@ const handleSelectAllMolecules = (action, isSelected) => (dispatch, getState) =>
}
};
+const handleSelectAllVisibleHits = (action, isSelected) => (dispatch, getState) => {
+ if (action && action.items) {
+ dispatch(selectAllVisibleObservations(action.items, setNextXMolecules, !isSelected));
+ }
+};
+
const handleSelectAllDatasetCompounds = (action, isSelected) => (dispatch, getState) => {
if (action.selectAllButton) {
dispatch(setSelectAllButtonForDataset(isSelected));
@@ -3647,6 +3740,68 @@ const handleTagAction = (action, isSelected) => (dispatch, getState) => {
}
};
+const handlePoseWindowAction = (action, invert) => (dispatch, getState) => {
+ if (action) {
+ const openDialog = action.isOpen;
+ if (openDialog) {
+ if (invert && action.prevPoseId && action.prevObservations?.length > 0) {
+ dispatch(setObservationsForLHSCmp(action.prevObservations));
+ dispatch(setOpenObservationsDialog(true));
+ dispatch(setPoseIdForObservationsDialog(action.prevPoseId));
+ dispatch(setScrollFiredForLHS(false));
+ } else if (invert) {
+ dispatch(setObservationsForLHSCmp([]));
+ dispatch(setOpenObservationsDialog(false));
+ dispatch(setPoseIdForObservationsDialog(0));
+ // dispatch(setScrollFiredForLHS(false));
+ } else if (action.object_id && action.items?.length > 0) {
+ dispatch(setObservationsForLHSCmp(action.items));
+ dispatch(setOpenObservationsDialog(true));
+ dispatch(setPoseIdForObservationsDialog(action.object_id));
+ dispatch(setScrollFiredForLHS(false));
+ } else {
+ console.error(`${invert ? 'Undo' : 'Redo'} failed for action: ${JSON.stringify(action)}`);
+ }
+ } else {
+ dispatch(setObservationsForLHSCmp([]));
+ dispatch(setOpenObservationsDialog(false));
+ dispatch(setPoseIdForObservationsDialog(0));
+ }
+ }
+};
+
+const handleInspirationsWindowAction = (action, invert) => (dispatch, getState) => {
+ if (action) {
+ const openDialog = action.isOpen;
+ if (openDialog) {
+ if (invert && action.prevCmpdId && action.prevInspirations?.length > 0) {
+ dispatch(setInspirationMoleculeDataList(action.prevInspirations));
+ dispatch(setIsOpenInspirationDialog(true));
+ dispatch(setInspirationList(action.datasetID, [action.prevCmpdId]));
+ dispatch(resetDatasetScrolledMap());
+ dispatch(setSelectedDatasetScrolled(false));
+ } else if (invert) {
+ dispatch(setInspirationMoleculeDataList([]));
+ dispatch(setIsOpenInspirationDialog(false));
+ dispatch(setInspirationList(action.datasetID, 0));
+ // dispatch(resetDatasetScrolledMap());
+ } else if (action.object_id && action.items?.length > 0) {
+ dispatch(setInspirationMoleculeDataList(action.items));
+ dispatch(setIsOpenInspirationDialog(true));
+ dispatch(setInspirationList(action.datasetID, [action.object_id]));
+ dispatch(resetDatasetScrolledMap());
+ dispatch(setSelectedDatasetScrolled(false));
+ } else {
+ console.error(`${invert ? 'Undo' : 'Redo'} failed for action: ${JSON.stringify(action)}`);
+ }
+ } else {
+ dispatch(setInspirationMoleculeDataList([]));
+ dispatch(setIsOpenInspirationDialog(false));
+ dispatch(setInspirationList(action.datasetID, 0));
+ }
+ }
+};
+
const handleDensityMoleculeAction = (action, type, isAdd, stage, state, skipTracking) => (dispatch, getState) => {
if (action.object_type === actionObjectType.MOLECULE || action.object_type === actionObjectType.INSPIRATION) {
if (isAdd) {
diff --git a/js/reducers/tracking/dispatchActionsSwitchSnapshot.js b/js/reducers/tracking/dispatchActionsSwitchSnapshot.js
index 4d6ed8686..7f03d891d 100644
--- a/js/reducers/tracking/dispatchActionsSwitchSnapshot.js
+++ b/js/reducers/tracking/dispatchActionsSwitchSnapshot.js
@@ -9,7 +9,11 @@ import {
setSnapshotActionsDownloaded
} from './actions';
import { resetSelectionState, setScrollFiredForLHS } from '../selection/actions';
-import { resetDatasetsStateOnSnapshotChange, resetDatasetScrolledMap } from '../../components/datasets/redux/actions';
+import {
+ resetDatasetsStateOnSnapshotChange,
+ resetDatasetScrolledMap,
+ setSelectedDatasetScrolled
+} from '../../components/datasets/redux/actions';
import { resetViewerControlsState } from '../../components/preview/viewerControls/redux/actions';
import { resetNglTrackingState } from '../nglTracking/dispatchActions';
import { removeAllNglComponents } from '../ngl/actions';
@@ -133,6 +137,7 @@ export const restoreAfterSnapshotChange = (stages, projectId) => async (dispatch
dispatch(resetDatasetScrolledMap()); // Have a look at useScrollToSelected.js
dispatch(setScrollFiredForLHS(false));
+ dispatch(setSelectedDatasetScrolled(false));
dispatch(setIsActionsRestoring(false, true));
console.count(`restoreAfterSnapshotChange end`);
diff --git a/js/reducers/tracking/trackingActions.js b/js/reducers/tracking/trackingActions.js
index 447e0d8bf..8415e4811 100644
--- a/js/reducers/tracking/trackingActions.js
+++ b/js/reducers/tracking/trackingActions.js
@@ -64,6 +64,42 @@ export const findTrackAction = (action, state) => (dispatch, getState) => {
text: `${actionDescription.TAG} ${action.item.tag} ${actionDescription.TURNED_OFF}`
};
}
+ } else if (action.type === selectionConstants.SET_OBSERVATION_DIALOG_ACTION) {
+ if (action) {
+ trackAction = {
+ type: actionType.POSE_WINDOW_ACTION,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: actionObjectType.MOLECULE,
+ object_name: action.poseId,
+ object_id: action.poseId,
+ items: action?.observations?.length > 0 ? [...action.observations] : [],
+ isOpen: action.open,
+ prevPoseId: action.prevPoseId,
+ prevObservations: action.prevObservations,
+ text: action.open ? `Pose window opened` : `Pose window closed`
+ };
+ }
+ } else if (action.type === customDatasetConstants.SET_INSPIRATION_DIALOG_ACTION) {
+ if (action) {
+ trackAction = {
+ type: actionType.INSPIRATION_WINDOW_ACTION,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: actionObjectType.COMPOUND,
+ object_name: action.inspirationList,
+ object_id: action.inspirationList,
+ items: action?.inpsirations?.length > 0 ? [...action.inpsirations] : [],
+ datasetID: action.datasetID,
+ isOpen: action.open,
+ prevCmpdId: action.prevInspirationList?.length > 0 ? action.prevInspirationList[0] : 0,
+ prevInspirations: action.prevInspirations,
+ isSelectedList: action.isSelectedList,
+ text: action.open ? `Inspirations window opened` : `Inspirations window closed`
+ };
+ }
} else if (action.type === apiConstants.SET_MOL_GROUP_ON) {
if (action.mol_group_on) {
let molGroupSelection = state.selectionReducers.mol_group_selection;
@@ -260,6 +296,34 @@ export const findTrackAction = (action, state) => (dispatch, getState) => {
text: `All hits were unselected`
};
}
+ } else if (action.type === selectionConstants.SET_SELECT_VISIBLE_POSES) {
+ if (action && action.items) {
+ let objectType = actionObjectType.MOLECULE;
+
+ trackAction = {
+ type: actionType.ALL_VISIBLE_HITS_SELECTED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ items: [...action.items],
+ text: `All visible hits were selected`
+ };
+ }
+ } else if (action.type === selectionConstants.SET_UNSELECT_VISIBLE_POSES) {
+ if (action && action.items) {
+ let objectType = actionObjectType.MOLECULE;
+
+ trackAction = {
+ type: actionType.ALL_VISIBLE_HITS_UNSELECTED,
+ annotation: actionAnnotation.CHECK,
+ timestamp: Date.now(),
+ username: username,
+ object_type: objectType,
+ items: [...action.items],
+ text: `All visible hits were unselected`
+ };
+ }
} else if (action.type === selectionConstants.APPEND_FRAGMENT_DISPLAY_LIST) {
if (action.item) {
let objectType = action.item.isInspiration === true ? actionObjectType.INSPIRATION : actionObjectType.MOLECULE;
diff --git a/js/reducers/tracking/trackingMiddleware.js b/js/reducers/tracking/trackingMiddleware.js
index def9892a8..8508793dd 100644
--- a/js/reducers/tracking/trackingMiddleware.js
+++ b/js/reducers/tracking/trackingMiddleware.js
@@ -10,13 +10,15 @@ const trackingMiddleware = ({ dispatch, getState }) => next => action => {
const state = getState();
if (action && action.type && !action.type.includes(constants.APPEND_ACTIONS_LIST)) {
let trackAction = dispatch(findTrackAction(action, state));
- if (trackAction && trackAction != null) {
+ if (trackAction && trackAction != null && Object.keys(trackAction).length > 0) {
const isSnapshotDirty = state.trackingReducers.isSnapshotDirty;
const snapshotLoadingInProgress = state.apiReducers.snapshotLoadingInProgress;
if (!isSnapshotDirty && !snapshotLoadingInProgress) {
dispatch(setIsSnapshotDirty(true));
}
dispatch(appendAndSendTrackingActions(trackAction));
+ } else if (trackAction && trackAction != null && Object.keys(trackAction).length === 0) {
+ console.error(`Track action is empty for action: ${JSON.stringify(action)}`);
}
}