diff --git a/js/components/datasets/datasetMoleculeList.js b/js/components/datasets/datasetMoleculeList.js index 99847c459..d17d656e6 100644 --- a/js/components/datasets/datasetMoleculeList.js +++ b/js/components/datasets/datasetMoleculeList.js @@ -46,7 +46,8 @@ import { getAllVisibleButNotLockedCompounds, getObservationForLHSReference, getCurrentDatasetIterator, - resetDatasetIterator + resetDatasetIterator, + getInspirationsForMol } from './redux/dispatchActions'; import { setAskLockCompoundsQuestion, @@ -61,7 +62,8 @@ import { appendColorToAllCompoundsOfDataset, removeCompoundColorOfDataset, removeColorFromAllCompoundsOfDataset, - setDatasetIterator + setDatasetIterator, + setInspirationDialogAction } from './redux/actions'; import { DatasetFilter } from './datasetFilter'; import { FilterList, Link, DeleteForever, ArrowUpward, ArrowDownward, Edit } from '@material-ui/icons'; @@ -469,6 +471,8 @@ const DatasetMoleculeList = ({ title, datasetID, url }) => { const purpleInput = useSelector(state => state.previewReducers.compounds[compoundsColors.purple.key]); const apricotInput = useSelector(state => state.previewReducers.compounds[compoundsColors.apricot.key]); + const allInspirations = useSelector(state => state.datasetsReducers.allInspirations); + const inputs = { [compoundsColors.blue.key]: blueInput, [compoundsColors.red.key]: redInput, @@ -955,6 +959,18 @@ const DatasetMoleculeList = ({ title, datasetID, url }) => { if (node) { setSelectedMoleculeRef(node); } + + dispatch( + setInspirationDialogAction( + datasetID, + nextItem.id, + getInspirationsForMol(allInspirations, datasetID, nextItem.id), + true, + 0, + [] + ) + ); + dispatch( moveDatasetMoleculeUpDown(stage, datasetID, firstItem, datasetID, nextItem, dataValue, ARROW_TYPE.DOWN) ); @@ -1005,6 +1021,18 @@ const DatasetMoleculeList = ({ title, datasetID, url }) => { if (node) { setSelectedMoleculeRef(node); } + + dispatch( + setInspirationDialogAction( + datasetID, + prevItem.id, + getInspirationsForMol(allInspirations, datasetID, prevItem.id), + true, + 0, + [] + ) + ); + dispatch(moveDatasetMoleculeUpDown(stage, datasetID, firstItem, datasetID, prevItem, dataValue, ARROW_TYPE.UP)); } } diff --git a/js/components/datasets/datasetMoleculeView/datasetMoleculeView.js b/js/components/datasets/datasetMoleculeView/datasetMoleculeView.js index e04b440ba..6b7fda1c9 100644 --- a/js/components/datasets/datasetMoleculeView/datasetMoleculeView.js +++ b/js/components/datasets/datasetMoleculeView/datasetMoleculeView.js @@ -58,7 +58,9 @@ import { setIsOpenLockVisibleCompoundsDialogLocal, setCmpForLocalLockVisibleCompoundsDialog, setAskLockCompoundsQuestion, - setCompoundToSelectedCompoundsByDataset + setCompoundToSelectedCompoundsByDataset, + setInspirationDialogAction, + setInspirationMoleculeDataList } from '../redux/actions'; import { centerOnLigandByMoleculeID } from '../../../reducers/ngl/dispatchActions'; import { ArrowDownward, ArrowUpward, MyLocation } from '@material-ui/icons'; @@ -465,6 +467,34 @@ const DatasetMoleculeView = memo( const [pdbData, setPdbData] = useState(null); const isPdbAvailable = !!(data && (data.pdb_info || data.site_observation_code)); + const isInspirationsDialogOpened = useSelector(state => state.datasetsReducers.isOpenInspirationDialog); + const inspirationLists = useSelector(state => state.datasetsReducers.inspirationLists); + + const dialogOpenedForInspirationWithId = + inspirationLists.hasOwnProperty(datasetID) && inspirationLists[datasetID]?.length > 0 + ? inspirationLists[datasetID][0] + : 0; + + const allInspirations = useSelector(state => state.datasetsReducers.allInspirations); + + useEffect(() => { + if (isInspirationsDialogOpened && dialogOpenedForInspirationWithId === currentID) { + dispatch(setInspirationMoleculeDataList(getInspirationsForMol(allInspirations, datasetID, currentID))); + if (setRef) { + setRef(ref.current); + } + } + }, [ + allInspirations, + currentID, + datasetID, + dialogOpenedForInspirationWithId, + dispatch, + isInspirationsDialogOpened, + inspirationLists, + setRef + ]); + useEffect(() => { if (data.site_observation_code) { const molecule = allMolecules.find(mol => mol.code === data.site_observation_code); @@ -781,6 +811,18 @@ const DatasetMoleculeView = memo( setRef(refNext); } + dispatch( + setInspirationDialogAction( + nextDatasetID, + nextItem.id, + getInspirationsForMol(allInspirations, nextDatasetID, nextItem.id), + true, + 0, + [], + true + ) + ); + dispatch( moveSelectedDatasetMoleculeUpDown( stage, @@ -825,6 +867,17 @@ const DatasetMoleculeView = memo( setRef(refNext); } + dispatch( + setInspirationDialogAction( + nextDatasetID, + nextItem.id, + getInspirationsForMol(allInspirations, nextDatasetID, nextItem.id), + true, + 0, + [] + ) + ); + dispatch( moveDatasetMoleculeUpDown(stage, datasetID, data, nextDatasetID, nextItem, dataValue, ARROW_TYPE.DOWN) ); @@ -866,6 +919,18 @@ const DatasetMoleculeView = memo( setRef(refPrevious); } + dispatch( + setInspirationDialogAction( + previousDatasetID, + previousItem.id, + getInspirationsForMol(allInspirations, previousDatasetID, previousItem.id), + true, + 0, + [], + true + ) + ); + dispatch( moveSelectedDatasetMoleculeUpDown( stage, @@ -912,6 +977,17 @@ const DatasetMoleculeView = memo( setRef(refPrevious); } + dispatch( + setInspirationDialogAction( + previousDatasetID, + previousItem.id, + getInspirationsForMol(allInspirations, previousDatasetID, previousItem.id), + true, + 0, + [] + ) + ); + dispatch( moveDatasetMoleculeUpDown( stage, @@ -1208,8 +1284,6 @@ const DatasetMoleculeView = memo( onClick={() => { setLoadingInspiration(true); dispatch((dispatch, getState) => { - const allInspirations = getState().datasetsReducers.allInspirations; - dispatch( clickOnInspirations({ datasetID, @@ -1217,6 +1291,17 @@ const DatasetMoleculeView = memo( computed_inspirations: getInspirationsForMol(allInspirations, datasetID, currentID) }) ); + dispatch( + setInspirationDialogAction( + datasetID, + currentID, + getInspirationsForMol(allInspirations, datasetID, currentID), + true, + 0, + [], + inSelectedCompoundsList + ) + ); }); if (setRef) { setRef(ref.current); diff --git a/js/components/datasets/inspirationDialog.js b/js/components/datasets/inspirationDialog.js index 80b0e2718..e018ae13c 100644 --- a/js/components/datasets/inspirationDialog.js +++ b/js/components/datasets/inspirationDialog.js @@ -17,7 +17,7 @@ import { } from '../preview/molecule/redux/dispatchActions'; import MoleculeView from '../preview/molecule/moleculeView'; import { moleculeProperty } from '../preview/molecule/helperConstants'; -import { setIsOpenInspirationDialog } from './redux/actions'; +import { setInspirationDialogAction, setInspirationList, setIsOpenInspirationDialog } from './redux/actions'; import { Button } from '../common/Inputs/Button'; import classNames from 'classnames'; import { colourList } from '../preview/molecule/utils/color'; @@ -146,6 +146,8 @@ export const InspirationDialog = memo( const moleculeLists = useSelector(state => state.datasetsReducers.moleculeLists); const scoreDatasetMap = useSelector(state => state.datasetsReducers.scoreDatasetMap); + const compoundId = useSelector(state => state.datasetsReducers); + /** * Get rationale as score of each individual molecule * @returns {string} @@ -354,7 +356,22 @@ export const InspirationDialog = memo( dispatch(setIsOpenInspirationDialog(false))} + onClick={() => { + dispatch(setIsOpenInspirationDialog(false)); + dispatch(setInspirationList(datasetID, [])); + dispatch( + setInspirationDialogAction( + datasetID, + 0, + [], + false, + inspirationLists.hasOwnProperty(datasetID) && inspirationLists[datasetID].length > 0 + ? inspirationLists[datasetID][0] + : 0, + inspirationMoleculeDataList + ) + ); + }} > diff --git a/js/components/datasets/redux/actions.js b/js/components/datasets/redux/actions.js index 715bde06f..a6eb987ba 100644 --- a/js/components/datasets/redux/actions.js +++ b/js/components/datasets/redux/actions.js @@ -199,6 +199,33 @@ export const setInspirationList = function(datasetID, inspirationList) { } }; }; + +export const setInspirationDialogAction = ( + datasetID, + inspirationList, + inpsirations, + open, + prevInspirationList, + prevInspirations, + isSelectedList = false +) => { + return { + type: constants.SET_INSPIRATION_DIALOG_ACTION, + datasetID: datasetID, + inspirationList: inspirationList, + inpsirations: inpsirations, + open: open, + prevInspirationList: prevInspirationList, + prevInspirations: prevInspirations, + isSelectedList: isSelectedList + }; +}; + +export const setInspirationDialogOpenedForSelectedCompounds = isOpen => ({ + type: constants.SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND, + isOpen: isOpen +}); + export const appendInspirationList = function(datasetID, itemID) { return { type: constants.APPEND_INSPIRATION_LIST, @@ -534,6 +561,11 @@ export const setDatasetScrolled = datasetId => ({ payload: datasetId }); +export const setSelectedDatasetScrolled = scrolled => ({ + type: constants.SET_SELECTED_DATASET_SCROLLED, + payload: scrolled +}); + export const resetDatasetScrolledMap = () => ({ type: constants.RESET_DATASET_SCROLLED_MAP }); diff --git a/js/components/datasets/redux/constants.js b/js/components/datasets/redux/constants.js index 1fdfcbd64..3e96418fe 100644 --- a/js/components/datasets/redux/constants.js +++ b/js/components/datasets/redux/constants.js @@ -101,6 +101,7 @@ export const constants = { SET_DATASET_SCROLLED: prefix + 'SET_DATASET_SCROLLED', RESET_DATASET_SCROLLED_MAP: prefix + 'RESET_DATASET_SCROLLED_MAP', + SET_SELECTED_DATASET_SCROLLED: prefix + 'SET_SELECTED_DATASET_SCROLLED', SET_IS_OPEN_LOCK_VISIBLE_COMPOUNDS_DIALOG_GLOBAL: prefix + 'SET_IS_OPEN_LOCK_VISIBLE_COMPOUNDS_DIALOG_GLOBAL', SET_IS_OPEN_LOCK_VISIBLE_COMPOUNDS_DIALOG_LOCAL: prefix + 'SET_IS_OPEN_LOCK_VISIBLE_COMPOUNDS_DIALOG_LOCAL', @@ -118,7 +119,10 @@ export const constants = { SET_UPDATED_DATASETS: prefix + 'SET_UPDATED_DATASETS', SET_DATASET_ITERATOR: prefix + 'SET_DATASET_ITERATOR', - SET_SELECTED_COMPOUNDS_ITERATOR: prefix + 'SET_SELECTED_COMPOUNDS_ITERATOR' + SET_SELECTED_COMPOUNDS_ITERATOR: prefix + 'SET_SELECTED_COMPOUNDS_ITERATOR', + + SET_INSPIRATION_DIALOG_ACTION: prefix + 'SET_INSPIRATION_DIALOG_ACTION', + SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND: prefix + 'SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND' }; export const COUNT_OF_VISIBLE_SCORES = 7; diff --git a/js/components/datasets/redux/dispatchActions.js b/js/components/datasets/redux/dispatchActions.js index 6e6aaf515..76f1e2a7d 100644 --- a/js/components/datasets/redux/dispatchActions.js +++ b/js/components/datasets/redux/dispatchActions.js @@ -1110,14 +1110,14 @@ export const moveSelectedDatasetMoleculeUpDown = ( const inspirations = getInspirationsForMol(allInspirations, datasetID, newItem.id); dispatch(setInspirationMoleculeDataList(inspirations)); - dispatch(clearCompoundView(newItem, datasetID, stage, true)); + dispatch(clearCompoundView(newItem, datasetID, stage, false)); await Promise.all([ - dispatch(moveSelectedMoleculeSettings(stage, item, newItem, newItemDatasetID, datasetID, dataValue, true)), - dispatch(moveSelectedDatasetMoleculeInspirationsSettings(item, newItem, stage, true)) + dispatch(moveSelectedMoleculeSettings(stage, item, newItem, newItemDatasetID, datasetID, dataValue, false)), + dispatch(moveSelectedDatasetMoleculeInspirationsSettings(item, newItem, stage, false)) ]); - dispatch(removeSelectedDatasetMolecules(stage, true, newItem, { ...lockedCompounds })); - dispatch(removeSelectedTypesOfDatasetInspirations([newItem], stage, true, datasetID)); + dispatch(removeSelectedDatasetMolecules(stage, false, newItem, { ...lockedCompounds })); + dispatch(removeSelectedTypesOfDatasetInspirations([newItem], stage, false, datasetID)); dispatch(setSelectedCompoundsIterator(newItemDatasetID, newItem)); }; @@ -1139,21 +1139,21 @@ export const moveDatasetMoleculeUpDown = (stage, datasetID, item, newItemDataset const dataValue = { ...data, objectsInView }; - dispatch(setArrowUpDown(datasetID, item, newItem, direction, dataValue)); + // dispatch(setArrowUpDown(datasetID, item, newItem, direction, dataValue)); const inspirations = getInspirationsForMol(allInspirations, datasetID, newItem.id); dispatch(setInspirationMoleculeDataList(inspirations)); - dispatch(clearCompoundView(newItem, datasetID, stage, true)); + dispatch(clearCompoundView(newItem, datasetID, stage, false)); console.log('moveDatasetMoleculeUpDown - compounds cleared'); await Promise.all([ - dispatch(moveSelectedMoleculeSettings(stage, item, newItem, newItemDatasetID, datasetID, dataValue, true)), - dispatch(moveSelectedDatasetMoleculeInspirationsSettings(item, newItem, stage, true)) + dispatch(moveSelectedMoleculeSettings(stage, item, newItem, newItemDatasetID, datasetID, dataValue, false)), + dispatch(moveSelectedDatasetMoleculeInspirationsSettings(item, newItem, stage, false)) ]); dispatch( - removeSelectedDatasetMolecules(stage, true, newItem, { [newItemDatasetID]: [newItem.id, ...lockedCompounds] }) + removeSelectedDatasetMolecules(stage, false, newItem, { [newItemDatasetID]: [newItem.id, ...lockedCompounds] }) ); - dispatch(removeSelectedTypesOfDatasetInspirations([newItem], stage, true, datasetID)); + dispatch(removeSelectedTypesOfDatasetInspirations([newItem], stage, false, datasetID)); dispatch(setDatasetIterator(newItemDatasetID, newItem)); }; diff --git a/js/components/datasets/redux/reducer.js b/js/components/datasets/redux/reducer.js index af94f6198..43595c0ee 100644 --- a/js/components/datasets/redux/reducer.js +++ b/js/components/datasets/redux/reducer.js @@ -83,7 +83,11 @@ export const INITIAL_STATE = { //iterator functionality for dataset tabs and also selected compounds tab iteratorDatasets: {}, - iteratorSelectedCompounds: null + iteratorSelectedCompounds: null, + + inspirationsDialogOpenedForSelectedCompound: false, + + isSelectedDatasetScrolled: false }; /** @@ -220,6 +224,9 @@ export const datasetsReducers = (state = INITIAL_STATE, action = {}) => { case constants.SET_IS_LOADING_MOLECULE_LIST: return Object.assign({}, state, { isLoadingMoleculeList: action.payload }); + case constants.SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND: + return { ...state, inspirationsDialogOpenedForSelectedCompound: action.isOpen }; + case constants.SET_SELECTED_DATASET_INDEX: return Object.assign({}, state, { selectedDatasetIndex: action.payload.value }); @@ -737,6 +744,10 @@ export const datasetsReducers = (state = INITIAL_STATE, action = {}) => { return { ...state, datasetScrolledMap: {} }; } + case constants.SET_SELECTED_DATASET_SCROLLED: { + return { ...state, isSelectedDatasetScrolled: action.payload }; + } + case constants.SET_COMPOUND_SET: return Object.assign({}, state, { expandCompoundSet: action.payload }); diff --git a/js/components/datasets/selectedCompoundsList.js b/js/components/datasets/selectedCompoundsList.js index 7cc277d8e..54975fe9b 100644 --- a/js/components/datasets/selectedCompoundsList.js +++ b/js/components/datasets/selectedCompoundsList.js @@ -25,6 +25,8 @@ import DatasetMoleculeView from './datasetMoleculeView'; import { InspirationDialog } from './inspirationDialog'; import { setCrossReferenceCompoundName, + setInspirationDialogAction, + setInspirationList, setIsOpenInspirationDialog, setIsOpenLockVisibleCompoundsDialogGlobal, setSelectedCompoundsList @@ -189,9 +191,10 @@ export const SelectedCompoundList = memo(() => { setCurrentPage(currentPage + 1); }; - const listItemOffset = (currentPage + 1) * moleculesPerPage; - const currentMolecules = moleculesObjectIDListOfCompoundsToBuy.slice(0, listItemOffset); - const canLoadMore = listItemOffset < moleculesObjectIDListOfCompoundsToBuy.length; + // const listItemOffset = (currentPage + 1) * moleculesPerPage; + // const currentMolecules = moleculesObjectIDListOfCompoundsToBuy.slice(0, listItemOffset); + const currentMolecules = moleculesObjectIDListOfCompoundsToBuy.slice(0, moleculesObjectIDListOfCompoundsToBuy.length); + const canLoadMore = false; //listItemOffset < moleculesObjectIDListOfCompoundsToBuy.length; const ligandList = useSelector(state => getListOfSelectedLigandOfAllDatasets(state)); const proteinList = useSelector(state => state.selectionReducers.proteinList); @@ -212,12 +215,12 @@ export const SelectedCompoundList = memo(() => { const compoundsToBuyList = useSelector(state => state.datasetsReducers.compoundsToBuyDatasetMap); - let selectedMolecules = []; - Object.keys(compoundsToBuyList).forEach(datasetId => { - const datasetCmpsToBuy = compoundsToBuyList[datasetId] || []; - const molsOfDataset = moleculeLists[datasetId] || []; - selectedMolecules = [...selectedMolecules, ...molsOfDataset.filter(mol => datasetCmpsToBuy?.includes(mol.id))]; - }); + // let selectedMolecules = []; + // Object.keys(compoundsToBuyList).forEach(datasetId => { + // const datasetCmpsToBuy = compoundsToBuyList[datasetId] || []; + // const molsOfDataset = moleculeLists[datasetId] || []; + // selectedMolecules = [...selectedMolecules, ...molsOfDataset.filter(mol => datasetCmpsToBuy?.includes(mol.id))]; + // }); const { addMoleculeViewRef, setScrollToMoleculeId, getNode } = useScrollToCompound(); @@ -265,6 +268,8 @@ export const SelectedCompoundList = memo(() => { const colorFilterSettings = useSelector(state => state.datasetsReducers.selectedColorsInFilter); + const allInspirations = useSelector(state => state.datasetsReducers.allInspirations); + const [lockCompoundsDialogAnchorE1, setLockCompoundsDialogAnchorE1] = useState(null); //we need to add also lists to dependancy array because isCompoundVisible depends on them @@ -300,6 +305,7 @@ export const SelectedCompoundList = memo(() => { useEffect(() => { return () => { dispatch(setIsOpenInspirationDialog(false)); + // dispatch(setInspirationList(datasetID, [])); dispatch(resetCrossReferenceDialog()); }; }, [dispatch]); @@ -708,6 +714,19 @@ export const SelectedCompoundList = memo(() => { if (node) { setSelectedMoleculeRef(node); } + + dispatch( + setInspirationDialogAction( + nextItem.datasetID, + nextItem.molecule.id, + getInspirationsForMol(allInspirations, nextItem.datasetID, nextItem.molecule.id), + true, + 0, + [], + true + ) + ); + dispatch( moveSelectedDatasetMoleculeUpDown( stage, @@ -767,6 +786,19 @@ export const SelectedCompoundList = memo(() => { if (node) { setSelectedMoleculeRef(node); } + + dispatch( + setInspirationDialogAction( + prevItem.datasetID, + prevItem.molecule.id, + getInspirationsForMol(allInspirations, prevItem.datasetID, prevItem.molecule.id), + true, + 0, + [], + true + ) + ); + dispatch( moveSelectedDatasetMoleculeUpDown( stage, diff --git a/js/components/datasets/useScrollToCompound.js b/js/components/datasets/useScrollToCompound.js index da333b329..263614ddc 100644 --- a/js/components/datasets/useScrollToCompound.js +++ b/js/components/datasets/useScrollToCompound.js @@ -1,22 +1,166 @@ import { useCallback, useEffect, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { + getListOfSelectedComplexOfAllDatasets, + getListOfSelectedLigandOfAllDatasets, + getListOfSelectedProteinOfAllDatasets, + getListOfSelectedSurfaceOfAllDatasets, + getMoleculesObjectIDListOfCompoundsToBuy +} from './redux/selectors'; +import { setSelectedDatasetScrolled } from './redux/actions'; +import { v4 as uuidv4 } from 'uuid'; export const useScrollToCompound = () => { + const dispatch = useDispatch(); + const [moleculeViewRefs, setMoleculeViewRefs] = useState({}); const [scrollToMoleculeId, setScrollToMoleculeId] = useState(null); + const [uuid] = useState(uuidv4()); + + const isSelectedDatasetScrolled = useSelector(state => state.datasetsReducers.isSelectedDatasetScrolled); + + const ligandList = useSelector(state => getListOfSelectedLigandOfAllDatasets(state)); + const proteinList = useSelector(state => state.selectionReducers.proteinList); + const complexList = useSelector(state => state.selectionReducers.complexList); + const surfaceList = useSelector(state => state.selectionReducers.surfaceList); + + const proteinListDataset = useSelector(state => getListOfSelectedProteinOfAllDatasets(state)); + const complexListDataset = useSelector(state => getListOfSelectedComplexOfAllDatasets(state)); + const surfaceListDataset = useSelector(state => getListOfSelectedSurfaceOfAllDatasets(state)); + + const moleculesObjectIDListOfCompoundsToBuy = useSelector(getMoleculesObjectIDListOfCompoundsToBuy); + + const isInspirationsDialogOpened = useSelector(state => state.datasetsReducers.isOpenInspirationDialog); + const inspirationLists = useSelector(state => state.datasetsReducers.inspirationLists); + const isItForSelectedCompoundsList = useSelector( + state => state.datasetsReducers.inspirationsDialogOpenedForSelectedCompound + ); + + const tabValue = useSelector(state => state.datasetsReducers.tabValue); + const rhsOpen = useSelector(state => state.previewReducers.viewerControls.sidesOpen.RHS); + const isSelectedCompoundsTab = tabValue === 1; + + const dialogOpenedForInspirationWithId = + isSelectedCompoundsTab && Object.keys(inspirationLists).length > 0 + ? inspirationLists[Object.keys(inspirationLists)].length > 0 + ? inspirationLists[Object.keys(inspirationLists)][0] + : 0 + : 0; + useEffect(() => { + console.log( + `${uuid} useScrollToCompound useEffect - start (rhsOpen=${rhsOpen}, isSelectedCompoundsTab=${isSelectedCompoundsTab})` + ); + if (rhsOpen && isSelectedCompoundsTab) { + console.log( + `${uuid} useScrollToCompound useEffect - rhs is opened and selected compounds tab is selected (isSelectedDatasetScrolled=${isSelectedDatasetScrolled})` + ); + if (!isSelectedDatasetScrolled) { + console.log( + `${uuid} useScrollToCompound useEffect - selected compounds are not scrolled yet (isInspirationsDialogOpened=${isInspirationsDialogOpened}, dialogOpenedForInspirationWithId=${dialogOpenedForInspirationWithId}, isItForSelectedCompoundsList=${isItForSelectedCompoundsList})` + ); + if (isInspirationsDialogOpened && dialogOpenedForInspirationWithId && isItForSelectedCompoundsList) { + console.log(`${uuid} useScrollToCompound useEffect - scrolling because inspirations dialog is opened`); + for (let i = 0; i < moleculesObjectIDListOfCompoundsToBuy?.length; i++) { + const molecule = moleculesObjectIDListOfCompoundsToBuy[i].molecule; + + if (molecule.id === dialogOpenedForInspirationWithId) { + console.log(`${uuid} useScrollToCompound useEffect - found molecule to scroll to`); + // setCurrentPage(i / moleculesPerPage + 1); + setScrollToMoleculeId(molecule.id); + break; + } + } + } else { + console.log(`${uuid} useScrollToCompound useEffect - scrolling because selected compounds are selected`); + for (let i = 0; i < moleculesObjectIDListOfCompoundsToBuy?.length; i++) { + const molecule = moleculesObjectIDListOfCompoundsToBuy[i].molecule; + + if (molecule.isCustomPdb) { + if ( + ligandList?.includes(molecule.id) || + proteinListDataset?.includes(molecule.id) || + complexListDataset?.includes(molecule.id) || + surfaceListDataset?.includes(molecule.id) + ) { + console.log(`${uuid} useScrollToCompound useEffect - found molecule to scroll to - custom pdb`); + // setCurrentPage(i / moleculesPerPage + 1); + setScrollToMoleculeId(molecule.id); + break; + } + } else { + if ( + ligandList?.includes(molecule.id) || + proteinList?.includes(molecule.id) || + complexList?.includes(molecule.id) || + surfaceList?.includes(molecule.id) + ) { + console.log( + `${uuid} useScrollToCompound useEffect - found molecule to scroll to molecule.id: ${ + molecule.id + } molecule: ${JSON.stringify(molecule)}` + ); + // setCurrentPage(i / moleculesPerPage + 1); + setScrollToMoleculeId(molecule.id); + console.log( + `${uuid} useScrollToCompound useEffect - found molecule to scroll to - after setScrollToMoleculeId to value ${molecule.id}` + ); + break; + } + } + } + } + console.log(`${uuid} useScrollToCompound useEffect - end - setting selected dataset scrolled`); + dispatch(setSelectedDatasetScrolled(true)); + } + } + console.log(`${uuid} useScrollToCompound useEffect - end`); + }, [ + complexList, + complexListDataset, + dialogOpenedForInspirationWithId, + dispatch, + isInspirationsDialogOpened, + isItForSelectedCompoundsList, + isSelectedCompoundsTab, + isSelectedDatasetScrolled, + ligandList, + moleculesObjectIDListOfCompoundsToBuy, + proteinList, + proteinListDataset, + rhsOpen, + surfaceList, + surfaceListDataset, + uuid + ]); + + useEffect(() => { + console.log(`${uuid} useScrollToCompound useEffect scrolling - scrollToMoleculeId=${scrollToMoleculeId}`); + // console.log( + // `${uuid} useScrollToCompound useEffect scrolling - moleculeViewRefs=${JSON.stringify(moleculeViewRefs)}` + // ); if (scrollToMoleculeId !== null) { const node = moleculeViewRefs[scrollToMoleculeId]; if (node) { setScrollToMoleculeId(null); if (!elementIsVisibleInViewport(node)) { + console.log(`${uuid} useScrollToCompound useEffect scrolling - scrolling to molecule ${scrollToMoleculeId}`); setTimeout(() => { node.scrollIntoView(); }); + } else { + console.log(`${uuid} useScrollToCompound useEffect scrolling - node is visible - skipping scrolling`); } + } else { + console.log(`${uuid} useScrollToCompound useEffect scrolling - node is null - skipping scrolling`); } + } else { + console.log( + `${uuid} useScrollToCompound useEffect scrolling - scrollToMoleculeId is either undefined or null or 0` + ); } - }, [moleculeViewRefs, scrollToMoleculeId]); + }, [moleculeViewRefs, scrollToMoleculeId, uuid]); const elementIsVisibleInViewport = (el, partiallyVisible = false) => { const { top, left, bottom, right } = el.getBoundingClientRect(); @@ -27,19 +171,15 @@ export const useScrollToCompound = () => { : top >= 0 && left >= 0 && bottom <= innerHeight && right <= innerWidth; }; - // Used to attach the ref of DOM nodes. - // const addMoleculeViewRef = useCallback((moleculeId, node) => { - // if (moleculeId && node) { - // setMoleculeViewRefs(prevRefs => ({ - // ...prevRefs, - // [moleculeId]: node - // })); - // } - // }, []); - const addMoleculeViewRef = useCallback((moleculeId, node) => { + // console.log(`useScrollToCompound addMoleculeViewRef - moleculeId=${moleculeId} and node=${node ? 'yes' : 'no'}`); setMoleculeViewRefs(prevRefs => { - if (prevRefs.hasOwnProperty(moleculeId)) return prevRefs; + if (prevRefs.hasOwnProperty(moleculeId) || !node) return prevRefs; + console.log( + `useScrollToCompound addMoleculeViewRef - adding this data moleculeId=${moleculeId} and node=${ + node ? 'yes' : 'no' + }` + ); return { ...prevRefs, [moleculeId]: node diff --git a/js/components/datasets/useScrollToSelected.js b/js/components/datasets/useScrollToSelected.js index e1c796277..6857411e6 100644 --- a/js/components/datasets/useScrollToSelected.js +++ b/js/components/datasets/useScrollToSelected.js @@ -11,45 +11,81 @@ export const useScrollToSelected = (datasetID, moleculesPerPage, setCurrentPage) const joinedMoleculeLists = useSelector(state => getJoinedMoleculeLists(datasetID, state), shallowEqual); const compoundsToBuyList = useSelector(state => state.datasetsReducers.compoundsToBuyDatasetMap[datasetID]); + const ligands = useSelector(state => state.datasetsReducers.ligandLists[datasetID]); + const proteins = useSelector(state => state.datasetsReducers.proteinLists[datasetID]); const complexes = useSelector(state => state.datasetsReducers.complexLists[datasetID]); const surfaces = useSelector(state => state.datasetsReducers.surfaceLists[datasetID]); + + const proteinList = useSelector(state => state.selectionReducers.proteinList); + const complexList = useSelector(state => state.selectionReducers.complexList); + const surfaceList = useSelector(state => state.selectionReducers.surfaceList); + const scrollFired = useSelector(state => state.datasetsReducers.datasetScrolledMap[datasetID]); const rhsOpen = useSelector(state => state.previewReducers.viewerControls.sidesOpen.RHS); + const isInspirationsDialogOpened = useSelector(state => state.datasetsReducers.isOpenInspirationDialog); + const inspirationLists = useSelector(state => state.datasetsReducers.inspirationLists); + const isItForSelectedCompoundsList = useSelector( + state => state.datasetsReducers.inspirationsDialogOpenedForSelectedCompound + ); + + const dialogOpenedForInspirationWithId = + inspirationLists.hasOwnProperty(datasetID) && inspirationLists[datasetID]?.length > 0 + ? inspirationLists[datasetID][0] + : 0; const [moleculeViewRefs, setMoleculeViewRefs] = useState({}); const [scrollToMoleculeId, setScrollToMoleculeId] = useState(null); + const tabValue = useSelector(state => state.datasetsReducers.tabValue); + const isComputedDatasetsTab = tabValue > 1; + // 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 (rhsOpen) { + if (rhsOpen && isComputedDatasetsTab) { if (!scrollFired) { - if ( - compoundsToBuyList?.length || - ligands?.length || - proteins?.length || - complexes?.length || - surfaces?.length - ) { - for (let i = 0; i < joinedMoleculeLists.length; i++) { + if (isInspirationsDialogOpened && dialogOpenedForInspirationWithId && !isItForSelectedCompoundsList) { + for (let i = 0; i < joinedMoleculeLists?.length; i++) { const molecule = joinedMoleculeLists[i]; - if ( - compoundsToBuyList?.includes(molecule.id) || - ligands?.includes(molecule.id) || - proteins?.includes(molecule.id) || - complexes?.includes(molecule.id) || - surfaces?.includes(molecule.id) - ) { + if (molecule.id === dialogOpenedForInspirationWithId) { setCurrentPage(i / moleculesPerPage + 1); setScrollToMoleculeId(molecule.id); break; } } + } else { + for (let i = 0; i < joinedMoleculeLists?.length; i++) { + const molecule = joinedMoleculeLists[i]; + + if (molecule.isCustomPdb) { + if ( + ligands?.includes(molecule.id) || + proteins?.includes(molecule.id) || + complexes?.includes(molecule.id) || + surfaces?.includes(molecule.id) + ) { + setCurrentPage(i / moleculesPerPage + 1); + setScrollToMoleculeId(molecule.id); + break; + } + } else { + if ( + ligands?.includes(molecule.id) || + proteinList?.includes(molecule.id) || + complexList?.includes(molecule.id) || + surfaceList?.includes(molecule.id) + ) { + setCurrentPage(i / moleculesPerPage + 1); + setScrollToMoleculeId(molecule.id); + break; + } + } + } } } @@ -67,7 +103,14 @@ export const useScrollToSelected = (datasetID, moleculesPerPage, setCurrentPage) ligands, proteins, complexes, - surfaces + surfaces, + dialogOpenedForInspirationWithId, + isItForSelectedCompoundsList, + isInspirationsDialogOpened, + proteinList, + complexList, + surfaceList, + isComputedDatasetsTab ]); // Second pass, once the list of molecules is displayed and the refs to their DOM nodes have been @@ -106,7 +149,7 @@ export const useScrollToSelected = (datasetID, 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/molecule/observationCmpList.js b/js/components/preview/molecule/observationCmpList.js index 8c9281551..7d64179ea 100644 --- a/js/components/preview/molecule/observationCmpList.js +++ b/js/components/preview/molecule/observationCmpList.js @@ -45,7 +45,8 @@ import { withDisabledMoleculesNglControlButtons, removeSelectedTypesInHitNavigator, selectAllHits, - autoHideTagEditorDialogsOnScroll + autoHideTagEditorDialogsOnScroll, + selectAllVisibleObservations } from './redux/dispatchActions'; import { DEFAULT_FILTER, PREDEFINED_FILTERS } from '../../../reducers/selection/constants'; import { Edit, FilterList } from '@material-ui/icons'; @@ -58,7 +59,9 @@ import { setMoleculeForTagEdit, setObservationsForLHSCmp, setOpenObservationsDialog, - setLHSCompoundsInitialized + setLHSCompoundsInitialized, + setPoseIdForObservationsDialog, + setObservationDialogAction } from '../../../reducers/selection/actions'; import { initializeFilter } from '../../../reducers/selection/dispatchActions'; import * as listType from '../../../constants/listTypes'; @@ -725,6 +728,9 @@ export const ObservationCmpList = memo(({ hideProjects }) => { if (!cmp) { dispatch(setObservationsForLHSCmp([])); dispatch(setOpenObservationsDialog(false)); + dispatch(setPoseIdForObservationsDialog(0)); + + // dispatch(setObservationDialogAction(0, [], false)); } } }, [isObservationDialogOpen, filteredLHSCompoundsList, observationsForLHSCmp, dispatch]); @@ -1135,7 +1141,7 @@ export const ObservationCmpList = memo(({ hideProjects }) => { [classes.contColButtonHalfSelected]: false })} onClick={() => { - dispatch(selectAllHits([], null, false)); + dispatch(selectAllVisibleObservations([], null, false)); // setSelectDisplayedHitsPressed(!selectDisplayedHitsPressed); }} disabled={false} @@ -1154,7 +1160,7 @@ export const ObservationCmpList = memo(({ hideProjects }) => { [classes.contColButtonHalfSelected]: false })} onClick={() => { - dispatch(selectAllHits(uniqueSelectedMoleculeForHitNavigator, null, false)); + dispatch(selectAllVisibleObservations(uniqueSelectedMoleculeForHitNavigator, null, false)); // setSelectDisplayedHitsPressed(!selectDisplayedHitsPressed); }} disabled={false} diff --git a/js/components/preview/molecule/observationCmpView/observationCmpView.js b/js/components/preview/molecule/observationCmpView/observationCmpView.js index 9a7834c84..9ecbd001b 100644 --- a/js/components/preview/molecule/observationCmpView/observationCmpView.js +++ b/js/components/preview/molecule/observationCmpView/observationCmpView.js @@ -2,7 +2,7 @@ * Created by abradley on 14/03/2018. */ -import React, { memo, useEffect, useState, useRef, useContext, useCallback, forwardRef } from 'react'; +import React, { memo, useEffect, useState, useRef, useContext, useCallback } from 'react'; import { useDispatch, useSelector } from 'react-redux'; import { Button, Grid, makeStyles, Tooltip, IconButton, Popper, Item, CircularProgress } from '@material-ui/core'; import { Panel } from '../../../common'; @@ -380,408 +380,339 @@ export const img_data_init = ` - {modifiedObjects.length < 2 ? ( - - {modifiedObjects.map((item, index) => - index < allTagsLength ? ( - -
{item.tag}
- - ) : ( -
- ) - )} - {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( -
+ return result; + }); + + const allTagsLength = allData.length > 9 ? 9 : allData.length; + const popperPadding = 250; // allTagsLength > 1 ? 250 : 420; + + return modifiedObjects?.length > 0 ? ( + + + {modifiedObjects.length < 2 ? ( + + {modifiedObjects.map((item, index) => + index < allTagsLength ? ( + +
{item.tag}
+
) : ( -
- - { - if (tagEditModalOpenNew) { - setTagEditModalOpenNew(false); - dispatch(setTagEditorOpen(!tagEditModalOpenNew)); - dispatch(setMoleculeForTagEdit([])); - dispatch(setIsLHSCmpTagEdit(false)); - } else { - dispatch(setIsLHSCmpTagEdit(true)); - setTagEditModalOpenNew(true); - dispatch(setMoleculeForTagEdit(observations.map(obs => obs.id))); - dispatch(setTagEditorOpen(true)); - if (setRef) { - setRef(ref.current); - } - } - }} - style={{ padding: 0, paddingBottom: 3, marginRight: 5, position: 'right' }} - // className={classes.editIcon} - > - - - - - -
- )} -
- ) : ( - -
- {modifiedObjects.map((item, index) => - index < allTagsLength ? ( - -
{item.tag}
-
- ) : ( -
- ) - )} -
-
- {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( -
- ) : ( +
+ ) + )} + {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( +
+ ) : ( +
+ - + - )} -
- - )} - - {tagEditorOpen === false ? ( - - - - - {allData.map((item, index) => ( - -
{item.tag_prefix ? `${item.tag_prefix} - ${item.tag}` : item.tag}
-
- ))}
-
-
-
+
+ )} +
) : ( -
+ +
+ {modifiedObjects.map((item, index) => + index < allTagsLength ? ( + +
{item.tag}
+
+ ) : ( +
+ ) + )} +
+
+ {DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( +
+ ) : ( + { + if (tagEditModalOpenNew) { + setTagEditModalOpenNew(false); + dispatch(setTagEditorOpen(!tagEditModalOpenNew)); + dispatch(setMoleculeForTagEdit([])); + dispatch(setIsLHSCmpTagEdit(false)); + } else { + dispatch(setIsLHSCmpTagEdit(true)); + setTagEditModalOpenNew(true); + dispatch(setMoleculeForTagEdit(observations.map(obs => obs.id))); + dispatch(setTagEditorOpen(true)); + if (setRef) { + setRef(ref.current); + } + } + }} + style={{ padding: 0, paddingBottom: 3, paddingRight: 5, cursor: 'pointer' }} + // className={classes.editIcon} + > + + + + + )} +
+
)} -
- ) : DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( - <> - ) : ( - - { - if (tagEditModalOpenNew) { - setTagEditModalOpenNew(false); - dispatch(setTagEditorOpen(!tagEditModalOpenNew)); - dispatch(setMoleculeForTagEdit([])); - dispatch(setIsLHSCmpTagEdit(false)); - } else { - dispatch(setIsLHSCmpTagEdit(true)); - setTagEditModalOpenNew(true); - dispatch(setMoleculeForTagEdit(observations.map(obs => obs.id))); - dispatch(setTagEditorOpen(true)); - if (setRef) { - setRef(ref.current); - } - } - }} - style={{ padding: 0, paddingBottom: 8, paddingRight: 5, cursor: 'pointer' }} - // className={classes.editIcon} + + {tagEditorOpen === false ? ( + - - - - - - ); - }, [ - // classes.editIcon, - classes.editButtonIcon, - classes.paper, - classes.popover, - classes.tagPopover, - dispatch, - getCanonSitesTagCategory, - getConformerSitesTagCategory, - observations, - open, - resolveTagBackgroundColor, - resolveTagForegroundColor, - setRef, - tagCategories, - tagEditModalOpenNew, - tagEditorOpen, - tagList, - tagPopoverOpen - ]); - - // componentDidMount - useEffect(() => { - const obs = getMainObservation(); - dispatch(getMolImage(obs.id, MOL_TYPE.HIT, imageWidth, imageHeight)).then(i => { - setImg_data(i); - }); - }, [data.id, data.smiles, imageHeight, imageWidth, dispatch, getMainObservation]); - - useEffect(() => { - dispatch(getQualityInformation(data)); - }, [data, dispatch]); - - const svg_image = ( - + + + + {allData.map((item, index) => ( + +
{item.tag_prefix ? `${item.tag_prefix} - ${item.tag}` : item.tag}
+
+ ))} +
+
+
+ + ) : ( +
+ )} + + ) : DJANGO_CONTEXT['username'] === 'NOT_LOGGED_IN' ? ( + <> + ) : ( + + { + if (tagEditModalOpenNew) { + setTagEditModalOpenNew(false); + dispatch(setTagEditorOpen(!tagEditModalOpenNew)); + dispatch(setMoleculeForTagEdit([])); + dispatch(setIsLHSCmpTagEdit(false)); + } else { + dispatch(setIsLHSCmpTagEdit(true)); + setTagEditModalOpenNew(true); + dispatch(setMoleculeForTagEdit(observations.map(obs => obs.id))); + dispatch(setTagEditorOpen(true)); + if (setRef) { + setRef(ref.current); + } + } + }} + style={{ padding: 0, paddingBottom: 8, paddingRight: 5, cursor: 'pointer' }} + // className={classes.editIcon} + > + + + + + ); - // Here add the logic that updates this based on the information - // const refinement = ; - const selected_style = { - backgroundColor: colourToggle - }; - const not_selected_style = {}; - const current_style = - isLigandOn || isProteinOn || isComplexOn || isSurfaceOn || isDensityOn || isVectorOn - ? selected_style - : not_selected_style; - - const addNewLigand = (skipTracking = false) => { - // if (selectMoleculeSite) { - // selectMoleculeSite(data.site); - // } - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - const color = getRandomColor(firstObs); - await dispatch(addLigand(stage, firstObs, color, false, true, skipTracking)); - } - }) - ); - }; - - const removeSelectedLigand = (skipTracking = false) => { - const selectedObs = getAllObservationsSelectedInList(fragmentDisplayList); - for (const obs of selectedObs) { - dispatch(removeLigand(stage, obs, skipTracking)); - } - selectedAll.current = false; - }; + }, [ + // classes.editIcon, + classes.editButtonIcon, + classes.paper, + classes.popover, + classes.tagPopover, + dispatch, + getCanonSitesTagCategory, + getConformerSitesTagCategory, + observations, + open, + resolveTagBackgroundColor, + resolveTagForegroundColor, + setRef, + tagCategories, + tagEditModalOpenNew, + tagEditorOpen, + tagList, + tagPopoverOpen + ]); + + // componentDidMount + useEffect(() => { + const obs = getMainObservation(); + dispatch(getMolImage(obs.id, MOL_TYPE.HIT, imageWidth, imageHeight)).then(i => { + setImg_data(i); + }); + }, [data.id, data.smiles, imageHeight, imageWidth, dispatch, getMainObservation]); + + useEffect(() => { + dispatch(getQualityInformation(data)); + }, [data, dispatch]); + + const svg_image = ( + + ); + // Here add the logic that updates this based on the information + // const refinement = ; + const selected_style = { + backgroundColor: colourToggle + }; + const not_selected_style = {}; + const current_style = + isLigandOn || isProteinOn || isComplexOn || isSurfaceOn || isDensityOn || isVectorOn + ? selected_style + : not_selected_style; + + const addNewLigand = (skipTracking = false) => { + // if (selectMoleculeSite) { + // selectMoleculeSite(data.site); + // } + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + const color = getRandomColor(firstObs); + await dispatch(addLigand(stage, firstObs, color, false, true, skipTracking)); + } + }) + ); + }; - const [loadingAll, setLoadingAll] = useState(false); - const [loadingLigand, setLoadingLigand] = useState(false); + const removeSelectedLigand = (skipTracking = false) => { + const selectedObs = getAllObservationsSelectedInList(fragmentDisplayList); + for (const obs of selectedObs) { + dispatch(removeLigand(stage, obs, skipTracking)); + } + selectedAll.current = false; + }; - const onLigand = calledFromSelectAll => { - setLoadingLigand(true); - if (calledFromSelectAll === true && selectedAll.current === true) { - if (isLigandOn === false) { - addNewLigand(calledFromSelectAll); - } - } else if (calledFromSelectAll && selectedAll.current === false) { - removeSelectedLigand(calledFromSelectAll); - } else if (!calledFromSelectAll) { - if (isLigandOn === false) { - addNewLigand(); - } else { - removeSelectedLigand(); - } - } - setLoadingLigand(false); - }; + const [loadingAll, setLoadingAll] = useState(false); + const [loadingLigand, setLoadingLigand] = useState(false); - const removeSelectedProtein = (skipTracking = false) => { - const selectedObs = getAllObservationsSelectedInList(proteinList); - for (const obs of selectedObs) { - dispatch(removeHitProtein(stage, obs, colourToggle, skipTracking)); + const onLigand = calledFromSelectAll => { + setLoadingLigand(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isLigandOn === false) { + addNewLigand(calledFromSelectAll); } - selectedAll.current = false; - }; - - const addNewProtein = (skipTracking = false) => { - // if (selectMoleculeSite) { - // selectMoleculeSite(data.site); - // } - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'protein', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - const color = getRandomColor(firstObs); - await dispatch(addHitProtein(stage, firstObs, color, true, skipTracking)); - } - }) - ); - }; - - const [loadingProtein, setLoadingProtein] = useState(false); - - const onProtein = calledFromSelectAll => { - setLoadingProtein(true); - if (calledFromSelectAll === true && selectedAll.current === true) { - if (isProteinOn === false) { - addNewProtein(calledFromSelectAll); - } - } else if (calledFromSelectAll && selectedAll.current === false) { - removeSelectedProtein(calledFromSelectAll); - } else if (!calledFromSelectAll) { - if (isProteinOn === false) { - addNewProtein(); - } else { - removeSelectedProtein(); - } + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedLigand(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isLigandOn === false) { + addNewLigand(); + } else { + removeSelectedLigand(); } - setLoadingProtein(false); - }; + } + setLoadingLigand(false); + }; - const removeSelectedComplex = (skipTracking = false) => { - const selectedObs = getAllObservationsSelectedInList(complexList); - for (const obs of selectedObs) { - dispatch(removeComplex(stage, obs, colourToggle, skipTracking)); - } - selectedAll.current = false; - }; - - const addNewComplex = (skipTracking = false) => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'complex', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - const color = getRandomColor(firstObs); - await dispatch(addComplex(stage, firstObs, color, skipTracking)); - } - }) - ); - }; - - const [loadingComplex, setLoadingComplex] = useState(false); - - const onComplex = calledFromSelectAll => { - setLoadingComplex(true); - if (calledFromSelectAll === true && selectedAll.current === true) { - if (isComplexOn === false) { - addNewComplex(calledFromSelectAll); - } - } else if (calledFromSelectAll && selectedAll.current === false) { - removeSelectedComplex(calledFromSelectAll); - } else if (!calledFromSelectAll) { - if (isComplexOn === false) { - addNewComplex(); - } else { - removeSelectedComplex(); + const removeSelectedProtein = (skipTracking = false) => { + const selectedObs = getAllObservationsSelectedInList(proteinList); + for (const obs of selectedObs) { + dispatch(removeHitProtein(stage, obs, colourToggle, skipTracking)); + } + selectedAll.current = false; + }; + + const addNewProtein = (skipTracking = false) => { + // if (selectMoleculeSite) { + // selectMoleculeSite(data.site); + // } + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'protein', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + const color = getRandomColor(firstObs); + await dispatch(addHitProtein(stage, firstObs, color, true, skipTracking)); } - } - setLoadingComplex(false); - }; + }) + ); + }; + + const [loadingProtein, setLoadingProtein] = useState(false); - const removeSelectedSurface = () => { - const selectedObs = getAllObservationsSelectedInList(surfaceList); - for (const obs of selectedObs) { - dispatch(removeSurface(stage, obs, colourToggle)); + const onProtein = calledFromSelectAll => { + setLoadingProtein(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isProteinOn === false) { + addNewProtein(calledFromSelectAll); } - }; - - const addNewSurface = () => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'surface', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - const color = getRandomColor(firstObs); - await dispatch(addSurface(stage, firstObs, color)); - } - }) - ); - }; - - const [loadingSurface, setLoadingSurface] = useState(false); - - const onSurface = () => { - setLoadingSurface(true); - if (isSurfaceOn === false) { - addNewSurface(); + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedProtein(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isProteinOn === false) { + addNewProtein(); } else { - removeSelectedSurface(); + removeSelectedProtein(); } - setLoadingSurface(false); - }; + } + setLoadingProtein(false); + }; - const removeSelectedDensity = () => { - const firstObs = getFirstObservationWithDensity(); - dispatch(removeDensity(stage, firstObs, colourToggle, false)); - }; - - const addNewDensityCustom = async () => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'density', async () => { - const firstObs = getFirstObservationWithDensity(); - await dispatch(addDensityCustomView(stage, firstObs, colourToggle, isWireframeStyle)); - }) - ); - }; - - const addNewDensity = async () => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { - await dispatch( - withDisabledMoleculeNglControlButton(currentID, 'density', async () => { - const firstObs = getFirstObservationWithDensity(); - await dispatch(addDensity(stage, firstObs, colourToggle, isWireframeStyle)); - }) - ); - }) - ); - }; - - const [loadingDensity, setLoadingDensity] = useState(false); - - const onDensity = () => { - setLoadingDensity(true); - if (isDensityOn === false && isDensityCustomOn === false) { - const firstObs = getFirstObservationWithDensity(); - dispatch(getDensityMapData(firstObs)).then(r => { - if (r) { - dispatch(setDensityModalOpen(true)); - } else { - addNewDensity(); - } - }); - } else if (isDensityCustomOn === false) { - addNewDensityCustom(); - } else { - removeSelectedDensity(); - } - setLoadingDensity(false); - }; + const removeSelectedComplex = (skipTracking = false) => { + const selectedObs = getAllObservationsSelectedInList(complexList); + for (const obs of selectedObs) { + dispatch(removeComplex(stage, obs, colourToggle, skipTracking)); + } + selectedAll.current = false; + }; + + const addNewComplex = (skipTracking = false) => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'complex', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + const color = getRandomColor(firstObs); + await dispatch(addComplex(stage, firstObs, color, skipTracking)); + } + }) + ); + }; - const removeSelectedQuality = () => { - const selectedObs = getAllObservationsSelectedInList(qualityList); - for (const obs of selectedObs) { - dispatch(removeQuality(stage, obs, colourToggle)); - } - }; - - const addNewQuality = () => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - const color = getRandomColor(firstObs); - await dispatch(addQuality(stage, firstObs, color)); - } - }) - ); - }; - - const onQuality = () => { - if (isQualityOn === false) { - addNewQuality(); - } else { - removeSelectedQuality(); - } - }; + const [loadingComplex, setLoadingComplex] = useState(false); - const removeSelectedVector = () => { - const selectedObs = getAllObservationsSelectedInList(vectorOnList); - for (const obs of selectedObs) { - dispatch(removeVector(stage, obs)); + const onComplex = calledFromSelectAll => { + setLoadingComplex(true); + if (calledFromSelectAll === true && selectedAll.current === true) { + if (isComplexOn === false) { + addNewComplex(calledFromSelectAll); } - }; - - const addNewVector = () => { - dispatch( - withDisabledMoleculeNglControlButton(currentID, 'vector', async () => { - const firstObs = getMainObservation(); - if (firstObs) { - await dispatch(addVector(stage, firstObs)); - } - }) - ); - }; - - const [loadingVector, setLoadingVector] = useState(false); - - const onVector = () => { - setLoadingVector(true); - if (isVectorOn === false) { - addNewVector(); + } else if (calledFromSelectAll && selectedAll.current === false) { + removeSelectedComplex(calledFromSelectAll); + } else if (!calledFromSelectAll) { + if (isComplexOn === false) { + addNewComplex(); } else { - removeSelectedVector(); + removeSelectedComplex(); } - setLoadingVector(false); - }; + } + setLoadingComplex(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' + const removeSelectedSurface = () => { + const selectedObs = getAllObservationsSelectedInList(surfaceList); + for (const obs of selectedObs) { + dispatch(removeSurface(stage, obs, colourToggle)); + } + }; + + const addNewSurface = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'surface', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + const color = getRandomColor(firstObs); + await dispatch(addSurface(stage, firstObs, color)); + } + }) + ); + }; + + const [loadingSurface, setLoadingSurface] = useState(false); + + const onSurface = () => { + setLoadingSurface(true); + if (isSurfaceOn === false) { + addNewSurface(); + } else { + removeSelectedSurface(); + } + setLoadingSurface(false); + }; + + const removeSelectedDensity = () => { + const firstObs = getFirstObservationWithDensity(); + dispatch(removeDensity(stage, firstObs, colourToggle, false)); + }; + + const addNewDensityCustom = async () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'density', async () => { + const firstObs = getFirstObservationWithDensity(); + await dispatch(addDensityCustomView(stage, firstObs, colourToggle, isWireframeStyle)); + }) + ); + }; + + const addNewDensity = async () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + await dispatch( + withDisabledMoleculeNglControlButton(currentID, 'density', async () => { + const firstObs = getFirstObservationWithDensity(); + await dispatch(addDensity(stage, firstObs, colourToggle, isWireframeStyle)); + }) + ); + }) + ); + }; + + const [loadingDensity, setLoadingDensity] = useState(false); + + const onDensity = () => { + setLoadingDensity(true); + if (isDensityOn === false && isDensityCustomOn === false) { + const firstObs = getFirstObservationWithDensity(); + dispatch(getDensityMapData(firstObs)).then(r => { + if (r) { + dispatch(setDensityModalOpen(true)); + } else { + addNewDensity(); + } }); - }; + } else if (isDensityCustomOn === false) { + addNewDensityCustom(); + } else { + removeSelectedDensity(); + } + setLoadingDensity(false); + }; - // 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 removeSelectedQuality = () => { + const selectedObs = getAllObservationsSelectedInList(qualityList); + for (const obs of selectedObs) { + dispatch(removeQuality(stage, obs, colourToggle)); } - const moleculeTitleTruncated = moleculeTitle.substring(0, 20) + (moleculeTitle.length > 20 ? '...' : ''); + }; + + const addNewQuality = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'ligand', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + const color = getRandomColor(firstObs); + await dispatch(addQuality(stage, firstObs, color)); + } + }) + ); + }; - const [isNameCopied, setNameCopied] = useClipboard(moleculeTitle, { successDuration: 5000 }); + const onQuality = () => { + if (isQualityOn === false) { + addNewQuality(); + } else { + removeSelectedQuality(); + } + }; - const moleculeLPCControlButtonDisabled = ['ligand', 'protein', 'complex'].some( - type => disableMoleculeNglControlButtons[type] + const removeSelectedVector = () => { + const selectedObs = getAllObservationsSelectedInList(vectorOnList); + for (const obs of selectedObs) { + dispatch(removeVector(stage, obs)); + } + }; + + const addNewVector = () => { + dispatch( + withDisabledMoleculeNglControlButton(currentID, 'vector', async () => { + const firstObs = getMainObservation(); + if (firstObs) { + await dispatch(addVector(stage, firstObs)); + } + }) ); + }; - const groupMoleculeLPCControlButtonDisabled = disableL || disableP || disableC; + const [loadingVector, setLoadingVector] = useState(false); - return ( - <> - { - if (outsideRef) { - outsideRef(data.id, node); - } - ref.current = node; - }} - container - justifyContent="space-between" - direction="row" - className={classes.container} - wrap="nowrap" - // ref={ref} - > - {/* Site number */} - - - { - 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?.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)}`); } }