diff --git a/js/components/common/Components/SearchField/index.js b/js/components/common/Components/SearchField/index.js index 289f28649..8b0543b58 100644 --- a/js/components/common/Components/SearchField/index.js +++ b/js/components/common/Components/SearchField/index.js @@ -1,5 +1,5 @@ import React, { useMemo } from 'react'; -import { makeStyles, TextField, InputAdornment } from '@material-ui/core'; +import { makeStyles, TextField, InputAdornment, IconButton } from '@material-ui/core'; import { Search } from '@material-ui/icons'; import classNames from 'classnames'; import { debounce } from 'lodash'; @@ -31,9 +31,18 @@ const useStyles = makeStyles(theme => ({ } })); -const SearchField = ({ className, id, placeholder, size, onChange, disabled, searchString }) => { +const SearchField = ({ + className, + id, + placeholder, + size, + onChange, + disabled, + searchString, + searchIconAction = null +}) => { const classes = useStyles(); - let value = searchString ?? ''; + let value = searchString ?? ''; const debounced = useMemo( () => @@ -57,7 +66,17 @@ const SearchField = ({ className, id, placeholder, size, onChange, disabled, sea InputProps={{ startAdornment: ( - + {searchIconAction ? ( + searchIconAction(true)} + > + + + ) : ( + + )} ), className: classes.input 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 06f58d1f7..000000000 --- a/js/components/preview/molecule/moleculeList.js +++ /dev/null @@ -1,1291 +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 -} 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 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(() => { - // const searchedString = currentActionList.find(action => action.type === 'SEARCH_STRING_HIT_NAVIGATOR'); - if (searchString) { - return allMoleculesList.filter(molecule => molecule.code.toLowerCase().includes(searchString.toLowerCase())); - // } else if (searchedString) { - // return getJoinedMoleculeList.filter(molecule => - // molecule.protein_code.toLowerCase().includes(searchedString.searchStringHitNavigator.toLowerCase()) - // ); - } else { - return getJoinedMoleculeList; - } - }, [getJoinedMoleculeList, allMoleculesList, searchString]); - - 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 = () => {}; - - // let filterSearchString = ''; - // const getSearchedString = () => { - // filterSearchString = currentActionList.find(action => action.type === 'SEARCH_STRING_HIT_NAVIGATOR'); - // }; - // getSearchedString(); - - // useEffect(() => { - // if (filterSearchString?.searchStringHitNavigator !== '') { - // setSearchString(filterSearchString.searchStringHitNavigator); - // } - // }, [filterSearchString]); - - const actions = [ - /* do not disable filter by itself if it does not have any result */ - /* - - ,*/ - { - // 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/observationCmpList.js b/js/components/preview/molecule/observationCmpList.js index ac19e5590..8754f7f4c 100644 --- a/js/components/preview/molecule/observationCmpList.js +++ b/js/components/preview/molecule/observationCmpList.js @@ -49,7 +49,8 @@ import { removeSelectedTypesInHitNavigator, selectAllHits, autoHideTagEditorDialogsOnScroll, - selectAllVisibleObservations + selectAllVisibleObservations, + searchForObservations } from './redux/dispatchActions'; import { DEFAULT_FILTER, PREDEFINED_FILTERS } from '../../../reducers/selection/constants'; import { Edit, FilterList } from '@material-ui/icons'; @@ -64,7 +65,8 @@ import { setOpenObservationsDialog, setLHSCompoundsInitialized, setPoseIdForObservationsDialog, - setObservationDialogAction + setObservationDialogAction, + setSearchSettingsDialogOpen } from '../../../reducers/selection/actions'; import { initializeFilter } from '../../../reducers/selection/dispatchActions'; import * as listType from '../../../constants/listTypes'; @@ -88,6 +90,7 @@ import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; import ObservationCmpView from './observationCmpView'; import { ObservationsDialog } from './observationsDialog'; import { useScrollToSelectedPose } from './useScrollToSelectedPose'; +import { SearchSettingsDialog } from './searchSettingsDialog'; const useStyles = makeStyles(theme => ({ container: { @@ -277,7 +280,6 @@ export const ObservationCmpList = memo(({ hideProjects }) => { 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 => { @@ -327,6 +329,9 @@ export const ObservationCmpList = memo(({ hideProjects }) => { const proteinsHasLoaded = useSelector(state => state.nglReducers.proteinsHasLoaded); + const searchSettingsDialogOpen = useSelector(state => state.selectionReducers.searchSettingsDialogOpen); + const searchSettings = useSelector(state => state.selectionReducers.searchSettings); + const [predefinedFilter, setPredefinedFilter] = useState(filter !== undefined ? filter.predefined : DEFAULT_FILTER); const [ascending, setAscending] = useState(true); @@ -467,17 +472,12 @@ export const ObservationCmpList = memo(({ hideProjects }) => { }, [object_selection]);*/ let joinedMoleculeLists = useMemo(() => { - // const searchedString = currentActionList.find(action => action.type === 'SEARCH_STRING_HIT_NAVIGATOR'); if (searchString) { - return allMoleculesList.filter(molecule => molecule.code.toLowerCase().includes(searchString.toLowerCase())); - // } else if (searchedString) { - // return getJoinedMoleculeList.filter(molecule => - // molecule.protein_code.toLowerCase().includes(searchedString.searchStringHitNavigator.toLowerCase()) - // ); + return dispatch(searchForObservations(searchString, allMoleculesList, searchSettings)); } else { return getJoinedMoleculeList; } - }, [getJoinedMoleculeList, allMoleculesList, searchString]); + }, [searchString, dispatch, allMoleculesList, getJoinedMoleculeList, searchSettings]); const addSelectedMoleculesFromUnselectedSites = useCallback( (joinedMoleculeLists, list) => { @@ -1014,6 +1014,10 @@ export const ObservationCmpList = memo(({ hideProjects }) => { return molecules; }; + const openSearchSettingsDialog = open => { + dispatch(setSearchSettingsDialogOpen(open)); + }; + const actions = [ { // searchString={filterSearchString?.searchStringHitNavigator ?? ''} searchString={searchString ?? ''} placeholder="Search" + searchIconAction={openSearchSettingsDialog} />, { setIsOpenLPCAlert(false); }} /> + {searchSettingsDialogOpen && ( + + )} {isObservationDialogOpen && ( )} diff --git a/js/components/preview/molecule/observationCmpView/observationCmpView.js b/js/components/preview/molecule/observationCmpView/observationCmpView.js index f2b126cef..bc761b4fd 100644 --- a/js/components/preview/molecule/observationCmpView/observationCmpView.js +++ b/js/components/preview/molecule/observationCmpView/observationCmpView.js @@ -4,7 +4,7 @@ import React, { memo, useEffect, useState, useRef, useContext, useCallback, forwardRef } from 'react'; import { useDispatch, useSelector } from 'react-redux'; -import { Button, Grid, makeStyles, Tooltip, IconButton, Popper, CircularProgress } from '@material-ui/core'; +import { Button, Grid, makeStyles, Tooltip, IconButton, Popper, CircularProgress, Table, TableBody, TableRow, TableCell } from '@material-ui/core'; import { Panel } from '../../../common'; import { MyLocation, Warning, Assignment, AssignmentTurnedIn } from '@material-ui/icons'; import SVGInline from 'react-svg-inline'; @@ -63,6 +63,7 @@ import { getFontColorByBackgroundColor } from '../../../../utils/colors'; import MoleculeSelectCheckbox from '../moleculeView/moleculeSelectCheckbox'; import { isAnyObservationTurnedOnForCmp } from '../../../../reducers/selection/selectors'; import { first } from 'lodash'; +import { ToastContext } from '../../../toast'; const useStyles = makeStyles(theme => ({ container: { @@ -381,9 +382,26 @@ const useStyles = makeStyles(theme => ({ marginRight: 5, position: 'right' }, - tooltipRow: { - marginTop: 2, - marginBottom: 2 + posePropertiesTableCell: { + padding: '4px 8px' + }, + posePropertiesTable: { + pointerEvents: 'auto', + '& tr > td:nth-of-type(2)': { + border: 'none', + borderLeft: '1px dashed ' + theme.palette.primary.main + }, + '& tr:hover': { + backgroundColor: theme.palette.primary.light + } + }, + posePropertiesTableIcon: { + padding: 0, + color: theme.palette.grey[500] + }, + posePropertiesTableIconActive: { + padding: 0, + color: theme.palette.grey[700] } })); @@ -441,6 +459,7 @@ const ObservationCmpView = memo( const [hasMap, setHasMap] = useState(false); + const { toastInfo } = useContext(ToastContext); const { getNglView } = useContext(NglContext); const stage = getNglView(VIEWS.MAJOR_VIEW) && getNglView(VIEWS.MAJOR_VIEW).stage; @@ -1316,26 +1335,62 @@ const ObservationCmpView = memo( return displayName; }, [aliasOrder, getMainObservation]); - const getDisplayNameTooltip = useCallback(() => { - const mainObservation = getMainObservation(); - const tooltip = <> -

{mainObservation?.prefix_tooltip ?? '-'}

- {aliasOrder?.map((alias, index) => { - if (alias === 'compound_code') { - return

{`${alias}: ${mainObservation?.compound_code}`}

; - // return <>

{`${alias}: ${mainObservation?.compound_code}`}; - } else { - const searchedIdentifier = mainObservation.identifiers.find(identifier => identifier.type === alias); - if (searchedIdentifier) { - return

{`${alias}: ${searchedIdentifier.name}`}

; - // return <>

{`${alias}: ${searchedIdentifier.name}`}; - } - } - })} - ; + const copyToClipboard = useCallback(async (type, text) => { + await navigator.clipboard.writeText(text); + toastInfo(`${text} of '${type}' was copied to the clipboard`, { autoHideDuration: 5000 }); + }, [toastInfo]); - return tooltip; - }, [aliasOrder, getMainObservation, classes.tooltipRow]); + const [anchorElTable, setAnchorElTable] = useState(null); + const [tableIsOpen, setTableIsOpen] = useState(false); + const handleTablePopoverOpen = (event) => { + setAnchorElTable(event.currentTarget); + }; + const handleTablePopoverClose = () => { + setAnchorElTable(null); + setTableIsOpen(false); + }; + const popoverOpen = Boolean(anchorElTable) || tableIsOpen; + + const getPosePropertiesTable = useCallback(() => { + const mainObservation = getMainObservation(); + const observationCode = getMainObservation()?.code.replaceAll(`${target_on_name}-`, ''); + + return setTableIsOpen(false)} + onMouseEnter={() => setTableIsOpen(true)}> + + + copyToClipboard('smiles', data.smiles)}> + copy smiles + {data.smiles} + + + + copyToClipboard('smiles', observationCode)}> + copy observation code + {observationCode} + + + + copyToClipboard('prefix_tooltip', mainObservation?.prefix_tooltip)}> + copy prefix_tooltip + {mainObservation?.prefix_tooltip ?? ''} + + + {aliasOrder?.map((alias, index) => { + const compoundCode = mainObservation.identifiers.find(identifier => identifier.type === alias)?.name ?? ''; + return + copyToClipboard(alias, alias === 'compound_code' ? mainObservation?.compound_code : compoundCode)}> + {`copy ${alias}`} + {(alias === 'compound_code') ? + {`${mainObservation?.compound_code}`} + : {`${compoundCode}`}} + + ; + })} + +
; + }, [aliasOrder, copyToClipboard, data.smiles, getMainObservation, classes.posePropertiesTable, classes.posePropertiesTableCell, target_on_name]); return ( <> @@ -1387,22 +1442,45 @@ const ObservationCmpView = memo( {/* Title label */} - - { - e.preventDefault(); - setNameCopied(moleculeTitle); - }} - className={classes.moleculeTitleLabel} + { + e.preventDefault(); + setNameCopied(moleculeTitle); + }} + className={classes.moleculeTitleLabel} + > + + {getMainObservation()?.code.replaceAll(`${target_on_name}-`, '')} + +
+ {getDisplayName()} + setAnchorElTable(null)} + ref={anchorElTable} > - - {getMainObservation()?.code.replaceAll(`${target_on_name}-`, '')} - -
- {getDisplayName()} -
-
+ + + {getPosePropertiesTable()} + +
+ {/* "Filtered"/calculated props {svg_image}
- {moleculeTooltipOpen && ( + {/* {moleculeTooltipOpen && ( {!isCopied ? : } - )} + )} */} {warningIconVisible && ( onQuality()}> diff --git a/js/components/preview/molecule/observationsDialog.js b/js/components/preview/molecule/observationsDialog.js index 09527cf83..f23bd201a 100644 --- a/js/components/preview/molecule/observationsDialog.js +++ b/js/components/preview/molecule/observationsDialog.js @@ -24,6 +24,7 @@ import { removeObservationsFromPose, removeSelectedMolTypes, removeSurface, + searchForObservations, updateObservationsInPose, updatePose, withDisabledMoleculesNglControlButtons @@ -49,9 +50,19 @@ import MoleculeView from './moleculeView'; import { TagEditor } from '../tags/modal/tagEditor'; import { ToastContext } from '../../toast'; import { DJANGO_CONTEXT } from '../../../utils/djangoContext'; -import { updateLHSCompound, updateMoleculeInMolLists, updateMoleculeTag, updateTag } from '../../../reducers/api/actions'; +import { + updateLHSCompound, + updateMoleculeInMolLists, + updateMoleculeTag, + updateTag +} from '../../../reducers/api/actions'; import { createPoseErrorMessage } from './api/poseApi'; -import { augumentTagObjectWithId, createMoleculeTagObject, DEFAULT_TAG_COLOR, getMoleculeTagForTag } from '../tags/utils/tagUtils'; +import { + augumentTagObjectWithId, + createMoleculeTagObject, + DEFAULT_TAG_COLOR, + getMoleculeTagForTag +} from '../tags/utils/tagUtils'; import { updateExistingTag } from '../tags/api/tagsApi'; import { XCA_TAGS_CATEGORIES } from './moleculeView/moleculeView'; @@ -251,6 +262,8 @@ export const ObservationsDialog = memo( const tagList = useSelector(state => state.apiReducers.moleculeTags); const targetId = useSelector(state => state.apiReducers.target_on); + const searchSettings = useSelector(state => state.selectionReducers.searchSettings); + const poses = useSelector(state => state.apiReducers.lhs_compounds_list); const compatiblePoses = useMemo(() => { const someObservation = observationsDataList[0]; @@ -270,9 +283,9 @@ export const ObservationsDialog = memo( const tagEditorRef = useRef(); const getCalculatedTagColumnWidth = (tagText, font = null) => { - const canvas = document.createElement("canvas"); - const ctx = canvas.getContext("2d"); - ctx.font = `${(font ?? '12px')} "Roboto", "Helvetica", "Arial", sans-serif`; + const canvas = document.createElement('canvas'); + const ctx = canvas.getContext('2d'); + ctx.font = `${font ?? '12px'} "Roboto", "Helvetica", "Arial", sans-serif`; // 16 as padding buffer const calculatedWidth = ctx.measureText(tagText).width + 16; return calculatedWidth; @@ -302,19 +315,20 @@ export const ObservationsDialog = memo( setHeaderWidths(old => { const newWidths = { ...old }; newWidths[tagCategory] = calculatedWidth; - return { ...newWidths } + return { ...newWidths }; }); } }; const moleculeList = useMemo(() => { if (searchString !== null) { - return observationsDataList.filter(molecule => - molecule.code.toLowerCase().includes(searchString.toLowerCase()) - ); + // return observationsDataList.filter(molecule => + // molecule.code.toLowerCase().includes(searchString.toLowerCase()) + // ); + return dispatch(searchForObservations(searchString, observationsDataList, searchSettings)); } return observationsDataList; - }, [observationsDataList, searchString]); + }, [dispatch, observationsDataList, searchSettings, searchString]); const allSelectedMolecules = useMemo( () => observationsDataList.filter(molecule => moleculesToEditIds.includes(molecule.id)), @@ -630,7 +644,9 @@ export const ObservationsDialog = memo( const totalApproximateHeight = observationsApproximateHeight + headerFooterApproximateHeight; /*if (totalApproximateHeight > maxHeight) { height = maxHeight; - } else*/ if (totalApproximateHeight < MIN_PANEL_HEIGHT) { + } else*/ if ( + totalApproximateHeight < MIN_PANEL_HEIGHT + ) { height = MIN_PANEL_HEIGHT; } else { height = totalApproximateHeight; @@ -646,15 +662,20 @@ export const ObservationsDialog = memo( * @param {string} category * @return {array} */ - const getTagsForCategory = useCallback(category => { - const tagCategory = tagCategoriesList.find(tagCategory => tagCategory.category === category); - return tagCategory ? tagList.filter(tag => { - if (tag.category === tagCategory.id) { - // console.log('good tag', { ...tag }); - return true; - } else return false; - }) : []; - }, [tagCategoriesList, tagList]); + const getTagsForCategory = useCallback( + category => { + const tagCategory = tagCategoriesList.find(tagCategory => tagCategory.category === category); + return tagCategory + ? tagList.filter(tag => { + if (tag.category === tagCategory.id) { + // console.log('good tag', { ...tag }); + return true; + } else return false; + }) + : []; + }, + [tagCategoriesList, tagList] + ); const updateCmp = (cmp, obs) => { let newCmp = { ...cmp }; @@ -849,7 +870,10 @@ export const ObservationsDialog = memo( } // then tag await tagObservations(tag, mainObservationTag); - toastInfo(`Tag for observations was changed from "${mainObservationTag.upload_name}" to "${tag.upload_name}". They could disappear based on your tag selection`, { autoHideDuration: 5000 }); + toastInfo( + `Tag for observations was changed from "${mainObservationTag.upload_name}" to "${tag.upload_name}". They could disappear based on your tag selection`, + { autoHideDuration: 5000 } + ); }; /** @@ -858,10 +882,17 @@ export const ObservationsDialog = memo( * @param {string} category category of tag * @returns {boolean} */ - const disableXCATagChange = useCallback(category => { - // #1522 CanonSite tags should not be allowed to change if there are selected only some observations - return category === 'CanonSites' && allSelectedMolecules.length > 0 && (allSelectedMolecules.length !== moleculeList.length); - }, [allSelectedMolecules, moleculeList]); + const disableXCATagChange = useCallback( + category => { + // #1522 CanonSite tags should not be allowed to change if there are selected only some observations + return ( + category === 'CanonSites' && + allSelectedMolecules.length > 0 && + allSelectedMolecules.length !== moleculeList.length + ); + }, + [allSelectedMolecules, moleculeList] + ); return ( @@ -1006,27 +1037,40 @@ export const ObservationsDialog = memo( {expandView && ( - - {XCA_TAGS_CATEGORIES.map( - (tagCategory, index) => ( - - {PLURAL_TO_SINGULAR[tagCategory]} - - ) - )} - + + {XCA_TAGS_CATEGORIES.map((tagCategory, index) => ( + + {PLURAL_TO_SINGULAR[tagCategory]} + + ))} + CentroidRes - + LongCode - + Path @@ -1158,19 +1202,34 @@ export const ObservationsDialog = memo( Change XCA tags - {XCA_TAG_CATEGORIES.map(category => - + {XCA_TAG_CATEGORIES.map(category => ( + Change {PLURAL_TO_SINGULAR[category]} - {!disableXCATagChange(category) && - + {!disableXCATagChange(category) && ( + {getTagsForCategory(category)?.map(tag => ( - handleXCAtagChange(tag)}> + handleXCAtagChange(tag)} + > {tag.upload_name} ))} - } + + )} - )} + ))} diff --git a/js/components/preview/molecule/redux/dispatchActions.js b/js/components/preview/molecule/redux/dispatchActions.js index dea61ad40..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)); @@ -1215,3 +1212,46 @@ export const updatePose = newPose => async (dispatch, getState) => { export const createPose = newPose => async (dispatch, getState) => { return createPoseApi(newPose); }; + +const observationSearchFunctions = { + 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, 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, 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, exact = false) => ( + dispatch, + getState +) => { + if (!observations || observations.length === 0) return []; + if (!searchTerm) return observations; + + let result = []; + + const searchBy = searchSettings.searchBy; + const searchByKeys = Object.keys(searchBy).filter(key => searchBy[key]); + + result = observations.filter(obs => { + return searchByKeys.reduce((acc, key) => acc || observationSearchFunctions[key](obs, searchTerm), false); + }); + + return result; +}; diff --git a/js/components/preview/molecule/searchSettingsDialog.js b/js/components/preview/molecule/searchSettingsDialog.js new file mode 100644 index 000000000..81034195d --- /dev/null +++ b/js/components/preview/molecule/searchSettingsDialog.js @@ -0,0 +1,115 @@ +import React, { memo, useState } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; +import { Grid, makeStyles, Checkbox, Typography, FormControlLabel } from '@material-ui/core'; +import { Button, Modal } from '../../common'; +import { setSearchSettings } from '../../../reducers/selection/actions'; + +const useStyles = makeStyles(theme => ({ + body: { + width: '100%', + marginTop: theme.spacing(2), + marginBottom: theme.spacing(1) + }, + margin: { + marginTop: theme.spacing(1) + }, + checkbox: { + margin: theme.spacing(0) + } +})); + +export const SearchSettingsDialog = memo(({ openDialog, setOpenDialog }) => { + const dispatch = useDispatch(); + const classes = useStyles(); + + const searchSettings = useSelector(state => state.selectionReducers.searchSettings); + + const [shortcode, setShortcode] = useState(searchSettings.searchBy.shortcode); + const [aliases, setAliases] = useState(searchSettings.searchBy.aliases); + const [compoundId, setCompoundId] = useState(searchSettings.searchBy.compoundId); + + const handleCloseModal = () => { + setOpenDialog(false); + }; + + const handleSaveButton = () => { + dispatch(setSearchSettings({ searchBy: { shortcode, aliases, compoundId } })); + setOpenDialog(false); + }; + + return ( + + <> + Search settings + + Search by: + + + + { + setShortcode(prev => !prev); + }} + /> + } + label="Observation shortcode" + labelPlacement="end" + className={classes.checkbox} + /> + + + { + setAliases(prev => !prev); + }} + /> + } + label="Compound aliases" + labelPlacement="end" + className={classes.checkbox} + /> + + + { + setCompoundId(prev => !prev); + }} + /> + } + label="Compound ID" + labelPlacement="end" + className={classes.checkbox} + /> + + + + + + + + + + + + + ); +}); diff --git a/js/components/preview/redux/dispatchActions.js b/js/components/preview/redux/dispatchActions.js index 980ce9ca3..7f18bfa71 100644 --- a/js/components/preview/redux/dispatchActions.js +++ b/js/components/preview/redux/dispatchActions.js @@ -73,7 +73,7 @@ export const shouldLoadProtein = ({ dispatch(setProteinIsLoading(true)); Promise.all( nglViewList.map(nglView => - dispatch(loadProtein(nglView)).finally(() => { + dispatch(loadProtein(nglView))?.finally(() => { dispatch(setOrientation(nglView.id, nglView.stage.viewerControls.getOrientation())); }) ) diff --git a/js/components/snapshot/redux/utilitySnapshotShapes.js b/js/components/snapshot/redux/utilitySnapshotShapes.js index f12c5925f..c4c3920ea 100644 --- a/js/components/snapshot/redux/utilitySnapshotShapes.js +++ b/js/components/snapshot/redux/utilitySnapshotShapes.js @@ -14,7 +14,8 @@ export const SNAPSHOT_VALUES_TO_BE_DELETED = { rhsDataIsLoading: true, rhsDataIsLoaded: false, proteinIsLoading: false, - proteinIsLoaded: false + proteinIsLoaded: false, + compound_identifiers: [] }, nglReducers: { objectsInView: {}, @@ -69,7 +70,8 @@ export const SNAPSHOT_VALUES_NOT_TO_BE_DELETED_SWITCHING_TARGETS = { moleculeTags: [], tagList: [], categoryList: [], - lhs_compounds_list: [] + lhs_compounds_list: [], + compound_identifiers: [] }, datasetsReducers: { datasets: [], 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/actions.js b/js/reducers/selection/actions.js index cb8f6afdd..3a5883e2b 100644 --- a/js/reducers/selection/actions.js +++ b/js/reducers/selection/actions.js @@ -4,14 +4,14 @@ import { constants } from './constants'; -export const setToBuyList = function (to_buy_list) { +export const setToBuyList = function(to_buy_list) { return { type: constants.SET_TO_BUY_LIST, to_buy_list: to_buy_list }; }; -export const appendToBuyList = function (item, index, skipTracking = false) { +export const appendToBuyList = function(item, index, skipTracking = false) { return { type: constants.APPEND_TO_BUY_LIST, item: item, @@ -20,7 +20,7 @@ export const appendToBuyList = function (item, index, skipTracking = false) { }; }; -export const removeFromToBuyList = function (item, index, skipTracking = false) { +export const removeFromToBuyList = function(item, index, skipTracking = false) { return { type: constants.REMOVE_FROM_TO_BUY_LIST, item: item, @@ -29,21 +29,21 @@ export const removeFromToBuyList = function (item, index, skipTracking = false) }; }; -export const appendToBuyListAll = function (items) { +export const appendToBuyListAll = function(items) { return { type: constants.APPEND_TO_BUY_LIST_ALL, items: items }; }; -export const removeFromToBuyListAll = function (items) { +export const removeFromToBuyListAll = function(items) { return { type: constants.REMOVE_FROM_BUY_LIST_ALL, items: items }; }; -export const setVectorList = function (vectList) { +export const setVectorList = function(vectList) { return { type: constants.SET_VECTOR_LIST, vector_list: vectList @@ -57,7 +57,7 @@ export const setCurrentVector = vectorSmile => { }; }; -export const setFragmentDisplayList = function (fragmentDisplayList, skipTracking = false) { +export const setFragmentDisplayList = function(fragmentDisplayList, skipTracking = false) { return { type: constants.SET_FRAGMENT_DISPLAY_LIST, fragmentDisplayList: fragmentDisplayList, @@ -65,7 +65,7 @@ export const setFragmentDisplayList = function (fragmentDisplayList, skipTrackin }; }; -export const appendFragmentDisplayList = function (item, skipTracking = false) { +export const appendFragmentDisplayList = function(item, skipTracking = false) { return { type: constants.APPEND_FRAGMENT_DISPLAY_LIST, item: item, @@ -73,7 +73,7 @@ export const appendFragmentDisplayList = function (item, skipTracking = false) { }; }; -export const removeFromFragmentDisplayList = function (item, skipTracking = false) { +export const removeFromFragmentDisplayList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_FRAGMENT_DISPLAY_LIST, item: item, @@ -81,7 +81,7 @@ export const removeFromFragmentDisplayList = function (item, skipTracking = fals }; }; -export const setProteinList = function (proteinList, skipTracking = false) { +export const setProteinList = function(proteinList, skipTracking = false) { return { type: constants.SET_PROTEIN_LIST, proteinList: proteinList, @@ -89,7 +89,7 @@ export const setProteinList = function (proteinList, skipTracking = false) { }; }; -export const appendProteinList = function (item, skipTracking = false) { +export const appendProteinList = function(item, skipTracking = false) { return { type: constants.APPEND_PROTEIN_LIST, item: item, @@ -97,14 +97,14 @@ export const appendProteinList = function (item, skipTracking = false) { }; }; -export const removeFromProteinList = function (item, skipTracking = false) { +export const removeFromProteinList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_PROTEIN_LIST, item: item, skipTracking: skipTracking }; }; -export const setComplexList = function (complexList, skipTracking = false) { +export const setComplexList = function(complexList, skipTracking = false) { return { type: constants.SET_COMPLEX_LIST, complexList: complexList, @@ -112,7 +112,7 @@ export const setComplexList = function (complexList, skipTracking = false) { }; }; -export const appendComplexList = function (item, skipTracking = false) { +export const appendComplexList = function(item, skipTracking = false) { return { type: constants.APPEND_COMPLEX_LIST, item: item, @@ -120,7 +120,7 @@ export const appendComplexList = function (item, skipTracking = false) { }; }; -export const removeFromComplexList = function (item, skipTracking = false) { +export const removeFromComplexList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_COMPLEX_LIST, item: item, @@ -128,7 +128,7 @@ export const removeFromComplexList = function (item, skipTracking = false) { }; }; -export const setSurfaceList = function (surfaceList, skipTracking = false) { +export const setSurfaceList = function(surfaceList, skipTracking = false) { return { type: constants.SET_SURFACE_LIST, surfaceList: surfaceList, @@ -136,7 +136,7 @@ export const setSurfaceList = function (surfaceList, skipTracking = false) { }; }; -export const appendSurfaceList = function (item, skipTracking = false) { +export const appendSurfaceList = function(item, skipTracking = false) { return { type: constants.APPEND_SURFACE_LIST, item: item, @@ -144,7 +144,7 @@ export const appendSurfaceList = function (item, skipTracking = false) { }; }; -export const removeFromSurfaceList = function (item, skipTracking = false) { +export const removeFromSurfaceList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_SURFACE_LIST, item: item, @@ -168,14 +168,14 @@ export const removeFromDensityListType = (item, skipTracking = false) => { }; }; -export const setDensityList = function (densityList) { +export const setDensityList = function(densityList) { return { type: constants.SET_DENSITY_LIST, densityList: densityList }; }; -export const appendDensityList = function (item, skipTracking = false) { +export const appendDensityList = function(item, skipTracking = false) { return { type: constants.APPEND_DENSITY_LIST, item: item, @@ -183,7 +183,7 @@ export const appendDensityList = function (item, skipTracking = false) { }; }; -export const removeFromDensityList = function (item, skipTracking = false) { +export const removeFromDensityList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_DENSITY_LIST, item: item, @@ -191,14 +191,14 @@ export const removeFromDensityList = function (item, skipTracking = false) { }; }; -export const setDensityListCustom = function (densityListCustom) { +export const setDensityListCustom = function(densityListCustom) { return { type: constants.SET_DENSITY_LIST_CUSTOM, densityListCustom: densityListCustom }; }; -export const appendDensityListCustom = function (item, skipTracking = false) { +export const appendDensityListCustom = function(item, skipTracking = false) { return { type: constants.APPEND_DENSITY_LIST_CUSTOM, item: item, @@ -206,7 +206,7 @@ export const appendDensityListCustom = function (item, skipTracking = false) { }; }; -export const removeFromDensityListCustom = function (item, skipTracking = false) { +export const removeFromDensityListCustom = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_DENSITY_LIST_CUSTOM, item: item, @@ -214,7 +214,7 @@ export const removeFromDensityListCustom = function (item, skipTracking = false) }; }; -export const setQualityList = function (qualityList, skipTracking = false) { +export const setQualityList = function(qualityList, skipTracking = false) { return { type: constants.SET_QUALITY_LIST, qualityList: qualityList, @@ -222,21 +222,21 @@ export const setQualityList = function (qualityList, skipTracking = false) { }; }; -export const appendInformationList = function (item) { +export const appendInformationList = function(item) { return { type: constants.APPEND_INFORMATION_LIST, item: item }; }; -export const removeFromInformationList = function (item) { +export const removeFromInformationList = function(item) { return { type: constants.REMOVE_FROM_INFORMATION_LIST, item: item }; }; -export const appendQualityList = function (item, skipTracking = false) { +export const appendQualityList = function(item, skipTracking = false) { return { type: constants.APPEND_QUALITY_LIST, item: item, @@ -244,7 +244,7 @@ export const appendQualityList = function (item, skipTracking = false) { }; }; -export const removeFromQualityList = function (item, skipTracking = false) { +export const removeFromQualityList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_QUALITY_LIST, item: item, @@ -252,7 +252,7 @@ export const removeFromQualityList = function (item, skipTracking = false) { }; }; -export const setVectorOnList = function (vectorOnList, skipTracking = false) { +export const setVectorOnList = function(vectorOnList, skipTracking = false) { return { type: constants.SET_VECTOR_ON_LIST, vectorOnList: vectorOnList, @@ -260,7 +260,7 @@ export const setVectorOnList = function (vectorOnList, skipTracking = false) { }; }; -export const appendVectorOnList = function (item, skipTracking = false) { +export const appendVectorOnList = function(item, skipTracking = false) { return { type: constants.APPEND_VECTOR_ON_LIST, item: item, @@ -268,7 +268,7 @@ export const appendVectorOnList = function (item, skipTracking = false) { }; }; -export const removeFromVectorOnList = function (item, skipTracking = false) { +export const removeFromVectorOnList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_VECTOR_ON_LIST, item: item, @@ -283,7 +283,7 @@ export const reloadSelectionReducer = savedSelectionReducers => { }; }; -export const resetSelectionState = function () { +export const resetSelectionState = function() { return { type: constants.RESET_SELECTION_STATE }; @@ -374,7 +374,7 @@ export const setArrowUpDown = (item, newItem, arrowType, data) => ({ } }); -export const setSelectedTagList = function (selectedTagList, skipTracking = false) { +export const setSelectedTagList = function(selectedTagList, skipTracking = false) { return { type: constants.SET_SELECTED_TAG_LIST, selectedTagList: selectedTagList, @@ -382,7 +382,7 @@ export const setSelectedTagList = function (selectedTagList, skipTracking = fals }; }; -export const appendSelectedTagList = function (item, skipTracking = false) { +export const appendSelectedTagList = function(item, skipTracking = false) { return { type: constants.APPEND_SELECTED_TAG_LIST, item: item, @@ -390,7 +390,7 @@ export const appendSelectedTagList = function (item, skipTracking = false) { }; }; -export const removeFromSelectedTagList = function (item, skipTracking = false) { +export const removeFromSelectedTagList = function(item, skipTracking = false) { return { type: constants.REMOVE_FROM_SELECTED_TAG_LIST, item: item, @@ -652,3 +652,17 @@ export const updateInToBeDisplayedList = item => { item: item }; }; + +export const setSearchSettingsDialogOpen = isOpen => { + return { + type: constants.SET_SEARCH_SETTINGS_DIALOG_OPEN, + isOpen: isOpen + }; +}; + +export const setSearchSettings = settings => { + return { + type: constants.SET_SEARCH_SETTINGS, + settings: settings + }; +}; diff --git a/js/reducers/selection/constants.js b/js/reducers/selection/constants.js index b66b797ce..922085e9a 100644 --- a/js/reducers/selection/constants.js +++ b/js/reducers/selection/constants.js @@ -110,7 +110,10 @@ export const constants = { 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' + UPDATE_IN_TO_BE_DISPLAYED_LIST: prefix + 'UPDATE_IN_TO_BE_DISPLAYED_LIST', + + SET_SEARCH_SETTINGS_DIALOG_OPEN: prefix + 'SET_SEARCH_SETTINGS_DIALOG_OPEN', + SET_SEARCH_SETTINGS: prefix + 'SET_SEARCH_SETTINGS' }; export const PREDEFINED_FILTERS = { diff --git a/js/reducers/selection/selectionReducers.js b/js/reducers/selection/selectionReducers.js index 982995ec6..2e2a903a8 100644 --- a/js/reducers/selection/selectionReducers.js +++ b/js/reducers/selection/selectionReducers.js @@ -68,11 +68,25 @@ export const INITIAL_STATE = { // display: true // } - toBeDisplayedList: [] + toBeDisplayedList: [], + searchSettingsDialogOpen: false, + searchSettings: { + searchBy: { + shortcode: true, + aliases: true, + compoundId: true + } + } }; export function selectionReducers(state = INITIAL_STATE, action = {}) { switch (action.type) { + case constants.SET_SEARCH_SETTINGS_DIALOG_OPEN: + return { ...state, searchSettingsDialogOpen: action.isOpen }; + + case constants.SET_SEARCH_SETTINGS: + return { ...state, searchSettings: JSON.parse(JSON.stringify(action.settings)) }; + case constants.SET_TO_BE_DISPLAYED_LIST: return { ...state, toBeDisplayedList: action.toBeDisplayedList };