diff --git a/js/components/datasets/redux/actions.js b/js/components/datasets/redux/actions.js index 3ec64c23c..0b7a79f33 100644 --- a/js/components/datasets/redux/actions.js +++ b/js/components/datasets/redux/actions.js @@ -623,3 +623,35 @@ export const setUpdatedDatasets = updatedDataset => ({ type: constants.SET_UPDATED_DATASETS, payload: { updatedDataset } }); + +export const setToBeDisplayedListForDataset = (datasetID, toBeDisplayedList) => { + return { + type: constants.SET_TO_BE_DISPLAYED_LIST_DATASET, + toBeDisplayedList: toBeDisplayedList, + datasetID: datasetID + }; +}; + +export const appendToBeDisplayedListForDataset = (datasetID, item) => { + return { + type: constants.APPEND_TO_BE_DISPLAYED_LIST_DATASET, + item: item, + datasetID: datasetID + }; +}; + +export const removeFromToBeDisplayedListForDataset = (datasetID, item) => { + return { + type: constants.REMOVE_FROM_TO_BE_DISPLAYED_LIST_DATASET, + item: item, + datasetID: datasetID + }; +}; + +export const updateInToBeDisplayedListForDataset = (datasetID, item) => { + return { + type: constants.UPDATE_IN_TO_BE_DISPLAYED_LIST_DATASET, + item: item, + datasetID: datasetID + }; +}; diff --git a/js/components/datasets/redux/constants.js b/js/components/datasets/redux/constants.js index 3e96418fe..b355e07be 100644 --- a/js/components/datasets/redux/constants.js +++ b/js/components/datasets/redux/constants.js @@ -122,7 +122,12 @@ export const constants = { 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' + SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND: prefix + 'SET_INSPIRATION_DIALOG_OPENED_FOR_SELECTED_COMPOUND', + + SET_TO_BE_DISPLAYED_LIST_DATASET: prefix + 'SET_TO_BE_DISPLAYED_LIST_DATASET', + APPEND_TO_BE_DISPLAYED_LIST_DATASET: prefix + 'APPEND_TO_BE_DISPLAYED_LIST_DATASET', + REMOVE_FROM_TO_BE_DISPLAYED_LIST_DATASET: prefix + 'REMOVE_FROM_TO_BE_DISPLAYED_LIST_DATASET', + UPDATE_IN_TO_BE_DISPLAYED_LIST_DATASET: prefix + 'UPDATE_IN_TO_BE_DISPLAYED_LIST_DATASET' }; export const COUNT_OF_VISIBLE_SCORES = 7; diff --git a/js/components/datasets/redux/dispatchActions.js b/js/components/datasets/redux/dispatchActions.js index 18f8eab34..64ea970e9 100644 --- a/js/components/datasets/redux/dispatchActions.js +++ b/js/components/datasets/redux/dispatchActions.js @@ -37,7 +37,9 @@ import { removeDataset, appendCompoundToSelectedCompoundsByDataset, setDatasetIterator, - setSelectedCompoundsIterator + setSelectedCompoundsIterator, + appendToBeDisplayedListForDataset, + updateInToBeDisplayedListForDataset } from './actions'; import { base_url } from '../../routes/constants'; import { @@ -78,6 +80,7 @@ import { getRepresentationsByType } from '../../nglView/generatingObjects'; import { getRandomColor } from '../../preview/molecule/utils/color'; import { isCompoundFromVectorSelector } from '../../preview/compounds/redux/dispatchActions'; import { getCompoundById } from '../../../utils/genericDispatchActions'; +import { NGL_OBJECTS } from '../../../reducers/ngl/constants'; export const initializeDatasetFilter = datasetID => (dispatch, getState) => { const state = getState(); @@ -104,34 +107,26 @@ export const addDatasetHitProtein = ( skipTracking = false, representations = undefined ) => async dispatch => { - dispatch(appendProteinList(datasetID, generateMoleculeCompoundId(data), skipTracking)); - return dispatch( - loadObject({ - target: Object.assign( - { display_div: VIEWS.MAJOR_VIEW }, - generateHitProteinObject(data, colourToggle, base_url, datasetID) - ), - stage, - previousRepresentations: representations, - orientationMatrix: null + dispatch( + appendToBeDisplayedListForDataset(datasetID, { + type: NGL_OBJECTS.PROTEIN, + id: data.id, + display: true, + representations: representations, + datasetID: datasetID }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeDatasetHitProtein = (stage, data, colourToggle, datasetID, skipTracking = false) => dispatch => { dispatch( - deleteObject( - Object.assign( - { display_div: VIEWS.MAJOR_VIEW }, - generateHitProteinObject(data, colourToggle, base_url, datasetID) - ), - stage - ) + updateInToBeDisplayedListForDataset(datasetID, { + id: data.id, + display: false, + type: NGL_OBJECTS.PROTEIN, + datasetID: datasetID + }) ); - dispatch(removeFromProteinList(datasetID, generateMoleculeCompoundId(data), skipTracking)); }; export const addDatasetComplex = ( @@ -142,31 +137,26 @@ export const addDatasetComplex = ( skipTracking = false, representations = undefined ) => async dispatch => { - dispatch(appendComplexList(datasetID, generateMoleculeCompoundId(data), skipTracking)); - return dispatch( - loadObject({ - target: Object.assign( - { display_div: VIEWS.MAJOR_VIEW }, - generateComplexObject(data, colourToggle, base_url, datasetID) - ), - stage, - previousRepresentations: representations, - orientationMatrix: null + dispatch( + appendToBeDisplayedListForDataset(datasetID, { + type: NGL_OBJECTS.COMPLEX, + id: data.id, + display: true, + representations: representations, + datasetID: datasetID }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeDatasetComplex = (stage, data, colourToggle, datasetID, skipTracking = false) => dispatch => { dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateComplexObject(data, colourToggle, base_url, datasetID)), - stage - ) + updateInToBeDisplayedListForDataset(datasetID, { + id: data.id, + display: false, + type: NGL_OBJECTS.COMPLEX, + datasetID: datasetID + }) ); - dispatch(removeFromComplexList(datasetID, generateMoleculeCompoundId(data), skipTracking)); }; export const addDatasetSurface = ( @@ -176,31 +166,48 @@ export const addDatasetSurface = ( datasetID, representations = undefined ) => async dispatch => { - dispatch(appendSurfaceList(datasetID, generateMoleculeCompoundId(data))); - return dispatch( - loadObject({ - target: Object.assign( - { display_div: VIEWS.MAJOR_VIEW }, - generateSurfaceObject(data, colourToggle, base_url, datasetID) - ), - stage, - previousRepresentations: representations, - orientationMatrix: null + dispatch( + appendToBeDisplayedListForDataset(datasetID, { + type: NGL_OBJECTS.SURFACE, + id: data.id, + display: true, + representations: representations, + datasetID: datasetID }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); + // dispatch(appendSurfaceList(datasetID, generateMoleculeCompoundId(data))); + // return dispatch( + // loadObject({ + // target: Object.assign( + // { display_div: VIEWS.MAJOR_VIEW }, + // generateSurfaceObject(data, colourToggle, base_url, datasetID) + // ), + // stage, + // previousRepresentations: representations, + // orientationMatrix: null + // }) + // ).finally(() => { + // const currentOrientation = stage.viewerControls.getOrientation(); + // dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + // }); }; export const removeDatasetSurface = (stage, data, colourToggle, datasetID) => dispatch => { dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url, datasetID)), - stage - ) + updateInToBeDisplayedListForDataset(datasetID, { + id: data.id, + display: false, + type: NGL_OBJECTS.SURFACE, + datasetID: datasetID + }) ); - dispatch(removeFromSurfaceList(datasetID, generateMoleculeCompoundId(data))); + // dispatch( + // deleteObject( + // Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url, datasetID)), + // stage + // ) + // ); + // dispatch(removeFromSurfaceList(datasetID, generateMoleculeCompoundId(data))); }; export const addDatasetLigand = ( @@ -211,42 +218,26 @@ export const addDatasetLigand = ( skipTracking = false, representations = undefined ) => async (dispatch, getState) => { - dispatch(appendLigandList(datasetID, generateMoleculeCompoundId(data), skipTracking)); - console.count(`Grabbed orientation before loading dataset ligand`); - const currentOrientation = stage.viewerControls.getOrientation(); - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data, colourToggle, datasetID)), - stage, - previousRepresentations: representations, - markAsRightSideLigand: true + dispatch( + appendToBeDisplayedListForDataset(datasetID, { + type: NGL_OBJECTS.LIGAND, + id: data.id, + display: true, + representations: representations, + datasetID: datasetID }) - ).finally(() => { - const skipOrientation = false; //state.trackingReducers.skipOrientationChange; - if (!skipOrientation) { - const ligandOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, ligandOrientation)); - - dispatch(appendMoleculeOrientation(getDatasetMoleculeID(datasetID, data?.id), ligandOrientation)); - - // keep current orientation of NGL View - if (!skipOrientation) { - console.count(`Before applying orientation after loading dataset ligand.`); - stage.viewerControls.orient(currentOrientation); - console.count(`After applying orientation after loading dataset ligand.`); - } - } - }); + ); }; export const removeDatasetLigand = (stage, data, colourToggle, datasetID, skipTracking = false) => dispatch => { dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data, undefined, datasetID)), - stage - ) + updateInToBeDisplayedListForDataset(datasetID, { + id: data.id, + display: false, + type: NGL_OBJECTS.LIGAND, + datasetID: datasetID + }) ); - dispatch(removeFromLigandList(datasetID, generateMoleculeCompoundId(data), skipTracking)); }; export const loadDataSets = targetId => async dispatch => { diff --git a/js/components/datasets/redux/reducer.js b/js/components/datasets/redux/reducer.js index 43595c0ee..b14225986 100644 --- a/js/components/datasets/redux/reducer.js +++ b/js/components/datasets/redux/reducer.js @@ -87,7 +87,20 @@ export const INITIAL_STATE = { inspirationsDialogOpenedForSelectedCompound: false, - isSelectedDatasetScrolled: false + isSelectedDatasetScrolled: false, + //Map of datasetID and its objects which shape is bellow + // Shape of the object in toBeDisplayedList: + // { + // type: 'L|P|C|S|V|RIBBON|etc...', + // id: 1, + + // center: true, + // withQuality: true, + // representations: [] etc... + + // display: true + // } + toBeDisplayedList: {} }; /** @@ -191,6 +204,45 @@ const removeDatasetFromState = (state, datasetId) => { export const datasetsReducers = (state = INITIAL_STATE, action = {}) => { switch (action.type) { + case constants.SET_TO_BE_DISPLAYED_LIST_DATASET: { + return setList(state, 'toBeDisplayedList', action.datasetID, action.list); + } + case constants.APPEND_TO_BE_DISPLAYED_LIST_DATASET: { + return { + ...state, + toBeDisplayedList: { + ...state.toBeDisplayedList, + [action.datasetID]: [...(state.toBeDisplayedList[action.datasetID] || []), action.item] + } + }; + } + case constants.REMOVE_FROM_TO_BE_DISPLAYED_LIST_DATASET: { + return { + ...state, + toBeDisplayedList: { + ...state.toBeDisplayedList, + [action.datasetID]: state.toBeDisplayedList[action.datasetID].filter( + i => i.id !== action.item.id || i.type !== action.item.type + ) + } + }; + } + case constants.UPDATE_IN_TO_BE_DISPLAYED_LIST_DATASET: { + return { + ...state, + toBeDisplayedList: { + ...state.toBeDisplayedList, + [action.datasetID]: state.toBeDisplayedList[action.datasetID].map(item => { + if (item.id === action.item.id && item.type === action.item.type) { + return { ...item, ...action.item }; + } else { + return item; + } + }) + } + }; + } + case constants.ADD_DATASET: const increasedDatasets = state.datasets.slice(); increasedDatasets.push(action.payload); diff --git a/js/components/preview/Preview.js b/js/components/preview/Preview.js index 653d9063d..8160e1683 100644 --- a/js/components/preview/Preview.js +++ b/js/components/preview/Preview.js @@ -47,6 +47,16 @@ import { setTagEditorOpen, setMoleculeForTagEdit, setToastMessages } from '../.. import { LoadingContext } from '../loading'; import { ToastContext } from '../toast'; import { TOAST_LEVELS } from '../toast/constants'; +import { useDisplayLigandLHS } from '../../reducers/ngl/useDisplayLigandLHS'; +import { useDisplayProteinLHS } from '../../reducers/ngl/useDisplayProteinLHS'; +import { useDisplayComplexLHS } from '../../reducers/ngl/useDisplayComplexLHS'; +import { useDisplaySurfaceLHS } from '../../reducers/ngl/useDisplaySurfacesLHS'; +import { useDisplayVectorLHS } from '../../reducers/ngl/useDisplayVectorLHS'; +import { useDisplayDensityLHS } from '../../reducers/ngl/useDisplayDensityLHS'; +import { useDisplayLigandRHS } from '../../reducers/ngl/useDisplayLigandRHS'; +import { useDisplayProteinRHS } from '../../reducers/ngl/useDisplayProteinRHS'; +import { useDisplayComplexRHS } from '../../reducers/ngl/useDisplayComplexRHS'; +import { useDisplaySurfaceRHS } from '../../reducers/ngl/useDisplaySurfaceRHS'; const ReactGridLayout = WidthProvider(ResponsiveGridLayout); @@ -128,6 +138,18 @@ const Preview = memo(({ isStateLoaded, hideProjects, isSnapshot = false }) => { const { setMoleculesAndTagsAreLoading } = useContext(LoadingContext); + useDisplayLigandLHS(); + useDisplayProteinLHS(); + useDisplayComplexLHS(); + useDisplaySurfaceLHS(); + useDisplayVectorLHS(); + useDisplayDensityLHS(); + + useDisplayLigandRHS(); + useDisplayProteinRHS(); + useDisplayComplexRHS(); + useDisplaySurfaceRHS(); + useEffect(() => { if (target_on && !isSnapshot) { dispatch(loadMoleculesAndTagsNew(target_on)); diff --git a/js/components/preview/molecule/modals/densityMapsModal.js b/js/components/preview/molecule/modals/densityMapsModal.js index 84e52979f..00a505758 100644 --- a/js/components/preview/molecule/modals/densityMapsModal.js +++ b/js/components/preview/molecule/modals/densityMapsModal.js @@ -60,11 +60,11 @@ export const DensityMapsModal = memo(({ openDialog, setOpenDialog, data, setDens }; const handleCloseModal = () => { - dispatch(setOpenDialog(false)); + setOpenDialog(false); }; const handleSaveButton = () => { - dispatch(setOpenDialog(false)); + setOpenDialog(false); setDensity(); }; diff --git a/js/components/preview/molecule/moleculeView/moleculeView.js b/js/components/preview/molecule/moleculeView/moleculeView.js index cf0c74fa8..78d6252b1 100644 --- a/js/components/preview/molecule/moleculeView/moleculeView.js +++ b/js/components/preview/molecule/moleculeView/moleculeView.js @@ -49,7 +49,14 @@ import { SvgTooltip } from '../../../common'; import { MOL_TYPE } from '../redux/constants'; import { DensityMapsModal } from '../modals/densityMapsModal'; import { getRandomColor } from '../utils/color'; -import { DEFAULT_TAG_COLOR, getAllTagsForCategories, getAllTagsForLHSCmp, getAllTagsForMol, getAllTagsForObservation, getAllTagsForObservationPopover } from '../../tags/utils/tagUtils'; +import { + DEFAULT_TAG_COLOR, + getAllTagsForCategories, + getAllTagsForLHSCmp, + getAllTagsForMol, + getAllTagsForObservation, + getAllTagsForObservationPopover +} from '../../tags/utils/tagUtils'; import MoleculeSelectCheckbox from './moleculeSelectCheckbox'; import useClipboard from 'react-use-clipboard'; import Typography from '@mui/material/Typography'; @@ -503,17 +510,24 @@ const MoleculeView = memo( return getFontColorByBackgroundColor(bgColor); }; - const getAllTags = useCallback(() => getAllTagsForCategories(data, tagList, tagCategories), [data, tagList, tagCategories]); + const getAllTags = useCallback(() => getAllTagsForCategories(data, tagList, tagCategories), [ + data, + tagList, + tagCategories + ]); /** * Get tag for render */ - const getTagType = useCallback((type) => { - const defaultTagObject = { tag_prefix: '-', color: 'orange' }; - const tagCategory = tagCategories.find(tag => tag.category === type); - const tagObject = tagCategory ? getAllTags().find(tag => tag.category === tagCategory.id) : defaultTagObject; - return tagObject ?? defaultTagObject; - }, [getAllTags, tagCategories]); + const getTagType = useCallback( + type => { + const defaultTagObject = { tag_prefix: '-', color: 'orange' }; + const tagCategory = tagCategories.find(tag => tag.category === type); + const tagObject = tagCategory ? getAllTags().find(tag => tag.category === tagCategory.id) : defaultTagObject; + return tagObject ?? defaultTagObject; + }, + [getAllTags, tagCategories] + ); const generateTagPopover = () => { // const allData = getAllTagsForObservation(data, tagList, tagCategories); @@ -1050,10 +1064,10 @@ const MoleculeView = memo( switch (string?.length) { case 1: case 2: - fontSize = 18 + fontSize = 18; break; case 4: - fontSize = 10 + fontSize = 10; break; default: break; @@ -1067,12 +1081,9 @@ const MoleculeView = memo( container justifyContent="space-between" direction="row" - className={classNames( - classes.container, - { - [classes.containerHeight]: !hideImage - } - )} + className={classNames(classes.container, { + [classes.containerHeight]: !hideImage + })} wrap="nowrap" ref={ref} > @@ -1104,22 +1115,33 @@ const MoleculeView = memo( {/* Title label */} - + { e.preventDefault(); setNameCopied(moleculeTitle); }} - className={classNames(classes.moleculeTitleLabel, { [classes.moleculeTitleLabelMainObs]: data.id === pose?.main_site_observation })} + className={classNames(classes.moleculeTitleLabel, { + [classes.moleculeTitleLabelMainObs]: data.id === pose?.main_site_observation + })} > - {moleculeTitleTruncated} + + {moleculeTitleTruncated} +
{data?.compound_code}
{/* Molecule properties */} - {getCalculatedProps().length > 0 && + {getCalculatedProps().length > 0 && ( {/* Molecule properties */} - } + )} {/* Control Buttons A, L, C, V */} @@ -1357,33 +1379,45 @@ const MoleculeView = memo( justifyContent="center" alignItems="center" // wrap="nowrap" - style={{ height: "100%" }} + style={{ height: '100%' }} > - {['CanonSites', 'ConformerSites', 'CrystalformSites', 'Crystalforms', 'Quatassemblies'].map(tagCategory => { - const tagTypeObject = getTagType(tagCategory); - const tagLabel = tagCategory === 'ConformerSites' ? tagTypeObject.tag_prefix.replace(getTagType('CanonSites')?.tag_prefix, '') : tagTypeObject?.tag_prefix; - return {PLURAL_TO_SINGULAR[tagCategory]} - {tagTypeObject.upload_name}} - > - - {tagLabel} - - - } + {['CanonSites', 'ConformerSites', 'CrystalformSites', 'Crystalforms', 'Quatassemblies'].map( + tagCategory => { + const tagTypeObject = getTagType(tagCategory); + const tagLabel = + tagCategory === 'ConformerSites' + ? tagTypeObject.tag_prefix.replace(getTagType('CanonSites')?.tag_prefix, '') + : tagTypeObject?.tag_prefix; + return ( + + {PLURAL_TO_SINGULAR[tagCategory]} - {tagTypeObject.upload_name} + + } + > + + {tagLabel} + + + ); + } )}
{/* Image */} - {(hideImage !== true) && + {hideImage !== true && (
)}
- } + + )} - {showExpandedView && - {['CanonSites', 'ConformerSites', 'CrystalformSites', 'Crystalforms', 'Quatassemblies'].map((tagCategory, index) => { - const tagTypeObject = getTagType(tagCategory); - let tagLabel = ''; - if (tagTypeObject) { - if (tagCategory === 'CrystalformSites') { - // "chop" more of CrystalformSites name - tagLabel = tagTypeObject.upload_name.substring(tagTypeObject.upload_name.indexOf('-') + 1); - tagLabel = tagLabel.substring(tagLabel.indexOf('-') + 1); - } else { - tagLabel = tagTypeObject.upload_name.substring(tagTypeObject.upload_name.indexOf('-') + 1); + {showExpandedView && ( + + {['CanonSites', 'ConformerSites', 'CrystalformSites', 'Crystalforms', 'Quatassemblies'].map( + (tagCategory, index) => { + const tagTypeObject = getTagType(tagCategory); + let tagLabel = ''; + if (tagTypeObject) { + if (tagCategory === 'CrystalformSites') { + // "chop" more of CrystalformSites name + tagLabel = tagTypeObject.upload_name.substring(tagTypeObject.upload_name.indexOf('-') + 1); + tagLabel = tagLabel.substring(tagLabel.indexOf('-') + 1); + } else { + tagLabel = tagTypeObject.upload_name.substring(tagTypeObject.upload_name.indexOf('-') + 1); + } + } + return ( + + + {tagLabel} + + + ); } - } - return - - {tagLabel} - - - })} - } - + )} + + )} + { if (r) { - dispatch(setDensityModalOpen(true)); + setDensityModalOpen(true); } else { addNewDensity(); } diff --git a/js/components/preview/molecule/redux/dispatchActions.js b/js/components/preview/molecule/redux/dispatchActions.js index eab06336c..dacdba66c 100644 --- a/js/components/preview/molecule/redux/dispatchActions.js +++ b/js/components/preview/molecule/redux/dispatchActions.js @@ -34,7 +34,9 @@ import { setTagEditorOpen, setMoleculeForTagEdit, setSelectVisiblePoses, - setUnselectVisiblePoses + setUnselectVisiblePoses, + appendToBeDisplayedList, + updateInToBeDisplayedList } from '../../../../reducers/selection/actions'; import { base_url } from '../../../routes/constants'; import { @@ -79,6 +81,7 @@ import { CATEGORY_TYPE } from '../../../../constants/constants'; import { selectJoinedMoleculeList } from './selectors'; import { compareTagsAsc } from '../../tags/utils/tagUtils'; import { createPoseApi, updatePoseApi } from '../api/poseApi'; +import { NGL_OBJECTS } from '../../../../reducers/ngl/constants'; // import { molFile, pdbApo } from './testData'; /** @@ -147,12 +150,12 @@ export const autoHideTagEditorDialogsOnScroll = ({ tagEditorRef, scrollBarRef }) } }; -const getViewUrl = (get_view, data) => { +export const getViewUrl = (get_view, data) => { const url = new URL(base_url + '/api/' + get_view + '/' + data.id + '/'); return url; }; -const handleVector = (json, stage, data) => (dispatch, getState) => { +export const handleVector = (json, stage, data) => (dispatch, getState) => { const state = getState(); const { vector_list, compoundsOfVectors } = state.selectionReducers; @@ -179,41 +182,13 @@ const handleVector = (json, stage, data) => (dispatch, getState) => { }; export const addVector = (stage, data, skipTracking = false) => async (dispatch, getState) => { - const currentVector = getState().selectionReducers.currentVector; - - dispatch(appendVectorOnList(generateMoleculeId(data), skipTracking)); - dispatch(selectVectorAndResetCompounds(currentVector)); - - return api({ url: getViewUrl('graph', data) }) - .then(response => { - const result = response.data.graph; - const new_dict = {}; - // Uniquify - if (result) { - Object.keys(result).forEach(key => { - const smiSet = new Set(); - new_dict[key] = {}; - new_dict[key]['addition'] = []; - new_dict[key]['vector'] = result[key]['vector']; - Object.keys(result[key]['addition']).forEach(index => { - const newSmi = result[key]['addition'][index]['end']; - if (smiSet.has(newSmi) !== true) { - new_dict[key]['addition'].push(result[key]['addition'][index]); - smiSet.add(newSmi); - } - }); - }); - } - return dispatch(updateVectorCompounds(data.smiles, new_dict)); - }) - .then(() => api({ url: new URL(base_url + '/api/vector/?id=' + data.id) })) - .then(response => { - dispatch(handleVector(response.data?.results[0]?.vectors, stage, data)); + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.VECTOR, + id: data.id, + display: true }) - .finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeCurrentVector = currentMoleculeSmile => (dispatch, getState) => { @@ -226,19 +201,7 @@ export const removeCurrentVector = currentMoleculeSmile => (dispatch, getState) }; export const removeVector = (stage, data, skipTracking = false) => (dispatch, getState) => { - const state = getState(); - const vector_list = state.selectionReducers.vector_list; - vector_list - .filter(item => item.moleculeId === data.id) - .forEach(item => dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, item), stage))); - - dispatch(removeCurrentVector(data.smiles)); - - dispatch(updateVectorCompounds(data.smiles, undefined)); - dispatch(updateBondColorMapOfCompounds(data.smiles, undefined)); - dispatch(removeFromVectorOnList(generateMoleculeId(data), skipTracking)); - - dispatch(setVectorList(vector_list.filter(item => item.moleculeId !== data.id))); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.VECTOR })); }; export const addComplex = ( @@ -249,29 +212,19 @@ export const addComplex = ( representations = undefined, preserveColour = false ) => async dispatch => { - dispatch(appendComplexList(generateMoleculeId(data), skipTracking)); - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateComplexObject(data, colourToggle, base_url)), - stage, - previousRepresentations: representations, - orientationMatrix: null, - preserveColour + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.COMPLEX, + id: data.id, + display: true, + representations: representations, + preserveColour: preserveColour }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeComplex = (stage, data, colourToggle, skipTracking = false) => dispatch => { - dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateComplexObject(data, colourToggle, base_url)), - stage - ) - ); - dispatch(removeFromComplexList(generateMoleculeId(data), skipTracking)); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.COMPLEX })); }; export const addSurface = ( @@ -282,29 +235,19 @@ export const addSurface = ( representations = undefined, preserveColour = false ) => async dispatch => { - dispatch(appendSurfaceList(generateMoleculeId(data), skipTracking)); - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url)), - stage, - previousRepresentations: representations, - orientationMatrix: null, - preserveColour + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.SURFACE, + id: data.id, + display: true, + representations: representations, + preserveColour: preserveColour }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeSurface = (stage, data, colourToggle, skipTracking = false) => dispatch => { - dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url)), - stage - ) - ); - dispatch(removeFromSurfaceList(generateMoleculeId(data), skipTracking)); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.SURFACE })); }; export const getDensityMapData = data => dispatch => { @@ -347,52 +290,18 @@ const setDensity = ( skipTracking = false, representations = undefined ) => async (dispatch, getState) => { - const prepParams = dispatch(getDensityChangedParams(isWireframeStyle)); - const densityObject = generateDensityObject(data, colourToggle, base_url, isWireframeStyle); - const combinedObject = { ...prepParams, ...densityObject }; dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, combinedObject), - stage, - previousRepresentations: representations, - orientationMatrix: null + appendToBeDisplayedList({ + type: NGL_OBJECTS.DENSITY, + id: data.id, + display: true, + representations: representations, + isWireframeStyle: isWireframeStyle }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - let molDataId = generateMoleculeId(data); - if (!data.proteinData) { - dispatch(getProteinData(data)).then(i => { - const proteinData = i; - data.proteinData = proteinData; - - molDataId['render_event'] = data.proteinData.render_event; - molDataId['render_sigmaa'] = data.proteinData.render_sigmaa; - molDataId['render_diff'] = data.proteinData.render_diff; - molDataId['render_quality'] = data.proteinData.render_quality; - - dispatch(appendDensityList(generateMoleculeId(data), skipTracking)); - dispatch(appendToDensityListType(molDataId, skipTracking)); - if (data.proteinData.render_quality) { - return dispatch(addQuality(stage, data, colourToggle, true)); - } - }); - } else { - molDataId['render_event'] = data.proteinData.render_event; - molDataId['render_sigmaa'] = data.proteinData.render_sigmaa; - molDataId['render_diff'] = data.proteinData.render_diff; - molDataId['render_quality'] = data.proteinData.render_quality; - - dispatch(appendDensityList(generateMoleculeId(data), skipTracking)); - dispatch(appendToDensityListType(molDataId, skipTracking)); - if (data.proteinData.render_quality) { - return dispatch(addQuality(stage, data, colourToggle, true)); - } - } - }); + ); }; -const getDensityChangedParams = (isWireframeStyle = undefined) => (dispatch, getState) => { +export const getDensityChangedParams = (isWireframeStyle = undefined) => (dispatch, getState) => { const state = getState(); const viewParams = state.nglReducers.viewParams; @@ -428,21 +337,12 @@ export const addDensityCustomView = ( ) => async (dispatch, getState) => { const state = getState(); const viewParams = state.nglReducers.viewParams; - // const contour_DENSITY = viewParams[NGL_PARAMS.contour_DENSITY]; - // const contour_DENSITY_MAP_sigmaa = viewParams[NGL_PARAMS.contour_DENSITY_MAP_sigmaa]; - // const contour_DENSITY_MAP_diff = viewParams[NGL_PARAMS.contour_DENSITY_MAP_diff]; if (data.proteinData) { return dispatch(setDensityCustom(stage, data, colourToggle, isWireframeStyle, skipTracking, representations)); - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY, invertedWireframe)); - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY_MAP_sigmaa, invertedWireframe)); - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY_MAP_diff, invertedWireframe)); } else { await dispatch(getDensityMapData(data)); return dispatch(setDensityCustom(stage, data, colourToggle, isWireframeStyle, skipTracking, representations)); } - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY, invertedWireframe)); - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY_MAP_sigmaa, invertedWireframe)); - // dispatch(setNglViewParams(NGL_PARAMS.contour_DENSITY_MAP_diff, invertedWireframe)); }; export const toggleDensityWireframe = (currentWireframeSetting, densityData) => dispatch => { @@ -470,26 +370,15 @@ const setDensityCustom = ( skipTracking = false, representations = undefined ) => async (dispatch, getState) => { - let densityObject = dispatch(getDensityChangedParams()); - densityObject = dispatch(toggleDensityWireframe(isWireframeStyle, densityObject)); - const oldDensityData = dispatch(deleteDensityObject(data, colourToggle, stage, !isWireframeStyle)); - densityObject = { ...densityObject, ...oldDensityData }; - const molId = generateMoleculeId(data); - dispatch(removeFromDensityList(molId, true)); - // dispatch(removeFromDensityListType(molId, true)); - - dispatch(appendDensityListCustom(generateMoleculeId(data), skipTracking)); - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, densityObject), - stage, - previousRepresentations: representations, - orientationMatrix: null + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.DENSITY_CUSTOM, + id: data.id, + display: true, + representations: representations, + isWireframeStyle: isWireframeStyle }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeDensity = ( @@ -500,19 +389,17 @@ export const removeDensity = ( skipTracking = false, representations = undefined ) => dispatch => { - dispatch(toggleDensityWireframe(isWireframeStyle)); - dispatch(deleteDensityObject(data, colourToggle, stage, isWireframeStyle)); - - const molId = generateMoleculeId(data); - dispatch(removeFromDensityList(molId, skipTracking)); - dispatch(removeFromDensityListCustom(molId, true)); - dispatch(removeFromDensityListType(molId, skipTracking)); - if (data.proteinData.render_quality) { - dispatch(removeQuality(stage, data, colourToggle, true)); - } + dispatch( + updateInToBeDisplayedList({ + id: data.id, + display: false, + isWireframeStyle: isWireframeStyle, + type: NGL_OBJECTS.DENSITY_CUSTOM + }) + ); }; -const deleteDensityObject = (data, colourToggle, stage, isWireframeStyle) => dispatch => { +export const deleteDensityObject = (data, colourToggle, stage, isWireframeStyle) => dispatch => { const densityObject = generateDensityObject(data, colourToggle, base_url, isWireframeStyle); dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, densityObject), stage)); @@ -534,44 +421,20 @@ export const addHitProtein = ( representations = undefined, preserveColour = false ) => async dispatch => { - // data.sdf_info = molFile; - dispatch(appendProteinList(generateMoleculeId(data), skipTracking)); - let hitProteinObject = generateHitProteinObject(data, colourToggle, base_url); - let qualityInformation = dispatch(readQualityInformation(hitProteinObject.name, hitProteinObject.sdf_info)); - - let hasAdditionalInformation = - withQuality === true && - qualityInformation && - qualityInformation.badproteinids && - qualityInformation.badproteinids.length !== 0; - if (hasAdditionalInformation) { - dispatch(appendQualityList(generateMoleculeId(data), true)); - } - - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, hitProteinObject), - stage, - previousRepresentations: representations, - orientationMatrix: null, - loadQuality: hasAdditionalInformation, - quality: qualityInformation, - preserveColour + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.PROTEIN, + id: data.id, + display: true, + withQuality: withQuality, + representations: representations, + preserveColour: preserveColour }) - ).finally(() => { - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); - }); + ); }; export const removeHitProtein = (stage, data, colourToggle, skipTracking = false) => dispatch => { - dispatch( - deleteObject( - Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateHitProteinObject(data, colourToggle, base_url)), - stage - ) - ); - dispatch(removeFromProteinList(generateMoleculeId(data), skipTracking)); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.PROTEIN })); }; export const addLigand = ( @@ -583,68 +446,41 @@ export const addLigand = ( skipTracking = false, representations = undefined ) => async (dispatch, getState) => { - // data.sdf_info = molFile; - console.count(`Grabbing orientation before loading ligand.`); - const currentOrientation = stage.viewerControls.getOrientation(); - dispatch(appendFragmentDisplayList(generateMoleculeId(data), skipTracking)); - - let moleculeObject = generateMoleculeObject(data, colourToggle); - let qualityInformation = dispatch(readQualityInformation(moleculeObject.name, moleculeObject.sdf_info)); - - let hasAdditionalInformation = - withQuality === true && qualityInformation && qualityInformation.badids && qualityInformation.badids.length !== 0; - if (hasAdditionalInformation) { - dispatch(appendQualityList(generateMoleculeId(data), true)); - } - - return dispatch( - loadObject({ - target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, moleculeObject), - stage, - previousRepresentations: representations, - loadQuality: hasAdditionalInformation, - quality: qualityInformation + dispatch( + appendToBeDisplayedList({ + type: NGL_OBJECTS.LIGAND, + id: data.id, + display: true, + center: centerOn, + withQuality: withQuality, + representations: representations }) - ).then(() => { - const state = getState(); - const skipOrientation = false; //state.trackingReducers.skipOrientationChange; - if (!skipOrientation) { - const ligandOrientation = stage.viewerControls.getOrientation(); - dispatch(setOrientation(VIEWS.MAJOR_VIEW, ligandOrientation)); - - dispatch(appendMoleculeOrientation(data?.id, ligandOrientation)); - if (centerOn === false) { - // keep current orientation of NGL View - console.count(`Before applying orientation matrix after loading ligand.`); - stage.viewerControls.orient(currentOrientation); - console.count(`After applying orientation matrix after loading ligand.`); - } - } - }); + ); }; export const removeLigand = (stage, data, skipTracking = false, withVector = true) => dispatch => { - dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); - dispatch(removeFromFragmentDisplayList(generateMoleculeId(data), skipTracking)); - dispatch(removeFromQualityList(generateMoleculeId(data), true)); - - if (withVector === true) { - // remove vector - dispatch(removeVector(stage, data, skipTracking)); - } + dispatch( + updateInToBeDisplayedList({ id: data.id, display: false, withVector: withVector, type: NGL_OBJECTS.LIGAND }) + ); }; export const addQuality = (stage, data, colourToggle, skipTracking = false, representations = undefined) => ( dispatch, getState ) => { - dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); + dispatch(removeFromFragmentDisplayList(generateMoleculeId(data))); + dispatch(removeFromQualityList(generateMoleculeId(data))); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.LIGAND })); + // dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); dispatch(appendQualityList(generateMoleculeId(data), skipTracking)); return dispatch(addLigand(stage, data, colourToggle, false, true, true, representations)); }; export const removeQuality = (stage, data, colourToggle, skipTracking = false) => dispatch => { - dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); + dispatch(removeFromFragmentDisplayList(generateMoleculeId(data))); + dispatch(removeFromQualityList(generateMoleculeId(data))); + dispatch(updateInToBeDisplayedList({ id: data.id, display: false, type: NGL_OBJECTS.LIGAND })); + // dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); dispatch(addLigand(stage, data, colourToggle, false, false, true)); dispatch(removeFromQualityList(generateMoleculeId(data), skipTracking)); }; diff --git a/js/reducers/ngl/actions.js b/js/reducers/ngl/actions.js index 7c58ae185..5931ff6b3 100644 --- a/js/reducers/ngl/actions.js +++ b/js/reducers/ngl/actions.js @@ -265,3 +265,8 @@ export const addToQualityCache = (name, cacheItem) => ({ type: CONSTANTS.ADD_TO_QUALITY_CACHE, payload: { name: name, cacheItem: cacheItem } }); + +export const setSkipOrientationChange = skip => ({ + type: CONSTANTS.SET_SKIP_ORIENTATION_CHANGE, + skip: skip +}); diff --git a/js/reducers/ngl/constants.js b/js/reducers/ngl/constants.js index 5fdde957a..a54932761 100644 --- a/js/reducers/ngl/constants.js +++ b/js/reducers/ngl/constants.js @@ -58,10 +58,23 @@ export const CONSTANTS = { SET_WARNING_ICON: prefix + 'SET_WARNING_ICON', SET_ELECTRON_COLOR_DENSITY: prefix + 'SET_ELECTRON_COLOR' + MAP_TYPE.event, SET_ELECTRON_COLOR_DENSITY_MAP_sigmaa: prefix + 'SET_ELECTRON_COLOR' + MAP_TYPE.sigmaa, - SET_ELECTRON_COLOR_DENSITY_MAP_diff: prefix + 'SET_ELECTRONCOLOR' + MAP_TYPE.diff + SET_ELECTRON_COLOR_DENSITY_MAP_diff: prefix + 'SET_ELECTRONCOLOR' + MAP_TYPE.diff, + + SET_SKIP_ORIENTATION_CHANGE: prefix + 'SET_SKIP_ORIENTATION_CHANGE' }; export const SCENES = { defaultScene: 'defaultScene', sessionScene: 'sessionScene' }; + +export const NGL_OBJECTS = { + PROTEIN: 'PROTEIN', + LIGAND: 'LIGAND', + SURFACE: 'SURFACE', + DENSITY: 'DENSITY', + DENSITY_CUSTOM: 'DENSITY_CUSTOM', + VECTOR: 'VECTOR', + COMPLEX: 'COMPLEX', + QUALITY: 'QUALITY' +}; diff --git a/js/reducers/ngl/nglReducers.js b/js/reducers/ngl/nglReducers.js index 1cf6a2d46..96163294f 100644 --- a/js/reducers/ngl/nglReducers.js +++ b/js/reducers/ngl/nglReducers.js @@ -45,13 +45,17 @@ export const INITIAL_STATE = { qualityCache: {}, electronDensityColor_event: 'blue', electronDensityColor_sigmaa: 'blue', - electronDensityColor_diff: 'blue' + electronDensityColor_diff: 'blue', + skipOrientationChange: false }; export default function nglReducers(state = INITIAL_STATE, action = {}) { switch (action.type) { // Defined in initialState - but may be needed if we want to load a different structure + case CONSTANTS.SET_SKIP_ORIENTATION_CHANGE: + return { ...state, skipOrientationChange: action.skip }; + case CONSTANTS.LOAD_OBJECT: // at first check if object was already stashed const objectsInViewStashTemp = JSON.parse(JSON.stringify(state.objectsInViewStash)); @@ -117,7 +121,10 @@ export default function nglReducers(state = INITIAL_STATE, action = {}) { // stash state of the object let newObjectsInViewStash = JSON.parse(JSON.stringify(state.objectsInViewStash)); if (objectsInViewTemp.hasOwnProperty(action.target.name)) { - newObjectsInViewStash[action.target.name] = { ...objectsInViewTemp[action.target.name], representations: objectsInViewTemp[action.target.name].representations }; + newObjectsInViewStash[action.target.name] = { + ...objectsInViewTemp[action.target.name], + representations: objectsInViewTemp[action.target.name].representations + }; } delete objectsInViewTemp[action.target.name]; diff --git a/js/reducers/ngl/useDisplayComplexLHS.js b/js/reducers/ngl/useDisplayComplexLHS.js new file mode 100644 index 000000000..ae3962bf3 --- /dev/null +++ b/js/reducers/ngl/useDisplayComplexLHS.js @@ -0,0 +1,85 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { appendComplexList, removeFromComplexList, removeFromToBeDisplayedList } from '../selection/actions'; +import { generateComplexObject, generateMoleculeId } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; +import { getToBeDisplayedStructures } from './utils'; + +export const useDisplayComplexLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedComplexes = useSelector(state => state.selectionReducers.complexList); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayComplex = useCallback( + complexData => { + const data = allObservations.find(obs => obs.id === complexData.id); + const colourToggle = getRandomColor(data); + + dispatch(appendComplexList(generateMoleculeId(data))); + return dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateComplexObject(data, colourToggle, base_url)), + stage, + previousRepresentations: complexData.representations, + orientationMatrix: null, + preserveColour: complexData.preserveColour + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allObservations, dispatch, stage] + ); + + const removeComplex = useCallback( + complexData => { + const data = allObservations.find(obs => obs.id === complexData.id); + const colourToggle = getRandomColor(data); + + dispatch( + deleteObject( + Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateComplexObject(data, colourToggle, base_url)), + stage + ) + ); + dispatch(removeFromComplexList(generateMoleculeId(data))); + + dispatch(removeFromToBeDisplayedList({ id: complexData.id, type: NGL_OBJECTS.COMPLEX })); + }, + [allObservations, dispatch, stage] + ); + + useEffect(() => { + const toBeDisplayedComplexes = getToBeDisplayedStructures( + toBeDisplayedList, + displayedComplexes, + NGL_OBJECTS.COMPLEX + ); + toBeDisplayedComplexes?.forEach(data => { + displayComplex(data); + }); + + const toBeRemovedComplexes = getToBeDisplayedStructures( + toBeDisplayedList, + displayedComplexes, + NGL_OBJECTS.COMPLEX, + true + ); + toBeRemovedComplexes?.forEach(data => { + removeComplex(data); + }); + }, [toBeDisplayedList, displayComplex, dispatch, stage, removeComplex, displayedComplexes]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayComplexRHS.js b/js/reducers/ngl/useDisplayComplexRHS.js new file mode 100644 index 000000000..36206351d --- /dev/null +++ b/js/reducers/ngl/useDisplayComplexRHS.js @@ -0,0 +1,97 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { VIEWS } from '../../constants/constants'; +import { NGL_OBJECTS } from './constants'; +import { getToBeDisplayedStructuresDataset } from './utils'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { + appendComplexList, + removeFromComplexList, + removeFromToBeDisplayedListForDataset +} from '../../components/datasets/redux/actions'; +import { generateComplexObject, generateMoleculeCompoundId } from '../../components/nglView/generatingObjects'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; + +export const useDisplayComplexRHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.datasetsReducers.toBeDisplayedList); + const displayedComplexes = useSelector(state => state.datasetsReducers.complexLists); + const allCompounds = useSelector(state => state.datasetsReducers.moleculeLists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayComplex = useCallback( + complexData => { + const datasetCompounds = allCompounds[complexData.datasetID]; + const data = datasetCompounds.find(obs => obs.id === complexData.id); + const colourToggle = getRandomColor(data); + const datasetID = complexData.datasetID; + + dispatch(appendComplexList(datasetID, generateMoleculeCompoundId(data))); + return dispatch( + loadObject({ + target: Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateComplexObject(data, colourToggle, base_url, datasetID) + ), + stage, + previousRepresentations: complexData.representations, + orientationMatrix: null + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allCompounds, dispatch, stage] + ); + + const removeComplex = useCallback( + complexData => { + const datasetCompounds = allCompounds[complexData.datasetID]; + const data = datasetCompounds?.find(obs => obs.id === complexData.id); + const datasetID = complexData.datasetID; + const colourToggle = getRandomColor(data); + + dispatch( + deleteObject( + Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateComplexObject(data, colourToggle, base_url, datasetID) + ), + stage + ) + ); + dispatch(removeFromComplexList(datasetID, generateMoleculeCompoundId(data))); + + dispatch(removeFromToBeDisplayedListForDataset(datasetID, { id: complexData.id, type: NGL_OBJECTS.COMPLEX })); + }, + [allCompounds, dispatch, stage] + ); + + useEffect(() => { + const toBeRemovedComplexes = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedComplexes, + NGL_OBJECTS.COMPLEX, + true + ); + toBeRemovedComplexes?.forEach(data => { + removeComplex(data); + }); + const toBeDisplayedComplexes = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedComplexes, + NGL_OBJECTS.COMPLEX + ); + toBeDisplayedComplexes?.forEach(data => { + displayComplex(data); + }); + }, [toBeDisplayedList, displayedComplexes, displayComplex, dispatch, stage, removeComplex]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayDensityLHS.js b/js/reducers/ngl/useDisplayDensityLHS.js new file mode 100644 index 000000000..a9b15337f --- /dev/null +++ b/js/reducers/ngl/useDisplayDensityLHS.js @@ -0,0 +1,212 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { + appendDensityList, + appendDensityListCustom, + appendToBeDisplayedList, + appendToDensityListType, + removeFromDensityList, + removeFromDensityListCustom, + removeFromDensityListType, + removeFromToBeDisplayedList +} from '../selection/actions'; +import { generateDensityObject, generateMoleculeId } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; +import { getToBeDisplayedStructures } from './utils'; +import { + addQuality, + deleteDensityObject, + getDensityChangedParams, + getDensityMapData, + getProteinData, + removeQuality, + toggleDensityWireframe +} from '../../components/preview/molecule/redux/dispatchActions'; + +export const useDisplayDensityLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedDensities = useSelector(state => state.selectionReducers.densityList); + const displayedCustomDensities = useSelector(state => state.selectionReducers.densityListCustom); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayDensity = useCallback( + async densityData => { + const data = allObservations.find(obs => obs.id === densityData.id); + const colourToggle = getRandomColor(data); + + if (!data.proteinData) { + await dispatch(getDensityMapData(data)); + } + + const prepParams = dispatch(getDensityChangedParams(densityData.isWireframeStyle)); + const densityObject = generateDensityObject(data, colourToggle, base_url, densityData.isWireframeStyle); + const combinedObject = { ...prepParams, ...densityObject }; + dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, combinedObject), + stage, + previousRepresentations: densityData.representations, + orientationMatrix: null + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + let molDataId = generateMoleculeId(data); + if (!data.proteinData) { + dispatch(getProteinData(data)).then(i => { + const proteinData = i; + data.proteinData = proteinData; + + molDataId['render_event'] = data.proteinData.render_event; + molDataId['render_sigmaa'] = data.proteinData.render_sigmaa; + molDataId['render_diff'] = data.proteinData.render_diff; + molDataId['render_quality'] = data.proteinData.render_quality; + + dispatch(appendDensityList(generateMoleculeId(data))); + dispatch(appendToDensityListType(molDataId)); + if (data.proteinData.render_quality) { + return dispatch(addQuality(stage, data, colourToggle, true)); + } + }); + } else { + molDataId['render_event'] = data.proteinData.render_event; + molDataId['render_sigmaa'] = data.proteinData.render_sigmaa; + molDataId['render_diff'] = data.proteinData.render_diff; + molDataId['render_quality'] = data.proteinData.render_quality; + + dispatch(appendDensityList(generateMoleculeId(data))); + dispatch(appendToDensityListType(molDataId)); + if (data.proteinData.render_quality) { + return dispatch(addQuality(stage, data, colourToggle, true)); + } + } + }); + }, + [allObservations, dispatch, stage] + ); + + const displayCustomDensity = useCallback( + async densityData => { + const data = allObservations.find(obs => obs.id === densityData.id); + const colourToggle = getRandomColor(data); + + if (!data.proteinData) { + await dispatch(getDensityMapData(data)); + } + + let densityObject = dispatch(getDensityChangedParams()); + densityObject = dispatch(toggleDensityWireframe(densityData.isWireframeStyle, densityObject)); + const oldDensityData = dispatch(deleteDensityObject(data, colourToggle, stage, !densityData.isWireframeStyle)); + densityObject = { ...densityObject, ...oldDensityData }; + const molId = generateMoleculeId(data); + dispatch(removeFromDensityList(molId, true)); + //here we need to remove density but we don't know if it is custom or not so we remove both + dispatch(removeFromToBeDisplayedList({ id: densityData.id, type: NGL_OBJECTS.DENSITY })); + dispatch(removeFromToBeDisplayedList({ id: densityData.id, type: NGL_OBJECTS.DENSITY_CUSTOM })); + //and then re-add it as custom + dispatch(appendDensityListCustom(generateMoleculeId(data))); + dispatch( + appendToBeDisplayedList({ + ...densityData + }) + ); + // dispatch(removeFromDensityListType(molId, true)); + + return dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, densityObject), + stage, + previousRepresentations: densityData.representations, + orientationMatrix: null + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allObservations, dispatch, stage] + ); + + const removeDensity = useCallback( + densityData => { + const data = allObservations.find(obs => obs.id === densityData.id); + const colourToggle = getRandomColor(data); + + dispatch(toggleDensityWireframe(densityData.isWireframeStyle)); + dispatch(deleteDensityObject(data, colourToggle, stage, densityData.isWireframeStyle)); + + const molId = generateMoleculeId(data); + dispatch(removeFromDensityList(molId)); + dispatch(removeFromDensityListCustom(molId, true)); + dispatch(removeFromDensityListType(molId)); + if (data.proteinData.render_quality) { + dispatch(removeQuality(stage, data, colourToggle, true)); + } + + dispatch(removeFromToBeDisplayedList({ id: densityData.id, type: NGL_OBJECTS.DENSITY })); + dispatch(removeFromToBeDisplayedList({ id: densityData.id, type: NGL_OBJECTS.DENSITY_CUSTOM })); + }, + [allObservations, dispatch, stage] + ); + + useEffect(() => { + const toBeDisplayedDensities = getToBeDisplayedStructures( + toBeDisplayedList, + displayedDensities, + NGL_OBJECTS.DENSITY + ); + toBeDisplayedDensities?.forEach(data => { + displayDensity(data); + }); + + const toBeDisplayedCustomDensities = getToBeDisplayedStructures( + toBeDisplayedList, + displayedCustomDensities, + NGL_OBJECTS.DENSITY_CUSTOM + ); + toBeDisplayedCustomDensities?.forEach(data => { + displayCustomDensity(data); + }); + + const toBeRemovedDensities = getToBeDisplayedStructures( + toBeDisplayedList, + displayedDensities, + NGL_OBJECTS.DENSITY, + true + ); + toBeRemovedDensities?.forEach(data => { + removeDensity(data); + }); + + const toBeRemovedCustomDensities = getToBeDisplayedStructures( + toBeDisplayedList, + displayedCustomDensities, + NGL_OBJECTS.DENSITY_CUSTOM, + true + ); + toBeRemovedCustomDensities?.forEach(data => { + removeDensity(data); + }); + }, [ + toBeDisplayedList, + displayDensity, + dispatch, + stage, + removeDensity, + displayedDensities, + displayedCustomDensities, + displayCustomDensity + ]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayLigandLHS.js b/js/reducers/ngl/useDisplayLigandLHS.js new file mode 100644 index 000000000..b828bb0c7 --- /dev/null +++ b/js/reducers/ngl/useDisplayLigandLHS.js @@ -0,0 +1,112 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { + appendFragmentDisplayList, + appendQualityList, + removeFromFragmentDisplayList, + removeFromQualityList, + removeFromToBeDisplayedList +} from '../selection/actions'; +import { generateMoleculeId, generateMoleculeObject } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { readQualityInformation } from '../../components/nglView/renderingHelpers'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { appendMoleculeOrientation } from './actions'; +import { removeVector } from '../../components/preview/molecule/redux/dispatchActions'; +import { getToBeDisplayedStructures } from './utils'; + +export const useDisplayLigandLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedLigands = useSelector(state => state.selectionReducers.fragmentDisplayList); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + const skipOrientationChange = useSelector(state => state.nglReducers.skipOrientationChange); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayLigand = useCallback( + ligandData => { + const data = allObservations.find(obs => obs.id === ligandData.id); + const colourToggle = getRandomColor(data); + const currentOrientation = stage.viewerControls.getOrientation(); + + dispatch(appendFragmentDisplayList(generateMoleculeId(data))); + + let moleculeObject = generateMoleculeObject(data, colourToggle); + let qualityInformation = dispatch(readQualityInformation(moleculeObject.name, moleculeObject.sdf_info)); + + let hasAdditionalInformation = + ligandData.withQuality === true && + qualityInformation && + qualityInformation.badids && + qualityInformation.badids.length !== 0; + if (hasAdditionalInformation) { + dispatch(appendQualityList(generateMoleculeId(data))); + } + + return dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, moleculeObject), + stage, + previousRepresentations: ligandData.representations, + loadQuality: hasAdditionalInformation, + quality: qualityInformation + }) + ).then(() => { + const skipOrientation = skipOrientationChange; + if (!skipOrientation) { + const ligandOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, ligandOrientation)); + + dispatch(appendMoleculeOrientation(data?.id, ligandOrientation)); + if (!ligandData.center) { + // keep current orientation of NGL View + console.count(`Before applying orientation matrix after loading ligand.`); + stage.viewerControls.orient(currentOrientation); + console.count(`After applying orientation matrix after loading ligand.`); + } + } + }); + }, + [allObservations, dispatch, skipOrientationChange, stage] + ); + + const removeLigand = useCallback( + ligandData => { + const data = allObservations.find(obs => obs.id === ligandData.id); + dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data)), stage)); + dispatch(removeFromFragmentDisplayList(generateMoleculeId(data))); + dispatch(removeFromQualityList(generateMoleculeId(data))); + if (ligandData.withVector === true) { + // remove vector + dispatch(removeVector(stage, data)); + } + + dispatch(removeFromToBeDisplayedList({ id: ligandData.id, type: NGL_OBJECTS.LIGAND })); + }, + [allObservations, dispatch, stage] + ); + + useEffect(() => { + const toBeRemovedLigands = getToBeDisplayedStructures( + toBeDisplayedList, + displayedLigands, + NGL_OBJECTS.LIGAND, + true + ); + toBeRemovedLigands?.forEach(data => { + removeLigand(data); + }); + const toBeDisplayedLigands = getToBeDisplayedStructures(toBeDisplayedList, displayedLigands, NGL_OBJECTS.LIGAND); + toBeDisplayedLigands?.forEach(data => { + displayLigand(data); + }); + }, [toBeDisplayedList, displayedLigands, displayLigand, dispatch, stage, removeLigand]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayLigandRHS.js b/js/reducers/ngl/useDisplayLigandRHS.js new file mode 100644 index 000000000..de3fb1de5 --- /dev/null +++ b/js/reducers/ngl/useDisplayLigandRHS.js @@ -0,0 +1,109 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { VIEWS } from '../../constants/constants'; +import { NGL_OBJECTS } from './constants'; +import { getToBeDisplayedStructuresDataset } from './utils'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { + appendLigandList, + removeFromLigandList, + removeFromToBeDisplayedListForDataset +} from '../../components/datasets/redux/actions'; +import { generateMoleculeCompoundId, generateMoleculeObject } from '../../components/nglView/generatingObjects'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { appendMoleculeOrientation } from './actions'; +import { getDatasetMoleculeID } from '../../components/datasets/redux/dispatchActions'; + +export const useDisplayLigandRHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.datasetsReducers.toBeDisplayedList); + const displayedLigands = useSelector(state => state.datasetsReducers.ligandLists); + const allCompounds = useSelector(state => state.datasetsReducers.moleculeLists); + const skipOrientationChange = useSelector(state => state.nglReducers.skipOrientationChange); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayLigand = useCallback( + ligandData => { + const datasetCompounds = allCompounds[ligandData.datasetID]; + const data = datasetCompounds.find(obs => obs.id === ligandData.id); + const colourToggle = getRandomColor(data); + const datasetID = ligandData.datasetID; + + dispatch(appendLigandList(datasetID, generateMoleculeCompoundId(data))); + console.count(`Grabbed orientation before loading dataset ligand`); + const currentOrientation = stage.viewerControls.getOrientation(); + return dispatch( + loadObject({ + target: Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateMoleculeObject(data, colourToggle, datasetID) + ), + stage, + previousRepresentations: ligandData.representations, + markAsRightSideLigand: true + }) + ).finally(() => { + const skipOrientation = skipOrientationChange; + if (!skipOrientation) { + const ligandOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, ligandOrientation)); + + dispatch(appendMoleculeOrientation(getDatasetMoleculeID(datasetID, data?.id), ligandOrientation)); + + // keep current orientation of NGL View + if (!skipOrientation) { + console.count(`Before applying orientation after loading dataset ligand.`); + stage.viewerControls.orient(currentOrientation); + console.count(`After applying orientation after loading dataset ligand.`); + } + } + }); + }, + [allCompounds, dispatch, skipOrientationChange, stage] + ); + + const removeLigand = useCallback( + ligandData => { + const datasetCompounds = allCompounds[ligandData.datasetID]; + const data = datasetCompounds?.find(obs => obs.id === ligandData.id); + const datasetID = ligandData.datasetID; + + dispatch( + deleteObject( + Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateMoleculeObject(data, undefined, datasetID)), + stage + ) + ); + dispatch(removeFromLigandList(datasetID, generateMoleculeCompoundId(data))); + + dispatch(removeFromToBeDisplayedListForDataset(datasetID, { id: ligandData.id, type: NGL_OBJECTS.LIGAND })); + }, + [allCompounds, dispatch, stage] + ); + + useEffect(() => { + const toBeRemovedLigands = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedLigands, + NGL_OBJECTS.LIGAND, + true + ); + toBeRemovedLigands?.forEach(data => { + removeLigand(data); + }); + const toBeDisplayedLigands = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedLigands, + NGL_OBJECTS.LIGAND + ); + toBeDisplayedLigands?.forEach(data => { + displayLigand(data); + }); + }, [toBeDisplayedList, displayedLigands, displayLigand, dispatch, stage, removeLigand]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayProteinLHS.js b/js/reducers/ngl/useDisplayProteinLHS.js new file mode 100644 index 000000000..a2f873a11 --- /dev/null +++ b/js/reducers/ngl/useDisplayProteinLHS.js @@ -0,0 +1,99 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { + appendProteinList, + appendQualityList, + removeFromProteinList, + removeFromToBeDisplayedList +} from '../selection/actions'; +import { generateHitProteinObject, generateMoleculeId } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { readQualityInformation } from '../../components/nglView/renderingHelpers'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; +import { getToBeDisplayedStructures } from './utils'; + +export const useDisplayProteinLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedProteins = useSelector(state => state.selectionReducers.proteinList); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayProtein = useCallback( + proteinData => { + const data = allObservations.find(obs => obs.id === proteinData.id); + const colourToggle = getRandomColor(data); + + dispatch(appendProteinList(generateMoleculeId(data))); + const hitProteinObject = generateHitProteinObject(data, colourToggle, base_url); + const qualityInformation = dispatch(readQualityInformation(hitProteinObject.name, hitProteinObject.sdf_info)); + + let hasAdditionalInformation = + proteinData.withQuality === true && + qualityInformation && + qualityInformation.badproteinids && + qualityInformation.badproteinids.length !== 0; + if (hasAdditionalInformation) { + dispatch(appendQualityList(generateMoleculeId(data), true)); + } + + return dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, hitProteinObject), + stage, + previousRepresentations: proteinData.representations, + orientationMatrix: null, + loadQuality: hasAdditionalInformation, + quality: qualityInformation, + preserveColour: proteinData.preserveColour + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allObservations, dispatch, stage] + ); + + const removeProtein = useCallback( + proteinData => { + const data = allObservations.find(obs => obs.id === proteinData.id); + const colourToggle = getRandomColor(data); + dispatch( + deleteObject( + Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateHitProteinObject(data, colourToggle, base_url)), + stage + ) + ); + dispatch(removeFromProteinList(generateMoleculeId(data))); + dispatch(removeFromToBeDisplayedList({ id: proteinData.id, type: NGL_OBJECTS.PROTEIN })); + }, + [allObservations, dispatch, stage] + ); + + useEffect(() => { + const toBeDisplayedProteins = getToBeDisplayedStructures(toBeDisplayedList, displayedProteins, NGL_OBJECTS.PROTEIN); + toBeDisplayedProteins?.forEach(data => { + displayProtein(data); + }); + + const toBeRemovedProteins = getToBeDisplayedStructures( + toBeDisplayedList, + displayedProteins, + NGL_OBJECTS.PROTEIN, + true + ); + toBeRemovedProteins?.forEach(data => { + removeProtein(data); + }); + }, [toBeDisplayedList, displayProtein, dispatch, stage, removeProtein, displayedProteins]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayProteinRHS.js b/js/reducers/ngl/useDisplayProteinRHS.js new file mode 100644 index 000000000..f6e45beaf --- /dev/null +++ b/js/reducers/ngl/useDisplayProteinRHS.js @@ -0,0 +1,97 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { VIEWS } from '../../constants/constants'; +import { NGL_OBJECTS } from './constants'; +import { getToBeDisplayedStructuresDataset } from './utils'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { + appendProteinList, + removeFromProteinList, + removeFromToBeDisplayedListForDataset +} from '../../components/datasets/redux/actions'; +import { generateHitProteinObject, generateMoleculeCompoundId } from '../../components/nglView/generatingObjects'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; + +export const useDisplayProteinRHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.datasetsReducers.toBeDisplayedList); + const displayedProteins = useSelector(state => state.datasetsReducers.proteinLists); + const allCompounds = useSelector(state => state.datasetsReducers.moleculeLists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayProtein = useCallback( + proteinData => { + const datasetCompounds = allCompounds[proteinData.datasetID]; + const data = datasetCompounds.find(obs => obs.id === proteinData.id); + const colourToggle = getRandomColor(data); + const datasetID = proteinData.datasetID; + + dispatch(appendProteinList(datasetID, generateMoleculeCompoundId(data))); + return dispatch( + loadObject({ + target: Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateHitProteinObject(data, colourToggle, base_url, datasetID) + ), + stage, + previousRepresentations: proteinData.representations, + orientationMatrix: null + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allCompounds, dispatch, stage] + ); + + const removeProtein = useCallback( + proteinData => { + const datasetCompounds = allCompounds[proteinData.datasetID]; + const data = datasetCompounds?.find(obs => obs.id === proteinData.id); + const datasetID = proteinData.datasetID; + const colourToggle = getRandomColor(data); + + dispatch( + deleteObject( + Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateHitProteinObject(data, colourToggle, base_url, datasetID) + ), + stage + ) + ); + dispatch(removeFromProteinList(datasetID, generateMoleculeCompoundId(data))); + + dispatch(removeFromToBeDisplayedListForDataset(datasetID, { id: proteinData.id, type: NGL_OBJECTS.PROTEIN })); + }, + [allCompounds, dispatch, stage] + ); + + useEffect(() => { + const toBeRemovedProteins = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedProteins, + NGL_OBJECTS.PROTEIN, + true + ); + toBeRemovedProteins?.forEach(data => { + removeProtein(data); + }); + const toBeDisplayedProteins = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedProteins, + NGL_OBJECTS.PROTEIN + ); + toBeDisplayedProteins?.forEach(data => { + displayProtein(data); + }); + }, [toBeDisplayedList, displayedProteins, displayProtein, dispatch, stage, removeProtein]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplaySurfaceRHS.js b/js/reducers/ngl/useDisplaySurfaceRHS.js new file mode 100644 index 000000000..32776da1f --- /dev/null +++ b/js/reducers/ngl/useDisplaySurfaceRHS.js @@ -0,0 +1,97 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { VIEWS } from '../../constants/constants'; +import { NGL_OBJECTS } from './constants'; +import { getToBeDisplayedStructuresDataset } from './utils'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { + appendSurfaceList, + removeFromSurfaceList, + removeFromToBeDisplayedListForDataset +} from '../../components/datasets/redux/actions'; +import { generateMoleculeCompoundId, generateSurfaceObject } from '../../components/nglView/generatingObjects'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; + +export const useDisplaySurfaceRHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.datasetsReducers.toBeDisplayedList); + const displayedSurfaces = useSelector(state => state.datasetsReducers.surfaceLists); + const allCompounds = useSelector(state => state.datasetsReducers.moleculeLists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displaySurface = useCallback( + surfaceData => { + const datasetCompounds = allCompounds[surfaceData.datasetID]; + const data = datasetCompounds.find(obs => obs.id === surfaceData.id); + const colourToggle = getRandomColor(data); + const datasetID = surfaceData.datasetID; + + dispatch(appendSurfaceList(datasetID, generateMoleculeCompoundId(data))); + return dispatch( + loadObject({ + target: Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateSurfaceObject(data, colourToggle, base_url, datasetID) + ), + stage, + previousRepresentations: surfaceData.representations, + orientationMatrix: null + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allCompounds, dispatch, stage] + ); + + const removeSurface = useCallback( + surfaceData => { + const datasetCompounds = allCompounds[surfaceData.datasetID]; + const data = datasetCompounds?.find(obs => obs.id === surfaceData.id); + const datasetID = surfaceData.datasetID; + const colourToggle = getRandomColor(data); + + dispatch( + deleteObject( + Object.assign( + { display_div: VIEWS.MAJOR_VIEW }, + generateSurfaceObject(data, colourToggle, base_url, datasetID) + ), + stage + ) + ); + dispatch(removeFromSurfaceList(datasetID, generateMoleculeCompoundId(data))); + + dispatch(removeFromToBeDisplayedListForDataset(datasetID, { id: surfaceData.id, type: NGL_OBJECTS.SURFACE })); + }, + [allCompounds, dispatch, stage] + ); + + useEffect(() => { + const toBeRemovedSurfaces = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedSurfaces, + NGL_OBJECTS.SURFACE, + true + ); + toBeRemovedSurfaces?.forEach(data => { + removeSurface(data); + }); + const toBeDisplayedSurfaces = getToBeDisplayedStructuresDataset( + toBeDisplayedList, + displayedSurfaces, + NGL_OBJECTS.SURFACE + ); + toBeDisplayedSurfaces?.forEach(data => { + displaySurface(data); + }); + }, [toBeDisplayedList, displayedSurfaces, displaySurface, dispatch, stage, removeSurface]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplaySurfacesLHS.js b/js/reducers/ngl/useDisplaySurfacesLHS.js new file mode 100644 index 000000000..427d6f3f2 --- /dev/null +++ b/js/reducers/ngl/useDisplaySurfacesLHS.js @@ -0,0 +1,81 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { appendSurfaceList, removeFromSurfaceList, removeFromToBeDisplayedList } from '../selection/actions'; +import { generateMoleculeId, generateSurfaceObject } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { getRandomColor } from '../../components/preview/molecule/utils/color'; +import { deleteObject, loadObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; +import { getToBeDisplayedStructures } from './utils'; + +export const useDisplaySurfaceLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedSurfaces = useSelector(state => state.selectionReducers.surfaceList); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displaySurface = useCallback( + surfaceData => { + const data = allObservations.find(obs => obs.id === surfaceData.id); + const colourToggle = getRandomColor(data); + + dispatch(appendSurfaceList(generateMoleculeId(data))); + return dispatch( + loadObject({ + target: Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url)), + stage, + previousRepresentations: surfaceData.representations, + orientationMatrix: null, + preserveColour: surfaceData.preserveColour + }) + ).finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allObservations, dispatch, stage] + ); + + const removeSurface = useCallback( + surfaceData => { + const data = allObservations.find(obs => obs.id === surfaceData.id); + const colourToggle = getRandomColor(data); + + dispatch( + deleteObject( + Object.assign({ display_div: VIEWS.MAJOR_VIEW }, generateSurfaceObject(data, colourToggle, base_url)), + stage + ) + ); + dispatch(removeFromSurfaceList(generateMoleculeId(data))); + + dispatch(removeFromToBeDisplayedList({ id: surfaceData.id, type: NGL_OBJECTS.SURFACE })); + }, + [allObservations, dispatch, stage] + ); + + useEffect(() => { + const toBeDisplayedSurfaces = getToBeDisplayedStructures(toBeDisplayedList, displayedSurfaces, NGL_OBJECTS.SURFACE); + toBeDisplayedSurfaces?.forEach(data => { + displaySurface(data); + }); + + const toBeRemovedSurfaces = getToBeDisplayedStructures( + toBeDisplayedList, + displayedSurfaces, + NGL_OBJECTS.SURFACE, + true + ); + toBeRemovedSurfaces?.forEach(data => { + removeSurface(data); + }); + }, [toBeDisplayedList, displaySurface, dispatch, stage, removeSurface, displayedSurfaces]); + + return {}; +}; diff --git a/js/reducers/ngl/useDisplayVectorLHS.js b/js/reducers/ngl/useDisplayVectorLHS.js new file mode 100644 index 000000000..e2ca3afc5 --- /dev/null +++ b/js/reducers/ngl/useDisplayVectorLHS.js @@ -0,0 +1,114 @@ +import { useCallback, useContext, useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { NGL_OBJECTS } from './constants'; +import { + appendVectorOnList, + removeFromToBeDisplayedList, + removeFromVectorOnList, + setVectorList, + updateBondColorMapOfCompounds, + updateVectorCompounds +} from '../selection/actions'; +import { generateMoleculeId } from '../../components/nglView/generatingObjects'; +import { VIEWS } from '../../constants/constants'; +import { NglContext } from '../../components/nglView/nglProvider'; +import { deleteObject, setOrientation } from './dispatchActions'; +import { base_url } from '../../components/routes/constants'; +import { getToBeDisplayedStructures } from './utils'; +import { selectVectorAndResetCompounds } from '../selection/dispatchActions'; +import { api } from '../../utils/api'; +import { getViewUrl, handleVector, removeCurrentVector } from '../../components/preview/molecule/redux/dispatchActions'; + +export const useDisplayVectorLHS = () => { + const dispatch = useDispatch(); + + const toBeDisplayedList = useSelector(state => state.selectionReducers.toBeDisplayedList); + const displayedVectors = useSelector(state => state.selectionReducers.vectorOnList); + const allObservations = useSelector(state => state.apiReducers.all_mol_lists); + const currentVector = useSelector(state => state.selectionReducers.currentVector); + const vector_list = useSelector(state => state.selectionReducers.vector_list); + + const { getNglView } = useContext(NglContext); + const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; + + const displayVector = useCallback( + vectorData => { + const data = allObservations.find(obs => obs.id === vectorData.id); + + dispatch(appendVectorOnList(generateMoleculeId(data))); + dispatch(selectVectorAndResetCompounds(currentVector)); + + return api({ url: getViewUrl('graph', data) }) + .then(response => { + const result = response.data.graph; + const new_dict = {}; + // Uniquify + if (result) { + Object.keys(result).forEach(key => { + const smiSet = new Set(); + new_dict[key] = {}; + new_dict[key]['addition'] = []; + new_dict[key]['vector'] = result[key]['vector']; + Object.keys(result[key]['addition']).forEach(index => { + const newSmi = result[key]['addition'][index]['end']; + if (smiSet.has(newSmi) !== true) { + new_dict[key]['addition'].push(result[key]['addition'][index]); + smiSet.add(newSmi); + } + }); + }); + } + return dispatch(updateVectorCompounds(data.smiles, new_dict)); + }) + .then(() => api({ url: new URL(base_url + '/api/vector/?id=' + data.id) })) + .then(response => { + dispatch(handleVector(response.data?.results[0]?.vectors, stage, data)); + }) + .finally(() => { + const currentOrientation = stage.viewerControls.getOrientation(); + dispatch(setOrientation(VIEWS.MAJOR_VIEW, currentOrientation)); + }); + }, + [allObservations, currentVector, dispatch, stage] + ); + + const removeVector = useCallback( + vectorData => { + const data = allObservations.find(obs => obs.id === vectorData.id); + + vector_list + .filter(item => item.moleculeId === data.id) + .forEach(item => dispatch(deleteObject(Object.assign({ display_div: VIEWS.MAJOR_VIEW }, item), stage))); + + dispatch(removeCurrentVector(data.smiles)); + + dispatch(updateVectorCompounds(data.smiles, undefined)); + dispatch(updateBondColorMapOfCompounds(data.smiles, undefined)); + dispatch(removeFromVectorOnList(generateMoleculeId(data))); + + dispatch(setVectorList(vector_list.filter(item => item.moleculeId !== data.id))); + + dispatch(removeFromToBeDisplayedList({ id: vectorData.id, type: NGL_OBJECTS.VECTOR })); + }, + [allObservations, dispatch, stage, vector_list] + ); + + useEffect(() => { + const toBeDisplayedVectors = getToBeDisplayedStructures(toBeDisplayedList, displayedVectors, NGL_OBJECTS.VECTOR); + toBeDisplayedVectors?.forEach(data => { + displayVector(data); + }); + + const toBeRemovedVectors = getToBeDisplayedStructures( + toBeDisplayedList, + displayedVectors, + NGL_OBJECTS.VECTOR, + true + ); + toBeRemovedVectors?.forEach(data => { + removeVector(data); + }); + }, [toBeDisplayedList, displayVector, dispatch, stage, removeVector, displayedVectors]); + + return {}; +}; diff --git a/js/reducers/ngl/utils.js b/js/reducers/ngl/utils.js new file mode 100644 index 000000000..b00e209d3 --- /dev/null +++ b/js/reducers/ngl/utils.js @@ -0,0 +1,28 @@ +export const getToBeDisplayedStructures = (toBeDisplayed, displayedList, type, toBeRemoved = false) => { + let structures = []; + + structures = toBeDisplayed.filter( + struct => + struct.type === type && + struct.display === !toBeRemoved && + (toBeRemoved || !displayedList.find(id => id === struct.id)) + ); + + return structures; +}; + +export const getToBeDisplayedStructuresDataset = (toBeDisplayed, displayedList, type, toBeRemoved = false) => { + let result = []; + + Object.keys(toBeDisplayed).forEach(datasetID => { + const displayedDatasetLigands = displayedList[datasetID] || []; + const toBeDisplayedDataset = toBeDisplayed[datasetID] || []; + result = [ + ...result, + ...getToBeDisplayedStructures(toBeDisplayedDataset, displayedDatasetLigands, type, toBeRemoved) + ]; + // result.push(...getToBeDisplayedStructures(toBeDisplayedDataset, displayedDatasetLigands, type, toBeRemoved)); + }); + + return result; +}; diff --git a/js/reducers/selection/actions.js b/js/reducers/selection/actions.js index e4d708386..88e60669b 100644 --- a/js/reducers/selection/actions.js +++ b/js/reducers/selection/actions.js @@ -617,3 +617,31 @@ export const setTargetToEdit = target => { target: target }; }; + +export const setToBeDisplayedList = toBeDisplayedList => { + return { + type: constants.SET_TO_BE_DISPLAYED_LIST, + toBeDisplayedList: toBeDisplayedList + }; +}; + +export const appendToBeDisplayedList = item => { + return { + type: constants.APPEND_TO_BE_DISPLAYED_LIST, + item: item + }; +}; + +export const removeFromToBeDisplayedList = item => { + return { + type: constants.REMOVE_FROM_TO_BE_DISPLAYED_LIST, + item: item + }; +}; + +export const updateInToBeDisplayedList = item => { + return { + type: constants.UPDATE_IN_TO_BE_DISPLAYED_LIST, + item: item + }; +}; diff --git a/js/reducers/selection/constants.js b/js/reducers/selection/constants.js index 7255753a6..795001356 100644 --- a/js/reducers/selection/constants.js +++ b/js/reducers/selection/constants.js @@ -104,7 +104,12 @@ export const constants = { SET_UNSELECT_VISIBLE_POSES: prefix + 'SET_UNSELECT_VISIBLE_POSES', SET_OBSERVATION_DIALOG_ACTION: prefix + 'SET_OBSERVATION_DIALOG_ACTION', - SET_TARGET_TO_EDIT: prefix + 'SET_TARGET_TO_EDIT' + SET_TARGET_TO_EDIT: prefix + 'SET_TARGET_TO_EDIT', + + SET_TO_BE_DISPLAYED_LIST: prefix + 'SET_TO_BE_DISPLAYED_LIST', + APPEND_TO_BE_DISPLAYED_LIST: prefix + 'APPEND_TO_BE_DISPLAYED_LIST', + REMOVE_FROM_TO_BE_DISPLAYED_LIST: prefix + 'REMOVE_FROM_TO_BE_DISPLAYED_LIST', + UPDATE_IN_TO_BE_DISPLAYED_LIST: prefix + 'UPDATE_IN_TO_BE_DISPLAYED_LIST' }; export const PREDEFINED_FILTERS = { diff --git a/js/reducers/selection/selectionReducers.js b/js/reducers/selection/selectionReducers.js index 2b17daaa1..49ff18944 100644 --- a/js/reducers/selection/selectionReducers.js +++ b/js/reducers/selection/selectionReducers.js @@ -56,11 +56,48 @@ export const INITIAL_STATE = { areLSHCompoundsInitialized: false, toastMessages: [], isScrollFiredForLHS: false, - targetToEdit: null + targetToEdit: null, + // Shape of the object in toBeDisplayedList: + // { + // type: 'L|P|C|S|V|RIBBON|etc...', + // id: 1, + + // center: true, + // withQuality: true, + // representations: [] etc... + + // display: true + // } + toBeDisplayedList: [] }; export function selectionReducers(state = INITIAL_STATE, action = {}) { switch (action.type) { + case constants.SET_TO_BE_DISPLAYED_LIST: + return { ...state, toBeDisplayedList: action.toBeDisplayedList }; + + case constants.APPEND_TO_BE_DISPLAYED_LIST: + return { ...state, toBeDisplayedList: [...state.toBeDisplayedList, action.item] }; + + case constants.REMOVE_FROM_TO_BE_DISPLAYED_LIST: + return { + ...state, + toBeDisplayedList: state.toBeDisplayedList.filter( + item => item.id !== action.item.id || item.type !== action.item.type + ) + }; + + case constants.UPDATE_IN_TO_BE_DISPLAYED_LIST: + return { + ...state, + toBeDisplayedList: state.toBeDisplayedList.map(item => { + if (item.id === action.item.id && item.type === action.item.type) { + return { ...item, ...action.item }; + } + return item; + }) + }; + case constants.SET_SCROLL_FIRED_FOR_LHS: { return { ...state, isScrollFiredForLHS: action.isFired }; }