diff --git a/js/components/direct/constants.js b/js/components/direct/constants.js index 935e6e4c2..e00e6d004 100644 --- a/js/components/direct/constants.js +++ b/js/components/direct/constants.js @@ -3,5 +3,6 @@ export const URL_TOKENS = { molecules: 'mols', exact: 'exact', tag: 'tag', - target_access_string: 'tas' + target_access_string: 'tas', + compound: 'compound' }; diff --git a/js/components/direct/directDisplay.js b/js/components/direct/directDisplay.js index ac06da46e..71f1bb79f 100644 --- a/js/components/direct/directDisplay.js +++ b/js/components/direct/directDisplay.js @@ -34,7 +34,11 @@ export const DirectDisplay = memo(props => { useEffect(() => { // example url http://127.0.0.1:8080/viewer/react/preview/direct/target/MID2A/tas/lb00000/mols/X0301_0A/L/P/S/X0393_0B/L/P // example url with 'exact' https://fragalysis-tibor-default.xchem-dev.diamond.ac.uk/viewer/react/preview/direct/target/NUDT7A_CRUDE/mols/NUDT7A_CRUDE-X0156_1/exact/L/P - // based on the issues #431, #448, #447 + // in two cases above we are searching for molecules using shortcode + // Now the search term is looked up in the `shortcode`, `compound ID` and all of the `aliases` (I can change this pretty quickly) + // `http://127.0.0.1:8080/viewer/react/preview/direct/target/A71EV2A/tas/lb18145-1/compound/7516/L/S/nonsense-45/L/P/exact/Z4/L/C/A0853a/L/P` + // URL above shows `L` and `S` for observation which contains substring `7516`, `L` and `P` for observation which exactly has string `nonsense-45` as a shortcode, + // compound ID or one of the aliases, `L` and `C` for all observations which contain substring `Z4`, and `L` and `P` for observations which contains substring `A0853a` const param = match.params[0]; if (!directAccessProcessed && param && param.startsWith(URL_TOKENS.target)) { let withoutKeyword = param.split(URL_TOKENS.target); @@ -63,7 +67,13 @@ export const DirectDisplay = memo(props => { } } } - if (rest && rest.length > 1 && rest[0] === URL_TOKENS.molecules) { + if (rest && rest.length > 1 && (rest[0] === URL_TOKENS.molecules || rest[0] === URL_TOKENS.compound)) { + let searchSettings = { searchBy: {} }; + if (rest[0] === URL_TOKENS.molecules) { + searchSettings = { searchBy: { shortcode: true, aliases: false, compoundId: false } }; + } else if (rest[0] === URL_TOKENS.compound) { + searchSettings = { searchBy: { shortcode: true, aliases: true, compoundId: true } }; + } rest = rest.slice(1); let i; let currentMolecule; @@ -105,7 +115,8 @@ export const DirectDisplay = memo(props => { C: false, S: false, V: false, - exact: false + exact: false, + searchSettings: searchSettings }; molecules.push(currentMolecule); } @@ -119,7 +130,8 @@ export const DirectDisplay = memo(props => { C: false, S: false, V: false, - exact: false + exact: false, + searchSettings: searchSettings }; molecules.push(currentMolecule); } diff --git a/js/components/preview/Preview.js b/js/components/preview/Preview.js index 15c05b3c2..0f05b77e9 100644 --- a/js/components/preview/Preview.js +++ b/js/components/preview/Preview.js @@ -354,5 +354,5 @@ const Preview = memo(({ isStateLoaded, hideProjects, isSnapshot = false }) => { }); export default withLoadingJobSpecs( - withSnapshotManagement(withUpdatingTarget(withLoadingProtein(withLoadingProjects(Preview)))) + withLoadingProjects(withSnapshotManagement(withUpdatingTarget(withLoadingProtein(Preview)))) ); diff --git a/js/components/preview/molecule/hitNavigator.js b/js/components/preview/molecule/hitNavigator.js index 18aa9876b..0c6a8442f 100644 --- a/js/components/preview/molecule/hitNavigator.js +++ b/js/components/preview/molecule/hitNavigator.js @@ -2,7 +2,6 @@ * Created by abradley on 14/03/2018. */ import React, { memo } from 'react'; -import { MoleculeList } from './moleculeList'; import { ObservationCmpList } from './observationCmpList'; const HitNavigator = memo(({ hideProjects }) => { diff --git a/js/components/preview/molecule/moleculeList.js b/js/components/preview/molecule/moleculeList.js deleted file mode 100644 index 208277912..000000000 --- a/js/components/preview/molecule/moleculeList.js +++ /dev/null @@ -1,1257 +0,0 @@ -/** - * Created by abradley on 14/03/2018. - */ -import { - Grid, - Chip, - Tooltip, - makeStyles, - CircularProgress, - Divider, - Typography, - IconButton, - ButtonGroup -} from '@material-ui/core'; -import React, { useState, useEffect, useCallback, memo, useRef, useContext, useMemo } from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import MoleculeView from './moleculeView'; -import { colourList } from './utils/color'; -import { MoleculeListSortFilterDialog, filterMolecules, getAttrDefinition } from './moleculeListSortFilterDialog'; -import InfiniteScroll from 'react-infinite-scroller'; -import { Button } from '../../common/Inputs/Button'; -import { Panel } from '../../common/Surfaces/Panel'; -import { ComputeSize } from '../../../utils/computeSize'; -import { moleculeProperty } from './helperConstants'; -import { VIEWS } from '../../../constants/constants'; -import { NglContext } from '../../nglView/nglProvider'; -import classNames from 'classnames'; -import { - addVector, - removeVector, - addHitProtein, - removeHitProtein, - addComplex, - removeComplex, - addSurface, - removeSurface, - addDensity, - removeDensity, - addLigand, - removeLigand, - initializeMolecules, - applyDirectSelection, - addQuality, - removeQuality, - withDisabledMoleculesNglControlButtons, - removeSelectedTypesInHitNavigator, - selectAllHits, - autoHideTagEditorDialogsOnScroll, - searchForObservations -} from './redux/dispatchActions'; -import { DEFAULT_FILTER, PREDEFINED_FILTERS } from '../../../reducers/selection/constants'; -import { Edit, FilterList } from '@material-ui/icons'; -import { selectAllMoleculeList, selectJoinedMoleculeList } from './redux/selectors'; -import { MOL_ATTRIBUTES } from './redux/constants'; -import { - setFilter, - setMolListToEdit, - setNextXMolecules, - setMoleculeForTagEdit, - setTagEditorOpenObs -} from '../../../reducers/selection/actions'; -import { initializeFilter } from '../../../reducers/selection/dispatchActions'; -import * as listType from '../../../constants/listTypes'; -import { useRouteMatch } from 'react-router-dom'; -import { setSortDialogOpen, setSearchStringOfHitNavigator } from './redux/actions'; -import { AlertModal } from '../../common/Modal/AlertModal'; -import { setSelectedAllByType, setDeselectedAllByType, setIsTagGlobalEdit } from '../../../reducers/selection/actions'; -import { TagEditor } from '../tags/modal/tagEditor'; -import { getMoleculeForId, selectTag } from '../tags/redux/dispatchActions'; -import SearchField from '../../common/Components/SearchField'; -import useDisableNglControlButtons from './useDisableNglControlButtons'; -import GroupNglControlButtonsContext from './groupNglControlButtonsContext'; -import { extractTargetFromURLParam } from '../utils'; -import { LoadingContext } from '../../loading'; -import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; -import { useScrollToSelectedPose } from './useScrollToSelectedPose'; - -const useStyles = makeStyles(theme => ({ - container: { - minHeight: '100px', - height: '100%', - width: 'inherit', - color: theme.palette.black - }, - noOfSelectedHits: { - marginLeft: '5px' - }, - gridItemHeader: { - height: '32px', - fontSize: '8px', - color: '#7B7B7B' - }, - gridItemHeaderVert: { - transform: 'rotate(-90deg)', - height: 'fit-content' - }, - gridItemHeaderHoriz: { - width: 'fit-content' - }, - gridItemList: { - overflow: 'auto', - height: `calc(99% - ${theme.spacing(6)}px - ${theme.spacing(2)}px)` - }, - centered: { - display: 'flex', - alignItems: 'center', - justifyContent: 'center' - }, - button: { - minWidth: 'unset' - }, - buttonActive: { - border: 'solid 1px #009000', - color: '#009000', - '&:hover': { - backgroundColor: '#E3EEDA', - borderColor: '#003f00', - color: '#003f00' - } - }, - paddingProgress: { - padding: theme.spacing(1) - }, - filterSection: { - marginTop: theme.spacing(1), - marginBottom: theme.spacing(1) - }, - filterTitle: { - transform: 'rotate(-90deg)' - }, - molHeader: { - marginLeft: 19, - width: 'inherit' - }, - rightBorder: { - borderRight: '1px solid', - borderRightColor: theme.palette.background.divider, - fontWeight: 'bold', - paddingLeft: theme.spacing(1) / 2, - paddingRight: theme.spacing(1) / 2, - paddingTop: theme.spacing(1), - paddingBottom: theme.spacing(1), - fontSize: 8, - width: 25, - textAlign: 'center', - '&:last-child': { - borderRight: 'none', - width: 32 - } - }, - contButtonsMargin: { - marginTop: theme.spacing(1) / 2, - marginBottom: theme.spacing(1) / 2, - marginLeft: theme.spacing(2) - }, - contColButton: { - minWidth: 'fit-content', - paddingLeft: theme.spacing(1) / 4, - paddingRight: theme.spacing(1) / 4, - paddingBottom: 0, - paddingTop: 0, - fontWeight: 'bold', - fontSize: 9, - borderRadius: 0, - borderColor: theme.palette.primary.main, - backgroundColor: theme.palette.primary.light, - '&:hover': { - backgroundColor: theme.palette.primary.light - // color: theme.palette.primary.contrastText - }, - '&:disabled': { - borderRadius: 0, - borderColor: 'white' - } - }, - contColButtonUnselected: { - minWidth: 'fit-content', - paddingLeft: theme.spacing(1) / 4, - paddingRight: theme.spacing(1) / 4, - paddingBottom: 0, - paddingTop: 0, - fontWeight: 'bold', - fontSize: 9, - borderRadius: 0, - borderColor: theme.palette.primary.main, - backgroundColor: theme.palette.primary.light, - '&:hover': { - backgroundColor: theme.palette.primary.light - // color: theme.palette.primary.contrastText - }, - '&:disabled': { - borderRadius: 0, - borderColor: 'white', - color: 'white' - } - }, - - contColButtonSelected: { - backgroundColor: theme.palette.primary.main, - color: theme.palette.primary.contrastText, - '&:hover': { - backgroundColor: theme.palette.primary.main - // color: theme.palette.black - } - }, - contColButtonHalfSelected: { - backgroundColor: theme.palette.primary.semidark, - color: theme.palette.primary.contrastText, - '&:hover': { - backgroundColor: theme.palette.primary.semidark - //color: theme.palette.black - } - }, - formControl: { - color: 'inherit', - margin: theme.spacing(1), - width: 87 - // fontSize: '1.2rem' - }, - select: { - color: 'inherit', - fill: 'inherit', - '&:hover:not(.Mui-disabled):before': { - borderColor: 'inherit' - }, - '&:before': { - borderColor: 'inherit' - }, - '&:not(.Mui-disabled)': { - fill: theme.palette.white - } - }, - selectIcon: { - fill: 'inherit' - }, - search: { - width: 150 - }, - total: { - ...theme.typography.button, - color: theme.palette.primary.main, - fontStyle: 'italic' - } -})); -let selectedDisplayHits = false; - -export const MoleculeList = memo(({ hideProjects }) => { - const classes = useStyles(); - const dispatch = useDispatch(); - let match = useRouteMatch(); - // let target = match && match.params && match.params.target; - let target = match && match.params && extractTargetFromURLParam(match.params[0]); - - const nextXMolecules = useSelector(state => state.selectionReducers.nextXMolecules); - const [selectAllHitsPressed, setSelectAllHitsPressed] = useState(false); - const [selectDisplayedHitsPressed, setSelectDisplayedHitsPressed] = useState(false); - const moleculesPerPage = 30; - const [currentPage, setCurrentPage] = useState(0); - const searchString = useSelector(state => state.previewReducers.molecule.searchStringLHS); - // const [searchString, setSearchString] = useState(null); - const [sortDialogAnchorEl, setSortDialogAnchorEl] = useState(null); - const oldUrl = useRef(''); - const setOldUrl = url => { - oldUrl.current = url; - }; - const list_type = listType.MOLECULE; - const imgHeight = 49; - const imgWidth = 150; - const sortDialogOpen = useSelector(state => state.previewReducers.molecule.sortDialogOpen); - const filter = useSelector(state => state.selectionReducers.filter); - const getJoinedMoleculeList = useSelector(state => selectJoinedMoleculeList(state)); - const allMoleculesList = useSelector(state => selectAllMoleculeList(state)); - - const selectedAll = useRef(false); - - const proteinList = useSelector(state => state.selectionReducers.proteinList); - const complexList = useSelector(state => state.selectionReducers.complexList); - const fragmentDisplayList = useSelector(state => state.selectionReducers.fragmentDisplayList); - const surfaceList = useSelector(state => state.selectionReducers.surfaceList); - const densityList = useSelector(state => state.selectionReducers.densityList); - const densityListCustom = useSelector(state => state.selectionReducers.densityListCustom); - const qualityList = useSelector(state => state.selectionReducers.qualityList); - const vectorOnList = useSelector(state => state.selectionReducers.vectorOnList); - const informationList = useSelector(state => state.selectionReducers.informationList); - const isTagEditorOpenObs = useSelector(state => state.selectionReducers.tagEditorOpenedObs); - const molForTagEditId = useSelector(state => state.selectionReducers.molForTagEdit); - const moleculesToEditIds = useSelector(state => state.selectionReducers.moleculesToEdit); - const isGlobalEdit = useSelector(state => state.selectionReducers.isGlobalEdit); - - const object_selection = useSelector(state => state.selectionReducers.mol_group_selection); - - const searchSettings = useSelector(state => state.selectionReducers.searchSettings); - - const all_mol_lists = useSelector(state => state.apiReducers.all_mol_lists); - const directDisplay = useSelector(state => state.apiReducers.direct_access); - const directAccessProcessed = useSelector(state => state.apiReducers.direct_access_processed); - const tags = useSelector(state => state.apiReducers.tagList); - const noTagsReceived = useSelector(state => state.apiReducers.noTagsReceived); - const categories = useSelector(state => state.apiReducers.categoryList); - - const proteinsHasLoaded = useSelector(state => state.nglReducers.proteinsHasLoaded); - - const [predefinedFilter, setPredefinedFilter] = useState(filter !== undefined ? filter.predefined : DEFAULT_FILTER); - - const isActiveFilter = !!(filter || {}).active; - - const { getNglView } = useContext(NglContext); - const { moleculesAndTagsAreLoading } = useContext(LoadingContext); - const majorViewStage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; - - const filterRef = useRef(); - const tagEditorRef = useRef(); - const scrollBarRef = useRef(); - const [tagEditorAnchorEl, setTagEditorAnchorEl] = useState(null); - - if (directDisplay && directDisplay.target) { - target = directDisplay.target; - } - - let selectedMolecule = []; - // TODO: Reset Infinity scroll - /*useEffect(() => { - // setCurrentPage(0); - }, [object_selection]);*/ - - let joinedMoleculeLists = useMemo(() => { - if (searchString) { - // return allMoleculesList.filter(molecule => molecule.code.toLowerCase().includes(searchString.toLowerCase())); - return dispatch(searchForObservations(searchString, allMoleculesList, searchSettings)); - } else { - return getJoinedMoleculeList; - } - }, [searchString, dispatch, allMoleculesList, searchSettings, getJoinedMoleculeList]); - - const addSelectedMoleculesFromUnselectedSites = useCallback( - (joinedMoleculeLists, list) => { - const addedMols = [...joinedMoleculeLists]; - const onlyAlreadySelected = []; - list?.forEach(moleculeID => { - const foundJoinedMolecule = addedMols.find(mol => mol.id === moleculeID); - if (!foundJoinedMolecule) { - const molecule = allMoleculesList.find(mol => mol.id === moleculeID); - if (molecule) { - addedMols.push(molecule); - onlyAlreadySelected.push(molecule); - } - } - }); - - const result = [...onlyAlreadySelected, ...joinedMoleculeLists]; - return result; - }, - [allMoleculesList] - ); - - //the dependencies which are marked by compiler as unnecessary are actually necessary because without them the memo returns - //old joinedMoleculeLists in situation where we want to preserve molecule in view which shouldn't be there - //but want to remove it after the tag editor dialog is closed - // eslint-disable-next-line react-hooks/exhaustive-deps - joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, proteinList), [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - proteinList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, complexList), [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - complexList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ]); - joinedMoleculeLists = useMemo( - () => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, fragmentDisplayList), - // eslint-disable-next-line react-hooks/exhaustive-deps - [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - fragmentDisplayList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ] - ); - // eslint-disable-next-line react-hooks/exhaustive-deps - joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, surfaceList), [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - surfaceList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, densityList), [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - densityList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ]); - // eslint-disable-next-line react-hooks/exhaustive-deps - joinedMoleculeLists = useMemo(() => addSelectedMoleculesFromUnselectedSites(joinedMoleculeLists, vectorOnList), [ - addSelectedMoleculesFromUnselectedSites, - joinedMoleculeLists, - vectorOnList, - molForTagEditId, - isTagEditorOpenObs, - moleculesToEditIds - ]); - - if (isActiveFilter) { - joinedMoleculeLists = filterMolecules(joinedMoleculeLists, filter); - } - - const loadNextMolecules = () => { - setCurrentPage(currentPage + 1); - }; - - if (molForTagEditId && !joinedMoleculeLists.some(m => m.id === molForTagEditId.some(mid => m.id === mid))) { - molForTagEditId.forEach(mid => { - const tagEditMol = dispatch(getMoleculeForId(mid)); - if (tagEditMol) { - joinedMoleculeLists.push(tagEditMol); - } - }); - // joinedMoleculeLists = [tagEditMol, ...joinedMoleculeLists]; - joinedMoleculeLists.sort((a, b) => { - if (a.code < b.code) { - return -1; - } - if (a.code > b.code) { - return 1; - } - return 0; - }); - } - - if (moleculesToEditIds && moleculesToEditIds.length > 0 && isGlobalEdit) { - moleculesToEditIds.forEach(mid => { - if (!joinedMoleculeLists.some(m => m.id === mid)) { - const tagEditMol = dispatch(getMoleculeForId(mid)); - if (tagEditMol) { - joinedMoleculeLists.push(tagEditMol); - } - } - }); - joinedMoleculeLists.sort((a, b) => { - if (a.code < b.code) { - return -1; - } - if (a.code > b.code) { - return 1; - } - return 0; - }); - } - - const listItemOffset = (currentPage + 1) * moleculesPerPage + nextXMolecules; - const canLoadMore = listItemOffset < joinedMoleculeLists.length; - - const wereMoleculesInitialized = useRef(false); - - useEffect(() => { - if ((proteinsHasLoaded === true || proteinsHasLoaded === null) && all_mol_lists.length > 0) { - if (!directAccessProcessed && directDisplay && directDisplay.molecules && directDisplay.molecules.length > 0) { - dispatch(applyDirectSelection(majorViewStage)); - wereMoleculesInitialized.current = true; - } - if ( - majorViewStage && - all_mol_lists && - hideProjects && - target !== undefined && - wereMoleculesInitialized.current === false && - tags && - tags.length > 0 && - categories && - categories.length > 0 - ) { - dispatch(initializeFilter(object_selection, joinedMoleculeLists)); - // dispatch(initializeMolecules(majorViewStage)); - wereMoleculesInitialized.current = true; - } - if ( - majorViewStage && - all_mol_lists && - target !== undefined && - wereMoleculesInitialized.current === false && - noTagsReceived - ) { - dispatch(initializeFilter(object_selection, joinedMoleculeLists)); - // dispatch(initializeMolecules(majorViewStage)); - wereMoleculesInitialized.current = true; - } - } - }, [ - list_type, - majorViewStage, - dispatch, - hideProjects, - target, - proteinsHasLoaded, - joinedMoleculeLists, - all_mol_lists, - directDisplay, - directAccessProcessed, - object_selection, - tags, - categories, - noTagsReceived - ]); - - const joinedMoleculeListsCopy = useMemo(() => [...joinedMoleculeLists], [joinedMoleculeLists]); - - // useEffect(() => { - // if (!joinedMoleculeListsCopy.length) { - // dispatch(setSortDialogOpen(false)); - // } - // }, [dispatch, joinedMoleculeListsCopy.length]); - - const handleFilterChange = filter => { - const filterSet = Object.assign({}, filter); - for (let attr of MOL_ATTRIBUTES) { - if (filterSet.filter[attr.key].priority === undefined || filterSet.filter[attr.key].priority === '') { - filterSet.filter[attr.key].priority = 0; - } - } - dispatch(setFilter(filterSet)); - }; - - const allSelectedMolecules = useMemo( - () => - allMoleculesList.filter( - molecule => moleculesToEditIds.includes(molecule.id) /*|| molForTagEditId.some(mid => molecule.id === mid)*/ - ), - [allMoleculesList, moleculesToEditIds /*, molForTagEditId*/] - ); - - let currentMolecules = joinedMoleculeLists.slice(0, listItemOffset); - if ( - fragmentDisplayList.length === 0 && - proteinList.length === 0 && - complexList.length === 0 && - surfaceList.length === 0 && - densityList.length === 0 && - vectorOnList.length === 0 - ) { - if (allSelectedMolecules.length === 0) { - selectedDisplayHits = false; - } - } else { - if (allSelectedMolecules.length === 0) { - selectedDisplayHits = false; - } else { - if (allSelectedMolecules.length !== 0) { - for (let i = 0; i < allSelectedMolecules.length; i++) { - const selectedMolecule = allSelectedMolecules[i]; - if ( - fragmentDisplayList.includes(selectedMolecule.id) || - proteinList.includes(selectedMolecule.id) || - complexList.includes(selectedMolecule.id) || - surfaceList.includes(selectedMolecule.id) || - densityList.includes(selectedMolecule.id) || - vectorOnList.includes(selectedMolecule.id) - ) { - selectedDisplayHits = true; - } else { - selectedDisplayHits = false; - break; - } - } - if (selectedDisplayHits) { - const notSelectedMols = []; - const danglingFrags = fragmentDisplayList.filter( - id => !allSelectedMolecules.filter(m => m.id === id).length > 0 - ); - if (danglingFrags && danglingFrags.length > 0) { - notSelectedMols.push(danglingFrags); - } - const danglingProteins = proteinList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); - if (danglingProteins && danglingProteins.length > 0) { - notSelectedMols.push(danglingProteins); - } - const danglingComplexes = complexList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); - if (danglingComplexes && danglingComplexes.length > 0) { - notSelectedMols.push(danglingComplexes); - } - const danglingSurfaces = surfaceList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); - if (danglingSurfaces && danglingSurfaces.length > 0) { - notSelectedMols.push(danglingSurfaces); - } - const danglingDensities = densityList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); - if (danglingDensities && danglingDensities.length > 0) { - notSelectedMols.push(danglingDensities); - } - const danglingVectors = vectorOnList.filter(id => !allSelectedMolecules.filter(m => m.id === id).length > 0); - if (danglingVectors && danglingVectors.length > 0) { - notSelectedMols.push(danglingVectors); - } - if (notSelectedMols && notSelectedMols.length > 0) { - selectedDisplayHits = false; - } - } - } - } - } - - joinedMoleculeListsCopy.map(data => { - if (fragmentDisplayList.includes(data.id)) { - selectedMolecule.push(data); - } - if (proteinList.includes(data.id)) { - selectedMolecule.push(data); - } - if (complexList.includes(data.id)) { - selectedMolecule.push(data); - } - if (surfaceList.includes(data.id)) { - selectedMolecule.push(data); - } - if (densityList.includes(data.id)) { - selectedMolecule.push(data); - } - if (vectorOnList.includes(data.id)) { - selectedMolecule.push(data); - } - }); - const uniqueSelectedMoleculeForHitNavigator = [...new Set(selectedMolecule)]; - - const newMolsToEdit = []; - currentMolecules.forEach(cm => { - if (moleculesToEditIds.includes(cm.id)) { - newMolsToEdit.push(cm.id); - } - }); - if (newMolsToEdit.length !== moleculesToEditIds.length) { - dispatch(setMolListToEdit(newMolsToEdit)); - } - - const changePredefinedFilter = event => { - let newFilter = Object.assign({}, filter); - - const preFilterKey = event.target.value; - setPredefinedFilter(preFilterKey); - - if (preFilterKey !== 'none') { - newFilter.active = true; - newFilter.predefined = preFilterKey; - Object.keys(PREDEFINED_FILTERS[preFilterKey].filter).forEach(attr => { - const maxValue = PREDEFINED_FILTERS[preFilterKey].filter[attr]; - newFilter.filter[attr].maxValue = maxValue; - newFilter.filter[attr].max = newFilter.filter[attr].max < maxValue ? maxValue : newFilter.filter[attr].max; - }); - dispatch(setFilter(newFilter)); - } else { - // close filter dialog options - setSortDialogAnchorEl(null); - dispatch(setSortDialogOpen(false)); - // reset filter - dispatch(setFilter(undefined)); - newFilter = dispatch(initializeFilter(object_selection, joinedMoleculeLists)); - } - // currently do not filter molecules by excluding them - /*setFilteredCount(getFilteredMoleculesCount(getListedMolecules(object_selection, cached_mol_lists), newFilter)); - handleFilterChange(newFilter);*/ - }; - - const joinedGivenMatch = useCallback( - givenList => { - return givenList.filter(element => allSelectedMolecules.filter(element2 => element2.id === element).length > 0) - .length; - }, - [allSelectedMolecules] - ); - - const joinedLigandMatchLength = useMemo(() => joinedGivenMatch(fragmentDisplayList), [ - fragmentDisplayList, - joinedGivenMatch - ]); - const joinedProteinMatchLength = useMemo(() => joinedGivenMatch(proteinList), [proteinList, joinedGivenMatch]); - const joinedComplexMatchLength = useMemo(() => joinedGivenMatch(complexList), [complexList, joinedGivenMatch]); - - const changeButtonClassname = (givenList = [], matchListLength) => { - if (!matchListLength) { - return false; - } else if (allSelectedMolecules.length === matchListLength) { - return true; - } - return null; - }; - - const isLigandOn = changeButtonClassname(fragmentDisplayList, joinedLigandMatchLength); - const isProteinOn = changeButtonClassname(proteinList, joinedProteinMatchLength); - const isComplexOn = changeButtonClassname(complexList, joinedComplexMatchLength); - - const addType = { - ligand: addLigand, - protein: addHitProtein, - complex: addComplex, - surface: addSurface, - quality: addQuality, - density: addDensity, - vector: addVector - }; - - const removeType = { - ligand: removeLigand, - protein: removeHitProtein, - complex: removeComplex, - surface: removeSurface, - quality: removeQuality, - density: removeDensity, - vector: removeVector - }; - - // TODO: "currentMolecules" do not need to correspondent to selections in {type}List - // TODO: so this could lead to inconsistend behaviour while scrolling - // TODO: maybe change "currentMolecules.forEach" to "{type}List.forEach" - - const removeSelectedType = (type, skipTracking = false) => { - if (type === 'ligand') { - allSelectedMolecules.forEach(molecule => { - dispatch(removeType[type](majorViewStage, molecule, skipTracking)); - }); - } else { - allSelectedMolecules.forEach(molecule => { - dispatch(removeType[type](majorViewStage, molecule, colourList[molecule.id % colourList.length], skipTracking)); - }); - } - - selectedAll.current = false; - }; - - const removeSelectedTypes = useCallback( - (skipMolecules = [], skipTracking = false) => { - dispatch(removeSelectedTypesInHitNavigator(skipMolecules, majorViewStage, skipTracking)); - }, - [dispatch, majorViewStage] - ); - - const selectMoleculeTags = moleculeTagsSet => { - const moleculeTags = tags.filter(tag => moleculeTagsSet.includes(tag.id)); - moleculeTags.forEach(tag => { - dispatch(selectTag(tag)); - }); - }; - - const addNewType = (type, skipTracking = false) => { - dispatch( - withDisabledMoleculesNglControlButtons( - allSelectedMolecules.map(molecule => molecule.id), - type, - async () => { - const promises = []; - - if (type === 'ligand') { - allSelectedMolecules.forEach(molecule => { - //selectMoleculeTags(molecule.tags_set); - - promises.push( - dispatch( - addType[type]( - majorViewStage, - molecule, - colourList[molecule.id % colourList.length], - false, - true, - skipTracking - ) - ) - ); - }); - } else { - allSelectedMolecules.forEach(molecule => { - //selectMoleculeTags(molecule.tags_set); - promises.push( - dispatch( - addType[type](majorViewStage, molecule, colourList[molecule.id % colourList.length], skipTracking) - ) - ); - }); - } - - await Promise.all(promises); - } - ) - ); - }; - - const ucfirst = string => { - return string.charAt(0).toUpperCase() + string.slice(1); - }; - - const onButtonToggle = (type, calledFromSelectAll = false) => { - setLastProcessedLPCType(type); - if (calledFromSelectAll === true && selectedAll.current === true) { - // REDO - if (eval('is' + ucfirst(type) + 'On') === false) { - addNewType(type, true); - } - } else if (calledFromSelectAll && selectedAll.current === false) { - removeSelectedType(type, true); - } else if (!calledFromSelectAll) { - if (eval('is' + ucfirst(type) + 'On') === false) { - let molecules = getSelectedMoleculesByType(type, true); - if (molecules && molecules.length > 100) { - setIsOpenLPCAlert(true); - } else { - dispatch(setSelectedAllByType(type, molecules)); - addNewType(type, true); - } - } else { - let molecules = getSelectedMoleculesByType(type, false); - dispatch(setDeselectedAllByType(type, molecules)); - removeSelectedType(type, true); - } - } - }; - - const getSelectedMoleculesByType = (type, isAdd) => { - switch (type) { - case 'ligand': - return isAdd ? getMoleculesToSelect(fragmentDisplayList) : getMoleculesToDeselect(fragmentDisplayList); - case 'protein': - return isAdd ? getMoleculesToSelect(proteinList) : getMoleculesToDeselect(proteinList); - case 'complex': - return isAdd ? getMoleculesToSelect(complexList) : getMoleculesToDeselect(complexList); - default: - return null; - } - }; - - const getMoleculesToSelect = list => { - let molecules = allSelectedMolecules.filter(m => !list.includes(m.id)); - return molecules; - }; - - const getMoleculesToDeselect = list => { - let molecules = allSelectedMolecules.filter(m => list.includes(m.id)); - return molecules; - }; - - const openGlobalTagEditor = () => {}; - - const actions = [ - { - // setSearchString(value); - dispatch(setSearchStringOfHitNavigator(value)); - }} - disabled={false || (getJoinedMoleculeList && getJoinedMoleculeList.length === 0)} - // searchString={filterSearchString?.searchStringHitNavigator ?? ''} - searchString={searchString ?? ''} - placeholder={'Search displayed hits'} - />, - - { - if (isTagEditorOpenObs === false) { - setTagEditorAnchorEl(event.currentTarget); - dispatch(setIsTagGlobalEdit(true)); - dispatch(setTagEditorOpenObs(true)); - } else { - setTagEditorAnchorEl(null); - dispatch(setIsTagGlobalEdit(false)); - dispatch(setTagEditorOpenObs(false)); - } - }} - > - - - - , - { - if (sortDialogOpen === false) { - setSortDialogAnchorEl(event.currentTarget); - dispatch(setSortDialogOpen(true)); - } else { - setSortDialogAnchorEl(null); - dispatch(setSortDialogOpen(false)); - } - }} - color={'inherit'} - disabled={/*!joinedMoleculeListsCopy.length || */ predefinedFilter !== 'none'} - > - - - - - ]; - - const [isOpenAlert, setIsOpenAlert] = useState(false); - const [isOpenLPCAlert, setIsOpenLPCAlert] = useState(false); - const [lastProcessedLPCType, setLastProcessedLPCType] = useState(null); - - const groupNglControlButtonsDisabledState = useDisableNglControlButtons(allSelectedMolecules); - - const anyControlButtonDisabled = Object.values(groupNglControlButtonsDisabledState).some(buttonState => buttonState); - - return ( - - { - dispatch(setNextXMolecules(joinedMoleculeLists?.length || 0)); - setIsOpenAlert(false); - }} - handleOnCancel={() => { - setIsOpenAlert(false); - }} - /> - { - let molecules = getSelectedMoleculesByType(lastProcessedLPCType, true); - dispatch(setSelectedAllByType(lastProcessedLPCType, molecules)); - addNewType(lastProcessedLPCType, true); - setIsOpenLPCAlert(false); - }} - handleOnCancel={() => { - setIsOpenLPCAlert(false); - }} - /> - {isTagEditorOpenObs && ( - - )} - {sortDialogOpen && ( - - )} -
- {isActiveFilter && ( - <> -
- - - - Filters - - - - - {filter.priorityOrder.map(attr => ( - - - - - - ))} - - - -
- - - )} -
- - - - {/* Tooltip should not have disabled element as a direct child */} - <> - - - - - {/* Tooltip should not have disabled element as a direct child */} - <> - - - - - {/* Tooltip should not have disabled element as a direct child */} - <> - {/* C stands for contacts now */} - - - - - - { - - - - - - } - {selectedDisplayHits === true ? ( - - - - - - ) : ( - - - - - - )} - - {`Selected: ${ - allSelectedMolecules ? allSelectedMolecules.length : 0 - }`} - - - - - {/* Header */} - - - {/* {Object.keys(moleculeProperty).map(key => ( - - {moleculeProperty[key]} - - ))} */} - - - - {console.log('tagEditorRef', tagEditorRef)} - {currentMolecules.length > 0 && ( - <> - - - // dispatch( - // autoHideTagEditorDialogsOnScroll({ - // tagEditorRef, - // scrollBarRef - // }) - // ) - // } - pageStart={0} - loadMore={loadNextMolecules} - hasMore={canLoadMore} - loader={ -
- - - -
- } - useWindow={false} - > - - {currentMolecules.map((data, index, array) => { - const selected = allSelectedMolecules.some(molecule => molecule.id === data.id); - const isTagEditorInvokedByMolecule = molForTagEditId.some(mid => data.id === mid); - - return ( - 0 && array[index - 1]} - nextItemData={index < array?.length && array[index + 1]} - setRef={setTagEditorAnchorEl} - removeSelectedTypes={removeSelectedTypes} - L={fragmentDisplayList.includes(data.id)} - P={proteinList.includes(data.id)} - C={complexList.includes(data.id)} - S={surfaceList.includes(data.id)} - D={densityList.includes(data.id)} - D_C={densityListCustom.includes(data.id)} - Q={qualityList.includes(data.id)} - V={vectorOnList.includes(data.id)} - I={informationList.includes(data.id)} - eventInfo={data?.proteinData?.event_info || null} - sigmaaInfo={data?.proteinData?.sigmaa_info || null} - diffInfo={data?.proteinData?.diff_info || null} - isTagEditorInvokedByMolecule={isTagEditorInvokedByMolecule} - isTagEditorOpen={isTagEditorInvokedByMolecule && isTagEditorOpenObs} - selected={selected} - disableL={selected && groupNglControlButtonsDisabledState.ligand} - disableP={selected && groupNglControlButtonsDisabledState.protein} - disableC={selected && groupNglControlButtonsDisabledState.complex} - /> - ); - })} - -
-
- - - - {`Total ${joinedMoleculeLists?.length}`} - - - - - - - - - - - - )} - {moleculesAndTagsAreLoading && ( - - - - - - )} -
-
- ); -}); diff --git a/js/components/preview/molecule/redux/dispatchActions.js b/js/components/preview/molecule/redux/dispatchActions.js index eb5f3d961..691895ef3 100644 --- a/js/components/preview/molecule/redux/dispatchActions.js +++ b/js/components/preview/molecule/redux/dispatchActions.js @@ -503,8 +503,9 @@ export const initializeMolecules = majorView => (dispatch, getState) => { const state = getState(); const noTagsReceived = state.apiReducers.noTagsReceived; const isSnapshot = state.apiReducers.isSnapshot; + const isDirectDisplay = Object.keys(state.apiReducers.direct_access || {})?.length > 0; - if (!isSnapshot) { + if (!isSnapshot && !isDirectDisplay) { const firstTag = dispatch(getFirstTagAlphabetically()); let firstMolecule = null; if (firstTag) { @@ -884,15 +885,11 @@ export const applyDirectSelection = stage => (dispatch, getState) => { if (!directAccessProcessed && directDisplay && directDisplay.molecules && directDisplay.molecules.length > 0) { const allMols = state.apiReducers.all_mol_lists; directDisplay.molecules.forEach(m => { - let directProteinNameModded = m.name.toLowerCase(); - let directProteinCodeModded = `${directDisplay.target.toLowerCase()}-${directProteinNameModded}`; - for (let molIndex = 0; molIndex < allMols.length; molIndex++) { - let molList = allMols; - let mol = molList[molIndex]; - let proteinCodeModded = mol.code.toLowerCase(); - if ( - m.exact ? proteinCodeModded === directProteinCodeModded : proteinCodeModded.includes(directProteinNameModded) - ) { + // let directProteinNameModded = m.name.toLowerCase(); + // let directProteinCodeModded = `${directDisplay.target.toLowerCase()}-${directProteinNameModded}`; + const foundMols = dispatch(searchForObservations(m.name, allMols, m.searchSettings, m.exact)); + foundMols?.forEach(mol => { + if (mol) { if (m.L && !fragmentDisplayList.includes(mol.id)) { dispatch(addLigand(stage, mol, colourList[mol.id % colourList.length], true)); } @@ -909,7 +906,7 @@ export const applyDirectSelection = stage => (dispatch, getState) => { dispatch(addVector(stage, mol, colourList[mol.id % colourList.length])); } } - } + }); }); // dispatch(setDirectAccess({})); dispatch(setDirectAccessProcessed(true)); @@ -1217,18 +1214,33 @@ export const createPose = newPose => async (dispatch, getState) => { }; const observationSearchFunctions = { - shortcode: (obs, searchTerm) => { - return obs.code?.toLowerCase().includes(searchTerm.toLowerCase()); + shortcode: (obs, searchTerm, exact = false) => { + if (exact) { + return obs?.code && obs.code.toLowerCase() === searchTerm.toLowerCase(); + } else { + return obs?.code?.toLowerCase().includes(searchTerm.toLowerCase()); + } }, - aliases: (obs, searchTerm) => { - return obs.identifiers?.some(idf => idf.name.toLowerCase().includes(searchTerm.toLowerCase())); + aliases: (obs, searchTerm, exact = false) => { + if (exact) { + return obs?.identifiers?.some(idf => idf.name.toLowerCase() === searchTerm.toLowerCase()); + } else { + return obs?.identifiers?.some(idf => idf.name.toLowerCase().includes(searchTerm.toLowerCase())); + } }, - compoundId: (obs, searchTerm) => { - return obs.compound_code?.toLowerCase().includes(searchTerm.toLowerCase()); + compoundId: (obs, searchTerm, exact = false) => { + if (exact) { + return obs?.compound_code && obs.compound_code.toLowerCase() === searchTerm.toLowerCase(); + } else { + return obs?.compound_code?.toLowerCase().includes(searchTerm.toLowerCase()); + } } }; -export const searchForObservations = (searchTerm, observations, searchSettings) => (dispatch, getState) => { +export const searchForObservations = (searchTerm, observations, searchSettings, exact = false) => ( + dispatch, + getState +) => { if (!observations || observations.length === 0) return []; if (!searchTerm) return observations; diff --git a/js/components/target/withLoadingTargetIdList.js b/js/components/target/withLoadingTargetIdList.js index 86146bb3a..52d4bfa29 100644 --- a/js/components/target/withLoadingTargetIdList.js +++ b/js/components/target/withLoadingTargetIdList.js @@ -1,10 +1,13 @@ import React, { memo, useEffect } from 'react'; -import { useDispatch } from 'react-redux'; -import { loadLegacyTargetList, loadTargetList } from './redux/dispatchActions'; +import { useDispatch, useSelector } from 'react-redux'; +import { getTargetProjectCombinations, loadLegacyTargetList, loadTargetList } from './redux/dispatchActions'; +import { setTargetIdList } from '../../reducers/api/actions'; export const withLoadingTargetList = WrappedComponent => { return memo(() => { const dispatch = useDispatch(); + const targetIdList = useSelector(state => state.apiReducers.target_id_list); + const projects = useSelector(state => state.targetReducers.projects); useEffect(() => { let onCancel = () => {}; @@ -17,6 +20,17 @@ export const withLoadingTargetList = WrappedComponent => { }; }, [dispatch]); + useEffect(() => { + if (targetIdList && targetIdList.length > 0 && projects && projects.length > 0) { + const firstTarget = targetIdList[0]; + if (typeof firstTarget.project !== 'object') { + const combinations = getTargetProjectCombinations(targetIdList, projects); + const updatedTargets = combinations.map(c => c.updatedTarget); + dispatch(setTargetIdList(updatedTargets)); + } + } + }, [dispatch, targetIdList, projects]); + return ; }); }; diff --git a/js/reducers/selection/selectionReducers.js b/js/reducers/selection/selectionReducers.js index 5af8eaaab..2e2a903a8 100644 --- a/js/reducers/selection/selectionReducers.js +++ b/js/reducers/selection/selectionReducers.js @@ -73,8 +73,8 @@ export const INITIAL_STATE = { searchSettings: { searchBy: { shortcode: true, - aliases: false, - compoundId: false + aliases: true, + compoundId: true } } };