import { rootSelector } from '@/redux/root/root.selectors'; import { ONE, SIZE_OF_EMPTY_ARRAY, ZERO } from '@/constants/common'; import { BioEntityWithPinType } from '@/types/bioEntity'; import { ElementIdTabObj } from '@/types/elements'; import { MultiSearchData } from '@/types/fetchDataState'; import { BioEntity, BioEntityContent, Comment, MapModel } from '@/types/models'; import { createSelector } from '@reduxjs/toolkit'; import { allCommentsSelectorOfCurrentMap, commentElementSelector, } from '@/redux/comment/comment.selectors'; import { currentDrawerReactionSelector } from '@/redux/reactions/reactions.selector'; import { allChemicalsBioEntitesOfAllMapsSelector, allChemicalsBioEntitesOfCurrentMapSelector, allChemicalsIdTabSelectorOfCurrentMap, chemicalsBioEntitiesForSelectedSearchElementSelector, searchedChemicalsBioEntitesOfCurrentMapSelector, } from '../chemicals/chemicals.selectors'; import { currentSelectedBioEntityIdSelector } from '../contextMenu/contextMenu.selector'; import { currentSearchedBioEntityId, currentSelectedSearchElement, } from '../drawer/drawer.selectors'; import { allDrugsBioEntitesOfAllMapsSelector, allDrugsBioEntitesOfCurrentMapSelector, allDrugsIdTabSelectorOfCurrentMap, drugsBioEntitiesForSelectedSearchElementSelector, searchedDrugsBioEntitesOfCurrentMapSelector, } from '../drugs/drugs.selectors'; import { currentModelIdSelector, modelsDataSelector } from '../models/models.selectors'; export const bioEntitySelector = createSelector(rootSelector, state => state.bioEntity); export const bioEntityDataSelector = createSelector(bioEntitySelector, bioEntity => bioEntity.data); export const bioEntityLoadingSelector = createSelector( bioEntitySelector, bioEntity => bioEntity.loading, ); export const bioEntityDataListSelector = createSelector(bioEntityDataSelector, bioEntityData => bioEntityData.map(b => b.data || []).flat(), ); export const allSubmapConnectionsBioEntitySelector = createSelector( bioEntitySelector, (bioEntityData): BioEntity[] => (bioEntityData?.submapConnections?.data || []).map(({ bioEntity }) => bioEntity), ); export const allSubmapConnectionsBioEntityOfCurrentSubmapSelector = createSelector( allSubmapConnectionsBioEntitySelector, currentModelIdSelector, (submapConnectionsBioEntity, currentModel): BioEntity[] => submapConnectionsBioEntity.filter(({ model }) => model === currentModel), ); export const bioEntitiesForSelectedSearchElement = createSelector( bioEntitySelector, currentSelectedSearchElement, (bioEntitiesState, currentSearchElement): MultiSearchData<BioEntityContent[]> | undefined => bioEntitiesState.data.find( ({ searchQueryElement }) => searchQueryElement === currentSearchElement, ), ); export const searchedFromMapBioEntityElement = createSelector( bioEntitiesForSelectedSearchElement, currentSearchedBioEntityId, (bioEntitiesState, currentBioEntityId): BioEntity | undefined => bioEntitiesState && bioEntitiesState.data?.find(({ bioEntity }) => bioEntity.id === currentBioEntityId)?.bioEntity, ); export const searchedBioEntityElementForContextMapSelector = createSelector( bioEntitySelector, currentSelectedBioEntityIdSelector, (bioEntitiesState, currentBioEntityId): BioEntity | undefined => { return bioEntitiesState.data .find(({ searchQueryElement }) => searchQueryElement === currentBioEntityId.toString()) ?.data?.find(({ bioEntity }) => bioEntity.id === currentBioEntityId)?.bioEntity; }, ); export const searchedBioEntityElementUniProtIdSelector = createSelector( searchedBioEntityElementForContextMapSelector, (bioEntitiesState): string | undefined => { return bioEntitiesState?.references.find(({ type }) => type === 'UNIPROT')?.resource; }, ); export const searchedFromMapBioEntityElementRelatedSubmapSelector = createSelector( searchedFromMapBioEntityElement, modelsDataSelector, (bioEntity, models): MapModel | undefined => models.find(({ idObject }) => idObject === bioEntity?.submodel?.mapId), ); export const loadingBioEntityStatusSelector = createSelector( bioEntitiesForSelectedSearchElement, state => state?.loading, ); export const searchedBioEntitesSelectorOfCurrentMap = createSelector( bioEntitySelector, currentSelectedSearchElement, currentModelIdSelector, (bioEntities, currentSearchElement, currentModelId): BioEntity[] => { if (!bioEntities) { return []; } return (bioEntities?.data || []) .filter(({ searchQueryElement }) => currentSearchElement ? searchQueryElement === currentSearchElement : true, ) .map(({ data }) => data || []) .flat() .filter(({ bioEntity }) => bioEntity.model === currentModelId) .map(({ bioEntity }) => bioEntity); }, ); export const allBioEntitesSelectorOfCurrentMap = createSelector( bioEntitySelector, currentModelIdSelector, (bioEntities, currentModelId): BioEntity[] => { if (!bioEntities) { return []; } return (bioEntities?.data || []) .map(({ data }) => data || []) .flat() .filter(({ bioEntity }) => bioEntity.model === currentModelId) .map(({ bioEntity }) => bioEntity); }, ); export const allBioEntitesIdTabSelectorOfCurrentMap = createSelector( bioEntitySelector, currentModelIdSelector, (bioEntities, currentModelId): ElementIdTabObj => { if (!bioEntities) { return {}; } return Object.fromEntries( (bioEntities?.data || []) .map(({ data, searchQueryElement }): [typeof data, string] => [data, searchQueryElement]) .map(([data, tab]) => (data || []) .flat() .filter(({ bioEntity }) => bioEntity.model === currentModelId) .map(d => [d.bioEntity.id, tab]), ) .flat(), ); }, ); export const numberOfBioEntitiesSelector = createSelector( bioEntitiesForSelectedSearchElement, state => (state?.data ? state.data.length : SIZE_OF_EMPTY_ARRAY), ); export const bioEntitiesPerModelSelector = createSelector( bioEntitiesForSelectedSearchElement, modelsDataSelector, (bioEntities, models) => { const bioEntitiesPerModelPerSearchElement = (models || []).map(model => { const bioEntitiesInGivenModel = (bioEntities?.data || []).filter( entity => model.idObject === entity.bioEntity.model, ); return { modelName: model.name, modelId: model.idObject, numberOfEntities: bioEntitiesInGivenModel.length, bioEntities: bioEntitiesInGivenModel, }; }); return bioEntitiesPerModelPerSearchElement.filter( model => model.numberOfEntities !== SIZE_OF_EMPTY_ARRAY, ); }, ); export const allVisibleBioEntitiesSelector = createSelector( searchedBioEntitesSelectorOfCurrentMap, searchedChemicalsBioEntitesOfCurrentMapSelector, searchedDrugsBioEntitesOfCurrentMapSelector, (content, chemicals, drugs): BioEntity[] => { return [content, chemicals, drugs].flat(); }, ); export const allElementsForSearchElementSelector = createSelector( bioEntitiesForSelectedSearchElement, chemicalsBioEntitiesForSelectedSearchElementSelector, drugsBioEntitiesForSelectedSearchElementSelector, (content, chemicals, drugs): BioEntity[] => { const contentBioEntities = (content?.data || []).map(({ bioEntity }) => bioEntity); return [contentBioEntities, chemicals || [], drugs || []].flat(); }, ); export const allElementsForSearchElementNumberByModelId = createSelector( allElementsForSearchElementSelector, (elements): Record<number, number> => { return elements.reduce( (acc, { model }) => ({ ...acc, [model]: (acc?.[model] || ZERO) + ONE, }), {} as Record<number, number>, ); }, ); export const allContentBioEntitesSelectorOfAllMaps = createSelector( bioEntitySelector, (bioEntities): BioEntity[] => { if (!bioEntities) { return []; } return (bioEntities?.data || []) .map(({ data }) => data || []) .flat() .map(({ bioEntity }) => bioEntity); }, ); export const allBioEntitiesSelector = createSelector( allContentBioEntitesSelectorOfAllMaps, allChemicalsBioEntitesOfAllMapsSelector, allDrugsBioEntitesOfAllMapsSelector, allSubmapConnectionsBioEntitySelector, (content, chemicals, drugs, submapConnections): BioEntity[] => { return [content, chemicals, drugs, submapConnections].flat(); }, ); export const allBioEntitiesElementsIdsSelector = createSelector( allBioEntitesIdTabSelectorOfCurrentMap, allChemicalsIdTabSelectorOfCurrentMap, allDrugsIdTabSelectorOfCurrentMap, (content, chemicals, drugs): ElementIdTabObj => { return { ...content, ...chemicals, ...drugs, }; }, ); export const currentDrawerBioEntitySelector = createSelector( allBioEntitiesSelector, commentElementSelector, currentSearchedBioEntityId, (bioEntities, commentElement, currentBioEntityId): BioEntity | undefined => { if (commentElement && commentElement.id === currentBioEntityId) { return commentElement; } return bioEntities.find(({ id }) => id === currentBioEntityId); }, ); export const currentDrawerBioEntityRelatedSubmapSelector = createSelector( currentDrawerBioEntitySelector, modelsDataSelector, (bioEntity, models): MapModel | undefined => models.find(({ idObject }) => idObject === bioEntity?.submodel?.mapId), ); export const allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector = createSelector( allSubmapConnectionsBioEntityOfCurrentSubmapSelector, allElementsForSearchElementNumberByModelId, (submapConnectionsBioEntity, modelElementsNumber): BioEntity[] => { return submapConnectionsBioEntity.filter( ({ submodel }) => submodel && modelElementsNumber?.[submodel.mapId] > ZERO, ); }, ); export const allBioEntitiesWithTypeOfCurrentMapSelector = createSelector( allBioEntitesSelectorOfCurrentMap, allChemicalsBioEntitesOfCurrentMapSelector, allDrugsBioEntitesOfCurrentMapSelector, allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector, (content, chemicals, drugs, submapConnections): BioEntityWithPinType[] => { return [ content.map(v => ({ ...v, type: 'bioEntity' as const })), chemicals.map(v => ({ ...v, type: 'chemicals' as const })), drugs.map(v => ({ ...v, type: 'drugs' as const })), submapConnections.map(v => ({ ...v, type: 'bioEntity' as const })), ].flat(); }, ); export const allVisibleBioEntitiesIdsSelector = createSelector( allVisibleBioEntitiesSelector, allSubmapConnectionsBioEntityOfCurrentSubmapWithRealConnectionsSelector, (elements, submapConnections): (string | number)[] => { return [...elements, ...submapConnections].map(e => e.id); }, ); export const currentDrawerElementCommentsSelector = createSelector( currentDrawerBioEntitySelector, allCommentsSelectorOfCurrentMap, (element, comments): Comment[] => { if (element) { return comments.filter( comment => comment.type === 'ALIAS' && comment.modelId === element.model && Number(comment.elementId) === element.id, ); } return []; }, ); export const currentDrawerReactionCommentsSelector = createSelector( currentDrawerReactionSelector, allCommentsSelectorOfCurrentMap, (reaction, comments): Comment[] => { if (reaction) { return comments.filter( comment => comment.type === 'REACTION' && comment.modelId === reaction.modelId && Number(comment.elementId) === reaction.id, ); } return []; }, );