From 8eb7e93917c247772604a7e1a06a52ee0eb93877 Mon Sep 17 00:00:00 2001 From: matej <60509086+matej-vavrek@users.noreply.github.com> Date: Tue, 10 Dec 2024 11:52:37 +0100 Subject: [PATCH 1/4] #1540 preferred aliases (#466) * removed SGC column from target list * #1540 added preferred alias functionality * #1540 added edit column to target list and use target settings modal * #1540 updated "Open" label and read only version of display name --------- Co-authored-by: boriskovar-m2ms --- js/components/header/index.js | 36 ++- js/components/landing/Landing.js | 10 +- .../observationCmpView/observationCmpView.js | 63 +++- js/components/preview/tags/api/tagsApi.js | 10 +- .../preview/tags/redux/dispatchActions.js | 9 +- .../snapshot/withSnapshotManagement.js | 5 +- js/components/target/targetList.js | 31 +- js/components/target/targetSettingsModal.js | 273 ++++++++++++++++++ js/reducers/api/actions.js | 25 +- js/reducers/api/apiReducers.js | 26 +- js/reducers/api/constants.js | 5 +- js/reducers/api/selectors.js | 6 + 12 files changed, 461 insertions(+), 38 deletions(-) create mode 100644 js/components/target/targetSettingsModal.js diff --git a/js/components/header/index.js b/js/components/header/index.js index c7a3b316a..3c737fade 100644 --- a/js/components/header/index.js +++ b/js/components/header/index.js @@ -72,7 +72,8 @@ import { AddProjectDetail } from '../projects/addProjectDetail'; import { ServicesStatusWrapper } from '../services'; import { COMPANIES, get_logo } from '../funders/constants'; import { setEditTargetDialogOpen } from '../target/redux/actions'; -import { Upload } from '@mui/icons-material'; +import { Settings, Upload } from '@mui/icons-material'; +import { TargetSettingsModal } from '../target/targetSettingsModal'; import { SnapshotType } from '../projects/redux/constants'; import { NglContext } from '../nglView/nglProvider'; import { VIEWS } from '../../constants/constants'; @@ -134,7 +135,7 @@ export default memo( const [openMenu, setOpenMenu] = useState(false); const [openFunders, setOpenFunders] = useState(false); - const [openTrackingModal, setOpenTrackingModal] = useState(false); + const [openTargetSettings, setOpenTargetSettings] = useState(false); const [versions, setVersions] = useState({}); const layoutEnabled = useSelector(state => state.layoutReducers.layoutEnabled); @@ -310,14 +311,13 @@ export default memo( > Menu - + + + + ); +}); diff --git a/js/reducers/api/actions.js b/js/reducers/api/actions.js index 08ebe6857..6b4c5b89f 100644 --- a/js/reducers/api/actions.js +++ b/js/reducers/api/actions.js @@ -43,6 +43,20 @@ export const setTargetOn = function (target_id, skipTracking = false) { }; }; +export const setTargetOnName = function (target_on_name) { + return { + type: constants.SET_TARGET_ON_NAME, + target_on_name: target_on_name + }; +}; + +export const setTargetOnAliases = function (target_on_aliases) { + return { + type: constants.SET_TARGET_ON_ALIASES, + target_on_aliases: target_on_aliases + }; +}; + export const setPanddaSiteList = function (pandda_site_list) { return { type: constants.SET_PANNDA_SITE_LIST, @@ -314,6 +328,7 @@ export const reloadApiState = function (apiReducers) { return { type: constants.RELOAD_API_STATE, + target_on_aliases: apiReducers.target_on_aliases, target_on_name: apiReducers.target_on_name, target_on: apiReducers.target_on, target_id: apiReducers.target_id, @@ -332,7 +347,8 @@ export const reloadApiState = function (apiReducers) { pandda_event_on: apiReducers.pandda_event_on, pandda_site_on: apiReducers.pandda_site_on, pandda_event_list: apiReducers.pandda_event_list, - pandda_site_list: apiReducers.pandda_site_list + pandda_site_list: apiReducers.pandda_site_list, + compound_identifiers: apiReducers.compound_identifiers }; }; @@ -436,3 +452,10 @@ export const setProteinIsLoaded = proteinIsLoaded => { proteinIsLoaded: proteinIsLoaded }; }; + +export const setCompoundIdentifiers = compoundIdentifiers => { + return { + type: constants.SET_COMPOUND_IDENTIFIERS, + compound_identifiers: compoundIdentifiers + }; +}; diff --git a/js/reducers/api/apiReducers.js b/js/reducers/api/apiReducers.js index 92f2c2481..800295b1f 100644 --- a/js/reducers/api/apiReducers.js +++ b/js/reducers/api/apiReducers.js @@ -24,6 +24,7 @@ export const INITIAL_STATE = { mol_group_on: undefined, target_on: undefined, target_on_name: undefined, + target_on_aliases: [], group_id: undefined, isFetching: false, app_on: 'PREVIEW', @@ -58,7 +59,8 @@ export const INITIAL_STATE = { rhsDataIsLoading: false, rhsDataIsLoaded: false, proteinIsLoading: false, - proteinIsLoaded: false + proteinIsLoaded: false, + compound_identifiers: [] }; export const RESET_TARGET_STATE = { @@ -74,6 +76,7 @@ export const RESET_TARGET_STATE = { mol_group_on: undefined, target_on: undefined, target_on_name: undefined, + target_on_aliases: [], group_id: undefined, isFetching: false, app_on: 'PREVIEW', @@ -100,7 +103,8 @@ export const RESET_TARGET_STATE = { target_data_loading_in_progress: false, all_data_loaded: false, snapshotLoadingInProgress: false, - lhs_compounds_list: [] + lhs_compounds_list: [], + compound_identifiers: [] }; export default function apiReducers(state = INITIAL_STATE, action = {}) { @@ -123,6 +127,9 @@ export default function apiReducers(state = INITIAL_STATE, action = {}) { case constants.SET_PROTEIN_IS_LOADED: return { ...state, proteinIsLoaded: action.proteinIsLoaded }; + case constants.SET_COMPOUND_IDENTIFIERS: + return { ...state, compound_identifiers: action.compound_identifiers }; + case constants.SET_OPEN_DISCOURSE_ERROR_MODAL: return Object.assign({}, state, { open_discourse_error_modal: action.payload }); @@ -142,17 +149,26 @@ export default function apiReducers(state = INITIAL_STATE, action = {}) { case constants.SET_TARGET_ON: { let target_on_name = undefined; + let target_on_aliases = []; for (let ind in state.target_id_list) { if (state.target_id_list[ind].id === action.target_on) { - target_on_name = state.target_id_list[ind].title; + target_on_name = state.target_id_list[ind].display_name; + target_on_aliases = state.target_id_list[ind].alias_order; } } return Object.assign({}, state, { target_on_name: target_on_name, + target_on_aliases: target_on_aliases, target_on: action.target_on }); } + case constants.SET_TARGET_ON_NAME: + return { ...state, target_on_name: action.target_on_name }; + + case constants.SET_TARGET_ON_ALIASES: + return { ...state, target_on_aliases: action.target_on_aliases }; + case constants.SET_MOL_GROUP_LIST: return Object.assign({}, state, { mol_group_list: action.mol_group_list @@ -378,6 +394,7 @@ export default function apiReducers(state = INITIAL_STATE, action = {}) { case constants.RELOAD_API_STATE: return Object.assign({}, state, { project: action.project, + target_on_aliases: action.target_on_aliases, target_on_name: action.target_on_name, target_on: action.target_on, target_id: action.target_id, @@ -396,7 +413,8 @@ export default function apiReducers(state = INITIAL_STATE, action = {}) { pandda_event_list: action.pandda_event_list, pandda_site_list: action.pandda_site_list, latestSession: action.latestSession, - direct_access: action.direct_access + direct_access: action.direct_access, + compound_identifiers: action.compound_identifiers // direct_access_processed: action.direct_access_processed }); diff --git a/js/reducers/api/constants.js b/js/reducers/api/constants.js index c8ae1a86f..22cda23fe 100644 --- a/js/reducers/api/constants.js +++ b/js/reducers/api/constants.js @@ -4,6 +4,8 @@ export const constants = { SET_LEGACY_TARGET_ID_LIST: prefix + 'SET_LEGACY_TARGET_ID_LIST', SET_DUCK_YANK_DATA: prefix + 'SET_DUCK_YANK_DATA', SET_TARGET_ON: prefix + 'REDUCERS_API_SET_TARGET_ON', + SET_TARGET_ON_NAME: prefix + 'SET_TARGET_ON_NAME', + SET_TARGET_ON_ALIASES: prefix + 'SET_TARGET_ON_ALIASES', // Pandda stuff SET_PANNDA_SITE_LIST: prefix + 'SET_PANNDA_SITE_LIST', @@ -62,5 +64,6 @@ export const constants = { SET_RHS_DATA_IS_LOADING: prefix + 'SET_RHS_DATA_IS_LOADING', SET_RHS_DATA_IS_LOADED: prefix + 'SET_RHS_DATA_IS_LOADED', SET_PROTEIN_IS_LOADING: prefix + 'SET_PROTEIN_IS_LOADING', - SET_PROTEIN_IS_LOADED: prefix + 'SET_PROTEIN_IS_LOADED' + SET_PROTEIN_IS_LOADED: prefix + 'SET_PROTEIN_IS_LOADED', + SET_COMPOUND_IDENTIFIERS: prefix + 'SET_COMPOUND_IDENTIFIERS' }; diff --git a/js/reducers/api/selectors.js b/js/reducers/api/selectors.js index e5f0260fe..bd7e7b5b6 100644 --- a/js/reducers/api/selectors.js +++ b/js/reducers/api/selectors.js @@ -16,3 +16,9 @@ export const getCombinedTargetList = state => { return result; }; +export const getCurrentTarget = state => { + const target_on = state.apiReducers.target_on; + const targets = state.apiReducers.target_id_list; + + return targets?.find(target => target.id === target_on); +}; From 771d3b06176fe569b4f2c4ff7de3bab6e6513859 Mon Sep 17 00:00:00 2001 From: boriskovar-m2ms Date: Wed, 11 Dec 2024 15:43:57 +0100 Subject: [PATCH 2/4] #1604 (#467) * removed SGC column from target list * #1540 added preferred alias functionality * #1540 added edit column to target list and use target settings modal * #1540 updated "Open" label and read only version of display name * Squashed commit of the following: commit c5bba583d5cea4c6a24e40511ee7828e20256a17 Author: Boris Kovar Date: Tue Dec 10 08:13:00 2024 +0100 #1603 - fixes * Squashed commit of the following: commit 9d9bf8c7b810540f71e9ca8c9fae5de146fd1a5f Author: Boris Kovar Date: Wed Dec 11 15:20:54 2024 +0100 fixed frontend part of the ticket. Now properly resolves target based on the target name and target access string --------- Co-authored-by: matej --- docker-compose.dev.vector.yml | 2 +- js/components/target/redux/dispatchActions.js | 19 +++++++++++-------- js/components/target/withUpdatingTarget.js | 9 +++++---- 3 files changed, 17 insertions(+), 13 deletions(-) diff --git a/docker-compose.dev.vector.yml b/docker-compose.dev.vector.yml index f776976b7..2c20f68d2 100644 --- a/docker-compose.dev.vector.yml +++ b/docker-compose.dev.vector.yml @@ -104,7 +104,7 @@ services: LEGACY_URL: 'https://fragalysis.diamond.ac.uk' DEPLOYMENT_MODE: 'development' DISABLE_RESTRICT_PROPOSALS_TO_MEMBERSHIP: 'True' - PUBLIC_TAS: 'lb18145-1' + PUBLIC_TAS: 'lb18145-1,lb32633-1' # INFECTIONS: 'structure-download' depends_on: database: diff --git a/js/components/target/redux/dispatchActions.js b/js/components/target/redux/dispatchActions.js index ae3fc101f..33c814845 100644 --- a/js/components/target/redux/dispatchActions.js +++ b/js/components/target/redux/dispatchActions.js @@ -58,7 +58,10 @@ export const loadProjectsList = () => async (dispatch, getState) => { return resp.data.results; }; -export const updateTarget = ({ target, setIsLoading, targetIdList, projectId }) => (dispatch, getState) => { +export const updateTarget = ({ targetName, projectName, setIsLoading, targetIdList, projectId }) => ( + dispatch, + getState +) => { const state = getState(); const isActionRestoring = false; //state.trackingReducers.isActionRestoring; const currentSessionProject = state.projectReducers.currentProject; @@ -68,11 +71,13 @@ export const updateTarget = ({ target, setIsLoading, targetIdList, projectId }) // Get from the REST API let targetUnrecognisedFlag = true; // if (target !== undefined) { - if (target) { + let target = null; + if (targetName) { if (targetIdList && targetIdList.length > 0) { - targetIdList.forEach(targetId => { - if (target === targetId.title) { + targetIdList.forEach(t => { + if (targetName === t.title && t.project.target_access_string === projectName) { targetUnrecognisedFlag = false; + target = t; } }); } @@ -86,7 +91,7 @@ export const updateTarget = ({ target, setIsLoading, targetIdList, projectId }) setIsLoading(true); let url = undefined; if (target) { - url = `${base_url}/api/targets/?title=${target}`; + url = `${base_url}/api/targets/?id=${target.id}`; return api({ url }) .then(response => { return dispatch(setTargetOn(response.data['results'][0].id)); @@ -163,9 +168,7 @@ export const getTargetProjectCombinations = (targets, projects) => { updatedTarget.project = project; result.push({ updatedTarget }); } else { - console.log( - `User don't have access to project ${projectId} which is associated with target ${target.title}` - ); + console.log(`User don't have access to project ${projectId} which is associated with target ${target.title}`); } } else { result.push({ updatedTarget: { ...target, project: { target_access_string: 'Legacy' } } }); diff --git a/js/components/target/withUpdatingTarget.js b/js/components/target/withUpdatingTarget.js index 68fa34ad1..a9b66b743 100644 --- a/js/components/target/withUpdatingTarget.js +++ b/js/components/target/withUpdatingTarget.js @@ -3,7 +3,7 @@ import { connect, useSelector } from 'react-redux'; import HandleUnrecognisedTarget from './handleUnrecognisedTarget'; import { updateTarget, setTargetUUIDs, resetTargetAndSelection } from './redux/dispatchActions'; import { useRouteMatch } from 'react-router-dom'; -import { extractTargetFromURLParam } from '../preview/utils'; +import { extractProjectFromURLParam, extractTargetFromURLParam } from '../preview/utils'; import { LoadingContext } from '../loading'; export const withUpdatingTarget = WrappedContainer => { @@ -12,7 +12,8 @@ export const withUpdatingTarget = WrappedContainer => { let match = useRouteMatch(); // const target = match && match.params && match.params.target; - let target = match && match.params && extractTargetFromURLParam(match.params[0]); + let targetName = match && match.params && extractTargetFromURLParam(match.params[0]); + const projectName = match && match.params && extractProjectFromURLParam(match.params[0]); const uuid = match && match.params && match.params.uuid; const snapshotUuid = match && match.params && match.params.snapshotUuid; const snapshotId = match && match.params && match.params.snapshotId; @@ -30,12 +31,12 @@ export const withUpdatingTarget = WrappedContainer => { }, [setTargetUUIDs, snapshotUuid, uuid]); useEffect(() => { - updateTarget({ target, setIsLoading, targetIdList, projectId }).catch(error => { + updateTarget({ targetName, projectName, setIsLoading, targetIdList, projectId }).catch(error => { setState(() => { throw error; }); }); - }, [setIsLoading, target, updateTarget, targetIdList, projectId, snapshotId /*, isActionRestoring*/]); + }, [setIsLoading, targetName, updateTarget, targetIdList, projectId, snapshotId, projectName]); if (isLoading === true) { return null; From df73b818f9f5374db3380d9703bfd82d029aa26f Mon Sep 17 00:00:00 2001 From: boriskovar-m2ms Date: Mon, 16 Dec 2024 10:56:38 +0100 Subject: [PATCH 3/4] #1563 - LHS search settings (#469) * removed SGC column from target list * #1540 added preferred alias functionality * #1540 added edit column to target list and use target settings modal * #1540 updated "Open" label and read only version of display name * Squashed commit of the following: commit c5bba583d5cea4c6a24e40511ee7828e20256a17 Author: Boris Kovar Date: Tue Dec 10 08:13:00 2024 +0100 #1603 - fixes * Squashed commit of the following: commit 9d9bf8c7b810540f71e9ca8c9fae5de146fd1a5f Author: Boris Kovar Date: Wed Dec 11 15:20:54 2024 +0100 fixed frontend part of the ticket. Now properly resolves target based on the target name and target access string * - fixed issue when the template protein is missing * Squashed commit of the following: commit 2fd529e84c3969768ff7a595046a4a4fc134a4ad Author: Boris Kovar Date: Mon Dec 16 10:44:15 2024 +0100 - changes labels in searchSettingsDialog commit 12499a9630db29a29e26235c8dc9f75dce559d54 Author: Boris Kovar Date: Mon Dec 16 09:47:05 2024 +0100 -small fix commit cbf95f1ee181dbfe3a6675fb684705e3e84840f8 Merge: 5bd77562 900d8518 Author: Boris Kovar Date: Thu Dec 12 13:34:14 2024 +0100 Merge branch '#1563-LHS-search' of https://github.com/m2ms/fragalysis-frontend into #1563-LHS-search commit 5bd7756253160fe06b61cc6c2062cf982a5397ce Author: Boris Kovar Date: Thu Dec 12 13:30:00 2024 +0100 - search aliases commit 3e7540f0e6c7478e14e83fb3da4c4b9e41bb35bb Author: Boris Kovar Date: Tue Dec 10 12:12:03 2024 +0100 - checkpoint commit 6631373d07e2efc2ba48e5f237fc3a92c708b68d Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done commit 900d851868384a6efe36a7e48d2877bf3bcacdbd Author: Boris Kovar Date: Thu Dec 12 13:30:00 2024 +0100 - search aliases commit a72f427e90e3ef3c88f9b1d3e32828bfe3c11ca5 Author: Boris Kovar Date: Tue Dec 10 12:12:03 2024 +0100 - checkpoint commit 4b7d69940fbb19214b7c258c7be59552e580e14e Merge: 4579a9de 32c837dc Author: Boris Kovar Date: Tue Dec 10 10:26:34 2024 +0100 Merge branch '#1563-LHS-search' of https://github.com/m2ms/fragalysis-frontend into #1563-LHS-search commit 4579a9de85c4491442e38933b57822510ae5ab1c Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done commit 32c837dcf295b71836e028ea7750fa9bc8ead28c Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done --------- Co-authored-by: matej --- .../common/Components/SearchField/index.js | 27 ++- .../preview/molecule/moleculeList.js | 48 +----- .../preview/molecule/observationCmpList.js | 28 ++-- .../preview/molecule/observationsDialog.js | 157 ++++++++++++------ .../preview/molecule/redux/dispatchActions.js | 28 ++++ .../preview/molecule/searchSettingsDialog.js | 115 +++++++++++++ .../preview/redux/dispatchActions.js | 2 +- .../snapshot/redux/utilitySnapshotShapes.js | 6 +- js/reducers/selection/actions.js | 86 ++++++---- js/reducers/selection/constants.js | 5 +- js/reducers/selection/selectionReducers.js | 16 +- 11 files changed, 373 insertions(+), 145 deletions(-) create mode 100644 js/components/preview/molecule/searchSettingsDialog.js 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/preview/molecule/moleculeList.js b/js/components/preview/molecule/moleculeList.js index 06f58d1f7..208277912 100644 --- a/js/components/preview/molecule/moleculeList.js +++ b/js/components/preview/molecule/moleculeList.js @@ -45,7 +45,8 @@ import { withDisabledMoleculesNglControlButtons, removeSelectedTypesInHitNavigator, selectAllHits, - autoHideTagEditorDialogsOnScroll + autoHideTagEditorDialogsOnScroll, + searchForObservations } from './redux/dispatchActions'; import { DEFAULT_FILTER, PREDEFINED_FILTERS } from '../../../reducers/selection/constants'; import { Edit, FilterList } from '@material-ui/icons'; @@ -288,6 +289,8 @@ export const MoleculeList = memo(({ hideProjects }) => { 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); @@ -321,17 +324,13 @@ export const MoleculeList = 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 allMoleculesList.filter(molecule => molecule.code.toLowerCase().includes(searchString.toLowerCase())); + return dispatch(searchForObservations(searchString, allMoleculesList, searchSettings)); } else { return getJoinedMoleculeList; } - }, [getJoinedMoleculeList, allMoleculesList, searchString]); + }, [searchString, dispatch, allMoleculesList, searchSettings, getJoinedMoleculeList]); const addSelectedMoleculesFromUnselectedSites = useCallback( (joinedMoleculeLists, list) => { @@ -849,40 +848,7 @@ export const MoleculeList = memo(({ hideProjects }) => { 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 */ - /* - - ,*/ ({ 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/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..eb5f3d961 100644 --- a/js/components/preview/molecule/redux/dispatchActions.js +++ b/js/components/preview/molecule/redux/dispatchActions.js @@ -1215,3 +1215,31 @@ export const updatePose = newPose => async (dispatch, getState) => { export const createPose = newPose => async (dispatch, getState) => { return createPoseApi(newPose); }; + +const observationSearchFunctions = { + shortcode: (obs, searchTerm) => { + return obs.code?.toLowerCase().includes(searchTerm.toLowerCase()); + }, + aliases: (obs, searchTerm) => { + return obs.identifiers?.some(idf => idf.name.toLowerCase().includes(searchTerm.toLowerCase())); + }, + compoundId: (obs, searchTerm) => { + return obs.compound_code?.toLowerCase().includes(searchTerm.toLowerCase()); + } +}; + +export const searchForObservations = (searchTerm, observations, searchSettings) => (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/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..5af8eaaab 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: false, + compoundId: false + } + } }; 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 }; From 2beca6b268be820456cc4fc47aa93bc070eb7671 Mon Sep 17 00:00:00 2001 From: boriskovar-m2ms Date: Thu, 19 Dec 2024 14:15:07 +0100 Subject: [PATCH 4/4] implements issues #1533, #1622 (#472) * removed SGC column from target list * #1540 added preferred alias functionality * #1540 added edit column to target list and use target settings modal * #1540 updated "Open" label and read only version of display name * Squashed commit of the following: commit c5bba583d5cea4c6a24e40511ee7828e20256a17 Author: Boris Kovar Date: Tue Dec 10 08:13:00 2024 +0100 #1603 - fixes * Squashed commit of the following: commit 9d9bf8c7b810540f71e9ca8c9fae5de146fd1a5f Author: Boris Kovar Date: Wed Dec 11 15:20:54 2024 +0100 fixed frontend part of the ticket. Now properly resolves target based on the target name and target access string * - fixed issue when the template protein is missing * Squashed commit of the following: commit 2fd529e84c3969768ff7a595046a4a4fc134a4ad Author: Boris Kovar Date: Mon Dec 16 10:44:15 2024 +0100 - changes labels in searchSettingsDialog commit 12499a9630db29a29e26235c8dc9f75dce559d54 Author: Boris Kovar Date: Mon Dec 16 09:47:05 2024 +0100 -small fix commit cbf95f1ee181dbfe3a6675fb684705e3e84840f8 Merge: 5bd77562 900d8518 Author: Boris Kovar Date: Thu Dec 12 13:34:14 2024 +0100 Merge branch '#1563-LHS-search' of https://github.com/m2ms/fragalysis-frontend into #1563-LHS-search commit 5bd7756253160fe06b61cc6c2062cf982a5397ce Author: Boris Kovar Date: Thu Dec 12 13:30:00 2024 +0100 - search aliases commit 3e7540f0e6c7478e14e83fb3da4c4b9e41bb35bb Author: Boris Kovar Date: Tue Dec 10 12:12:03 2024 +0100 - checkpoint commit 6631373d07e2efc2ba48e5f237fc3a92c708b68d Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done commit 900d851868384a6efe36a7e48d2877bf3bcacdbd Author: Boris Kovar Date: Thu Dec 12 13:30:00 2024 +0100 - search aliases commit a72f427e90e3ef3c88f9b1d3e32828bfe3c11ca5 Author: Boris Kovar Date: Tue Dec 10 12:12:03 2024 +0100 - checkpoint commit 4b7d69940fbb19214b7c258c7be59552e580e14e Merge: 4579a9de 32c837dc Author: Boris Kovar Date: Tue Dec 10 10:26:34 2024 +0100 Merge branch '#1563-LHS-search' of https://github.com/m2ms/fragalysis-frontend into #1563-LHS-search commit 4579a9de85c4491442e38933b57822510ae5ab1c Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done commit 32c837dcf295b71836e028ea7750fa9bc8ead28c Author: Boris Kovar Date: Fri Dec 6 10:05:51 2024 +0100 - #1563 - all the UI and infrastructure and search by shortcode and compound id is done * - fixes #1622 and changes defaults for search settings * Squashed commit of the following: commit 021d6f2ab4f46346918cb51de66a63e766ad67f4 Merge: a5fb9016 182a10a7 Author: boriskovar-m2ms Date: Thu Dec 19 13:49:43 2024 +0100 Merge branch '#1533-direct-links' of https://github.com/m2ms/fragalysis-frontend into #1533-direct-links commit a5fb90161a01db52d1e439c41c212812f1952d72 Author: Boris Kovar Date: Tue Dec 17 16:56:49 2024 +0100 - implements #1533 commit 182a10a7a930d0d83a3a6d75e2cf5b85d03c1475 Author: Boris Kovar Date: Tue Dec 17 16:56:49 2024 +0100 - implements #1533 --------- Co-authored-by: matej --- js/components/direct/constants.js | 3 +- js/components/direct/directDisplay.js | 20 +- js/components/preview/Preview.js | 2 +- .../preview/molecule/hitNavigator.js | 1 - .../preview/molecule/moleculeList.js | 1257 ----------------- .../preview/molecule/redux/dispatchActions.js | 48 +- .../target/withLoadingTargetIdList.js | 18 +- js/reducers/selection/selectionReducers.js | 4 +- 8 files changed, 67 insertions(+), 1286 deletions(-) delete mode 100644 js/components/preview/molecule/moleculeList.js 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 } } };