From 07b0882f12eabb3f5e16a5e77e9383c0828de2ef Mon Sep 17 00:00:00 2001 From: Kartik Puri Date: Mon, 16 Mar 2026 17:45:51 -0700 Subject: [PATCH 1/5] react compiler bug fixes and improvements --- buildScript/webpack.config.js | 2 +- src/firefly/js/ui/tap/Constraints.js | 39 +++++++++++--------- src/firefly/js/ui/tap/SpatialSearch.jsx | 47 +++++++++++++++++-------- 3 files changed, 55 insertions(+), 33 deletions(-) diff --git a/buildScript/webpack.config.js b/buildScript/webpack.config.js index 58ae3b6ea7..45b9da7281 100644 --- a/buildScript/webpack.config.js +++ b/buildScript/webpack.config.js @@ -36,7 +36,7 @@ export default function makeWebpackConfig(config) { const MIN_CHROME_VERSION= '130'; const MIN_FIREFOX_VERSION= '134'; const MIN_EDGE_VERSION= '130'; - const useReactCompiler = false; + const useReactCompiler = true; if (!process.env.NODE_ENV) { process.env.NODE_ENV = ['local', 'dev'].includes(BUILD_ENV) ? 'development' : 'production'; diff --git a/src/firefly/js/ui/tap/Constraints.js b/src/firefly/js/ui/tap/Constraints.js index 2914986439..438fd4f9c2 100644 --- a/src/firefly/js/ui/tap/Constraints.js +++ b/src/firefly/js/ui/tap/Constraints.js @@ -3,10 +3,23 @@ import React from 'react'; export const ConstraintContext = React.createContext({}); +function hasAdqlConstraint(constraintObj) { + return Boolean( + constraintObj?.adqlConstraint || + constraintObj?.adqlConstraintsAry?.length + ); +} + +function getAdqlConstraintsList(constraintObj) { + if (constraintObj?.adqlConstraintsAry?.length) return constraintObj.adqlConstraintsAry; + if (constraintObj?.adqlConstraint) return [constraintObj.adqlConstraint]; + return []; +} + export function isTapUpload(tapBrowserState) { const {constraintFragments}= tapBrowserState; return [...constraintFragments.values()] - .some( (c) => Boolean(c.uploadFile && c.TAP_UPLOAD && c.adqlConstraint)); + .some((c) => Boolean(c.uploadFile && c.TAP_UPLOAD && hasAdqlConstraint(c))); } /** @@ -16,7 +29,8 @@ export function isTapUpload(tapBrowserState) { */ export function getUploadConstraint(tapBrowserState) { const {constraintFragments}= tapBrowserState; - return [...constraintFragments.values()].find( (c) => Boolean(c.uploadFile && c.TAP_UPLOAD && c.adqlConstraint)); + return [...constraintFragments.values()] + .find((c) => Boolean(c.uploadFile && c.TAP_UPLOAD && hasAdqlConstraint(c))); } export function getTapUploadSchemaEntry(tapBrowserState) { @@ -38,26 +52,17 @@ export function getHelperConstraints(tapBrowserState) { const {constraintFragments}= tapBrowserState; const adqlConstraints = []; const adqlConstraintErrorsArray = []; - const siaConstraints = []; - // adqlComponents can apparently be modified during iteration in the forEach... + Array.from(constraintFragments.values()).forEach((constraintObj) => { - if (!constraintObj.adqlConstraintErrors?.length) { - if (constraintObj.adqlConstraint) { - adqlConstraints.push(constraintObj.adqlConstraint); - } + if (!constraintObj?.constraintErrors?.length) { + adqlConstraints.push(...getAdqlConstraintsList(constraintObj)); } else { - adqlConstraintErrorsArray.push(constraintObj.constraintErrors); - } - if (!constraintObj.constraintErrors?.length) { - if (constraintObj.siaConstraints?.length > 0) { - siaConstraints.push(...constraintObj.siaConstraints); - } - } else { - adqlConstraintErrorsArray.push(constraintObj.constraintErrors); + adqlConstraintErrorsArray.push(...constraintObj.constraintErrors); } }); + return { - valid: adqlConstraintErrorsArray?.length === 0, + valid: adqlConstraintErrorsArray.length === 0, messages: adqlConstraintErrorsArray, where: adqlConstraints.join('\n AND ') }; diff --git a/src/firefly/js/ui/tap/SpatialSearch.jsx b/src/firefly/js/ui/tap/SpatialSearch.jsx index 6d4586e02e..1a096c42e6 100644 --- a/src/firefly/js/ui/tap/SpatialSearch.jsx +++ b/src/firefly/js/ui/tap/SpatialSearch.jsx @@ -28,12 +28,7 @@ import {DEF_TARGET_PANEL_KEY} from '../TargetPanel.jsx'; import {ConstraintContext} from './Constraints.js'; import {ROW_POSITION, SEARCH_POSITION} from './Cutout'; import {getDataServiceOption} from './DataServicesOptions'; -import { - DebugObsCore, - getPanelPrefix, - makeCollapsibleCheckHeader, - makeFieldErrorList, - makePanelStatusUpdater, +import {DebugObsCore, getPanelPrefix, makeCollapsibleCheckHeader, makeFieldErrorList, } from './TableSearchHelpers.jsx'; import {showUploadTableChooser} from '../UploadTableChooser.js'; import { @@ -134,7 +129,6 @@ export function SpatialSearch({sx, cols, serviceUrl, serviceLabel, serviceId, co const {setConstraintFragment}= useContext(ConstraintContext); const {setVal,getVal,makeFldObj}= useContext(FieldGroupCtx); - const [constraintResult, setConstraintResult] = useState({}); const [getUploadInfo, setUploadInfo]= useFieldGroupValue('uploadInfo'); const [posDefaultOpenMsg, setPosDefaultOpenMsg]= useState(true); @@ -142,8 +136,6 @@ export function SpatialSearch({sx, cols, serviceUrl, serviceLabel, serviceId, co const uploadInfo= getUploadInfo() || undefined; - const updatePanelStatus= makePanelStatusUpdater(checkHeaderCtl.isPanelActive(), Spatial); - useEffect(() => { if (!canUpload) setVal(SPATIAL_TYPE,SINGLE); }, [serviceUrl,canUpload]); @@ -236,15 +228,40 @@ export function SpatialSearch({sx, cols, serviceUrl, serviceLabel, serviceId, co useFieldGroupWatch([cornerCalcType], () => onChangeToPolygonMethod()); - useEffect(() => { - const constraints= makeSpatialConstraints(columnsModel, obsCoreEnabled, makeFldObj(fldListAry), uploadInfo, tableName, canUpload,useSIAv2); - updatePanelStatus(constraints, constraintResult, setConstraintResult,useSIAv2); - }); - + const targetWp = getVal(ServerParams.USER_TARGET_WORLD_PT); + const spatialRegOp = getVal(SpatialRegOp); + const spatialType = getVal(SPATIAL_TYPE); + const spatialMethodVal = getVal(SpatialMethod); + const radiusVal = getVal(RadiusSize); + const polygonCornersVal = getVal(PolygonCorners); + const centerLonVal = getVal(CenterLonColumns); + const centerLatVal = getVal(CenterLatColumns); + const uploadCenterLonVal = getVal(UploadCenterLonColumns); + const uploadCenterLatVal = getVal(UploadCenterLatColumns); + const cornerCalcTypeVal = getVal(cornerCalcType); + const closestVal = getVal(Closest); + + const constraintResult = React.useMemo(() => { + const constraints = makeSpatialConstraints( + columnsModel, obsCoreEnabled, makeFldObj(fldListAry), + uploadInfo, tableName, canUpload, useSIAv2 + ); + + return { + ...constraints, + constraintErrors: [...(constraints.errAry ?? [])], + simpleError: constraints.errAry?.[0] ?? '', + }; + }, [columnsModel, obsCoreEnabled, uploadInfo, tableName, canUpload, + useSIAv2, targetWp, spatialRegOp, spatialType, spatialMethodVal, + radiusVal, polygonCornersVal, centerLonVal, centerLatVal, + uploadCenterLonVal, uploadCenterLatVal, cornerCalcTypeVal, closestVal, + ]); + useEffect(() => { setConstraintFragment(panelPrefix, constraintResult); return () => setConstraintFragment(panelPrefix, ''); - }, [constraintResult]); + }, [panelPrefix, setConstraintFragment, constraintResult]); if (disablePanel) { return ( From b233af35e7016e44b3987da3564cca49d77302d4 Mon Sep 17 00:00:00 2001 From: Kartik Puri Date: Mon, 16 Mar 2026 18:22:49 -0700 Subject: [PATCH 2/5] more fixes --- src/firefly/js/ui/tap/SpatialSearch.jsx | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/src/firefly/js/ui/tap/SpatialSearch.jsx b/src/firefly/js/ui/tap/SpatialSearch.jsx index 1a096c42e6..62698ed939 100644 --- a/src/firefly/js/ui/tap/SpatialSearch.jsx +++ b/src/firefly/js/ui/tap/SpatialSearch.jsx @@ -338,13 +338,11 @@ function getSpacialLayoutMode(spacialType, obsCoreEnabled, canUpload) { const SpatialSearchLayout = ({initArgs, obsCoreEnabled, uploadInfo, setUploadInfo, serviceLabel, serviceId, hipsUrl, centerWP, fovDeg, capabilities, embeddedInHiPS, slotProps}) => { - const {getVal}= useContext(FieldGroupCtx); - - const spacialType= getVal(SPATIAL_TYPE) ?? SINGLE; - const spatialMethod= getVal(SpatialMethod)??CONE_CHOICE_KEY; - const closest= getVal(Closest)??''; - const cornerCalcTypeValue= getVal(cornerCalcType)??'image'; - const spatialRegOpValue= getVal(SpatialRegOp) ?? SpatialRegOpType.CONTAINS_POINT; + const spacialType = useFieldGroupValue(SPATIAL_TYPE)[0]() ?? SINGLE; + const spatialMethod = useFieldGroupValue(SpatialMethod)[0]() ?? CONE_CHOICE_KEY; + const closest = useFieldGroupValue(Closest)[0]() ?? ''; + const cornerCalcTypeValue = useFieldGroupValue(cornerCalcType)[0]() ?? 'image'; + const spatialRegOpValue = useFieldGroupValue(SpatialRegOp)[0]() ?? SpatialRegOpType.CONTAINS_POINT; const layoutMode= getSpacialLayoutMode(spacialType,obsCoreEnabled,capabilities?.canUpload); const isCone= spatialMethod === CONE_CHOICE_KEY; const containsPoint= spatialRegOpValue === SpatialRegOpType.CONTAINS_POINT; From 6b4b61a0f58787ae20cba928062b0bcae4a159c6 Mon Sep 17 00:00:00 2001 From: Kartik Puri Date: Tue, 17 Mar 2026 17:22:11 -0700 Subject: [PATCH 3/5] tap dropdown and user entered urls bug fixes --- src/firefly/js/ui/ListBoxInputField.jsx | 4 +- src/firefly/js/ui/tap/TapSearchRootPanel.jsx | 96 ++++++++++++-------- src/firefly/js/ui/tap/TapUtil.js | 51 ++++++++--- 3 files changed, 100 insertions(+), 51 deletions(-) diff --git a/src/firefly/js/ui/ListBoxInputField.jsx b/src/firefly/js/ui/ListBoxInputField.jsx index 940aa65c68..060700bc3a 100644 --- a/src/firefly/js/ui/ListBoxInputField.jsx +++ b/src/firefly/js/ui/ListBoxInputField.jsx @@ -33,9 +33,9 @@ export function ListBoxInputFieldView({value:fieldValue='', onChange, fieldKey, - {options?.map((({value,label,disabled=false}) => { + {options?.map((({value,label,disabled=false},idx) => { return ( - ); diff --git a/src/firefly/js/ui/tap/Constraints.js b/src/firefly/js/ui/tap/Constraints.js index 438fd4f9c2..4631c3bb71 100644 --- a/src/firefly/js/ui/tap/Constraints.js +++ b/src/firefly/js/ui/tap/Constraints.js @@ -3,7 +3,7 @@ import React from 'react'; export const ConstraintContext = React.createContext({}); -function hasAdqlConstraint(constraintObj) { +export function hasAdqlConstraint(constraintObj) { return Boolean( constraintObj?.adqlConstraint || constraintObj?.adqlConstraintsAry?.length diff --git a/src/firefly/js/ui/tap/SpatialSearch.jsx b/src/firefly/js/ui/tap/SpatialSearch.jsx index 62698ed939..d94f3a266e 100644 --- a/src/firefly/js/ui/tap/SpatialSearch.jsx +++ b/src/firefly/js/ui/tap/SpatialSearch.jsx @@ -241,7 +241,19 @@ export function SpatialSearch({sx, cols, serviceUrl, serviceLabel, serviceId, co const cornerCalcTypeVal = getVal(cornerCalcType); const closestVal = getVal(Closest); + const isSpatialPanelActive = checkHeaderCtl?.isPanelActive(); + const constraintResult = React.useMemo(() => { + + //when spatial panel is inactive, it should not contribute constraints or errors + if (!isSpatialPanelActive) { + return { + adqlConstraintsAry: [], + constraintErrors: [], + simpleError: '', + }; + } + const constraints = makeSpatialConstraints( columnsModel, obsCoreEnabled, makeFldObj(fldListAry), uploadInfo, tableName, canUpload, useSIAv2 @@ -252,7 +264,7 @@ export function SpatialSearch({sx, cols, serviceUrl, serviceLabel, serviceId, co constraintErrors: [...(constraints.errAry ?? [])], simpleError: constraints.errAry?.[0] ?? '', }; - }, [columnsModel, obsCoreEnabled, uploadInfo, tableName, canUpload, + }, [isSpatialPanelActive, columnsModel, obsCoreEnabled, uploadInfo, tableName, canUpload, useSIAv2, targetWp, spatialRegOp, spatialType, spatialMethodVal, radiusVal, polygonCornersVal, centerLonVal, centerLatVal, uploadCenterLonVal, uploadCenterLatVal, cornerCalcTypeVal, closestVal, diff --git a/src/firefly/js/ui/tap/TapSearchRootPanel.jsx b/src/firefly/js/ui/tap/TapSearchRootPanel.jsx index 3228e94a97..eb3232c9e2 100644 --- a/src/firefly/js/ui/tap/TapSearchRootPanel.jsx +++ b/src/firefly/js/ui/tap/TapSearchRootPanel.jsx @@ -125,6 +125,8 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const tapOps = useMemo(() => makeTapServiceOptions(tapConfig.services, tapConfig.additional?.services, userServices), [tapConfig.services, tapConfig.additional?.services, userServices]); const tapState= getTapBrowserState(); + if (!initArgs?.urlApi?.execute) searchFromAPIOnce(true); //if not execute then mark as done, i.e. disable any auto searching + initApiAddedServiceOnce(initArgs); //only look for the extra service the first time const {current:clickFuncRef} = useRef({clickFunc:undefined}); const [selectBy, setSelectBy]= useState(() => { const val= getVal('selectBy'); @@ -135,7 +137,7 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const [servicesShowing, setServicesShowingInternal]= useState(tapState.lastServicesShowing); const [obsCoreTableModel, setObsCoreTableModel] = useState(); const [serviceUrl, setServiceUrl]= useState(() => getInitServiceUrl(tapState,initArgs,tapOps,lockedServiceUrl,lockedServiceName)); - + activateInitArgsAdqlOnce(groupKey, tapState,initArgs,setSelectBy); const setServicesShowing= (showing) => { setServicesShowingInternal(showing); @@ -144,10 +146,10 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const obsCoreEnabled = obsCoreTableModel?.tableData?.data?.length > 0; - const onDeleteServiceOption = (deletedValue) => { + const onDeleteServiceOption = (deletedValue, clearServiceOnDelete=false) => { deleteUserService(deletedValue); - if (serviceUrl === deletedValue) { + if (clearServiceOnDelete) { setServiceUrl(''); setTapBrowserState({...getTapBrowserState(), serviceUrl: ''}); setVal(ADQL_QUERY_KEY, ''); @@ -156,7 +158,6 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock } }; - const onTapServiceOptionSelect= (selectedOption) => { if (!selectedOption) return; setVal(ADQL_QUERY_KEY, ''); @@ -167,18 +168,6 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock setTapBrowserState({...getTapBrowserState(), serviceUrl: nextServiceUrl}); }; - useEffect(() => { - if (!initArgs?.urlApi?.execute) searchFromAPIOnce(true); - }, [initArgs?.urlApi?.execute]); - - useEffect(() => { - initApiAddedServiceOnce(initArgs); - }, [initArgs]); - - useEffect(() => { - activateInitArgsAdqlOnce(groupKey, tapState,initArgs,setSelectBy); - }, [groupKey, initArgs, setSelectBy, tapState]); - useEffect(() => { const {serviceUrl:u}= initArgs?.searchParams ?? {}; u && u!==serviceUrl && setServiceUrl(u); @@ -380,9 +369,9 @@ function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect }, renderValue: ({value}) => - (), + (), decorator: - (label,value) => (), + (label,value) => (), }} /> )} {enterUrl ? 'Type the url of a TAP service & press enter' : 'Choose a TAP service from the list'} @@ -394,7 +383,7 @@ function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect } -function ServiceOpRender({ops, value, sx, onDeleteServiceOption}) { +function ServiceOpRender({ops, value, sx, onDeleteServiceOption, clearServiceOnDelete=false}) { const op = ops.find((t) => t.value === value); if (!op) return 'none'; return ( @@ -409,10 +398,7 @@ function ServiceOpRender({ops, value, sx, onDeleteServiceOption}) { { op.userAdded && - { e.preventDefault(); e.stopPropagation(); @@ -420,7 +406,7 @@ function ServiceOpRender({ops, value, sx, onDeleteServiceOption}) { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - onDeleteServiceOption(value); + onDeleteServiceOption(value, clearServiceOnDelete); }} /> } diff --git a/src/firefly/js/ui/tap/TapSearchSubmit.js b/src/firefly/js/ui/tap/TapSearchSubmit.js index 9c7248bfed..6c922af8b0 100644 --- a/src/firefly/js/ui/tap/TapSearchSubmit.js +++ b/src/firefly/js/ui/tap/TapSearchSubmit.js @@ -9,7 +9,7 @@ import { getHelperConstraints, getTapUploadSchemaEntry, getUploadServerFile, - getUploadTableName, + getUploadTableName, hasAdqlConstraint, isTapUpload } from 'firefly/ui/tap/Constraints'; import {makeTblRequest, setNoCache} from 'firefly/tables/TableRequestUtil'; @@ -105,9 +105,9 @@ export function onTapSearchSubmit({request, serviceUrl, tapBrowserState, additio function getCutoutType(tapBrowserState) { const spatial= tapBrowserState?.constraintFragments?.get('spatial'); - if (spatial?.adqlConstraint?.length) return spatial.cutoutType; + if (hasAdqlConstraint(spatial)) return spatial.cutoutType; const location= tapBrowserState?.constraintFragments?.get('location'); - if (location?.adqlConstraint?.length) return location.cutoutType; + if (hasAdqlConstraint(location)) return location.cutoutType; return ROW_POSITION; } @@ -129,7 +129,7 @@ export function getAdqlQuery(tapBrowserState, additionalClauses, allowColumnCons if (isUpload) { //check for more than one upload file (in Spatial and in ObjectID col) - should this be a utility function in constraints.js? const { constraintFragments } = tapBrowserState; const entries = [...constraintFragments.values()]; - const matchingEntries = entries.filter((c) => Boolean(c.uploadFile && c.TAP_UPLOAD && c.adqlConstraint)); + const matchingEntries = entries.filter((c) => Boolean(c.uploadFile && c.TAP_UPLOAD && hasAdqlConstraint(c))); if (matchingEntries.length > 1) { if (showErrors) showInfoPopup('We currently do not support searches with more than one uploaded table.', 'Error'); return; diff --git a/src/firefly/js/ui/tap/TapUtil.js b/src/firefly/js/ui/tap/TapUtil.js index ae4a547156..c3252e25c4 100644 --- a/src/firefly/js/ui/tap/TapUtil.js +++ b/src/firefly/js/ui/tap/TapUtil.js @@ -574,13 +574,23 @@ export function getTapServices() { const baseName= 'User Entered'; function getUserServiceAry() { - return [...getPreference(USER_SERVICE_PREFS, [])]; + const seen = new Set(); + + return [...getPreference(USER_SERVICE_PREFS, [])] + .filter((entry) => entry?.value?.trim()) + .filter((entry) => { + const key = normalizeTapUrl(entry.value); + if (!key || seen.has(key)) return false; + seen.add(key); + return true; + }); } + export function addUserService(serviceUrl) { const userServices = getUserServiceAry(); const normalizedServiceUrl = normalizeTapUrl(serviceUrl); - if (getTapServiceByURL(normalizedServiceUrl)) return; // don't add if service already exist + if (getTapServices().some(({value}) => normalizeTapUrl(value) === normalizedServiceUrl)) return; //don't add if service already exists const usedNumAry = userServices .map((s) => s.label) @@ -710,7 +720,7 @@ export const getServiceHiPS= (serviceUrl) => getTapServiceByURL(serviceUrl)?.hip export const normalizeTapUrl = (url='') => url.trim().replace(/\/+$/, '').toLowerCase(); export function makeTapServiceOptions(baseServices, additionalServices, userServices=[]) { - const startingTapServices= hasElements(baseServices) ? [...baseServices] : getTAPServicesByName(); + const startingTapServices= hasElements(baseServices) ? [...baseServices] : makeTAPDefaultServicesByName(); const mergedServices= mergeServices(startingTapServices, additionalServices); const allServices= [...mergedServices, ...userServices]; const seen = new Set(); From 6ff9f6229550efdde8af50c83c427a24676c2d3d Mon Sep 17 00:00:00 2001 From: Kartik Puri Date: Mon, 30 Mar 2026 17:34:09 -0700 Subject: [PATCH 5/5] revert changes for additional bugs --- src/firefly/js/ui/tap/TapSearchRootPanel.jsx | 80 +++++++++----------- src/firefly/js/ui/tap/TapUtil.js | 61 +++------------ 2 files changed, 48 insertions(+), 93 deletions(-) diff --git a/src/firefly/js/ui/tap/TapSearchRootPanel.jsx b/src/firefly/js/ui/tap/TapSearchRootPanel.jsx index eb3232c9e2..4c936f74ac 100644 --- a/src/firefly/js/ui/tap/TapSearchRootPanel.jsx +++ b/src/firefly/js/ui/tap/TapSearchRootPanel.jsx @@ -2,7 +2,7 @@ * License information at https://github.com/Caltech-IPAC/firefly/blob/master/License.txt */ import {Box, Button, ChipDelete, FormHelperText, Stack, Typography} from '@mui/joy'; -import {getAppOptions, getPreference} from 'firefly/core/AppDataCntlr.js'; +import {getAppOptions} from 'firefly/core/AppDataCntlr.js'; import {dispatchMultiValueChange} from 'firefly/fieldGroup/FieldGroupCntlr.js'; import FieldGroupUtils, {getFieldVal} from 'firefly/fieldGroup/FieldGroupUtils.js'; import {FieldGroup, FieldGroupCtx} from 'firefly/ui/FieldGroup.jsx'; @@ -12,7 +12,9 @@ import { ADQL_QUERY_KEY, defTapBrowserState, deleteUserService, getMaxrecHardLimit, loadObsCoreSchemaTables, tapHelpId, USER_ENTERED_TITLE, - makeTapServiceOptions, USER_SERVICE_PREFS, getTapServiceOptions, + getServiceLabel, + getServiceNamesAsKey, + getTapServiceOptions, } from 'firefly/ui/tap/TapUtil.js'; import {ValidationField} from 'firefly/ui/ValidationField.jsx'; import {intValidator} from 'firefly/util/Validate.js'; @@ -20,10 +22,10 @@ import {makeSearchOnce} from 'firefly/util/WebUtil.js'; import {TextButton} from 'firefly/visualize/ui/Buttons.jsx'; import {once} from 'lodash'; import {bool, object, shape, string} from 'prop-types'; -import React, {useContext, useEffect, useMemo, useRef, useState} from 'react'; +import React, {useContext, useEffect, useRef, useState} from 'react'; import {InputField} from '../InputField.jsx'; import {ListBoxInputFieldView} from '../ListBoxInputField.jsx'; -import {useFieldGroupMetaState, useFieldGroupValue, useStoreConnector} from '../SimpleComponent.jsx'; +import {useFieldGroupMetaState, useFieldGroupValue} from '../SimpleComponent.jsx'; import {SwitchInputField} from '../SwitchInputField.jsx'; import {ConstraintContext, getHelperConstraints, getUploadConstraint, isTapUpload} from './Constraints.js'; import {TapTitleCustomizeButton} from './TableSearchHelpers'; @@ -120,13 +122,11 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const {setVal,getVal,setFld,groupKey}= useContext(FieldGroupCtx); const [getTapBrowserState,setTapBrowserState]= useFieldGroupMetaState(defTapBrowserState); const [getUserTitle,setUserTitle]= useFieldGroupValue(USER_ENTERED_TITLE); - const tapConfig = useStoreConnector(() => getAppOptions().tap ?? {}); - const userServices = useStoreConnector(() => getPreference(USER_SERVICE_PREFS, [])); - const tapOps = useMemo(() => makeTapServiceOptions(tapConfig.services, tapConfig.additional?.services, userServices), - [tapConfig.services, tapConfig.additional?.services, userServices]); + const [srvNameKey, setSrvNameKey]= useState(() => getServiceNamesAsKey()); const tapState= getTapBrowserState(); - if (!initArgs?.urlApi?.execute) searchFromAPIOnce(true); //if not execute then mark as done, i.e. disable any auto searching - initApiAddedServiceOnce(initArgs); //only look for the extra service the first time + if (!initArgs?.urlApi?.execute) searchFromAPIOnce(true); // if not execute then mark as done, i.e. disable any auto searching + initApiAddedServiceOnce(initArgs); // only look for the extra service the first time + const tapOps= getTapServiceOptions(); const {current:clickFuncRef} = useRef({clickFunc:undefined}); const [selectBy, setSelectBy]= useState(() => { const val= getVal('selectBy'); @@ -139,6 +139,7 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const [serviceUrl, setServiceUrl]= useState(() => getInitServiceUrl(tapState,initArgs,tapOps,lockedServiceUrl,lockedServiceName)); activateInitArgsAdqlOnce(groupKey, tapState,initArgs,setSelectBy); + const setServicesShowing= (showing) => { setServicesShowingInternal(showing); setTapBrowserState({...getTapBrowserState(), lastServicesShowing:showing}); @@ -146,26 +147,20 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock const obsCoreEnabled = obsCoreTableModel?.tableData?.data?.length > 0; - const onDeleteServiceOption = (deletedValue, clearServiceOnDelete=false) => { - deleteUserService(deletedValue); - - if (clearServiceOnDelete) { - setServiceUrl(''); - setTapBrowserState({...getTapBrowserState(), serviceUrl: ''}); - setVal(ADQL_QUERY_KEY, ''); - setFld(ADQL_QUERY_KEY, {placeholder: '', value: ''}); - setObsCoreTableModel(undefined); - } - }; - const onTapServiceOptionSelect= (selectedOption) => { - if (!selectedOption) return; + if (!selectedOption) { + setSrvNameKey(getServiceNamesAsKey()); + return; + } setVal(ADQL_QUERY_KEY, ''); setFld(ADQL_QUERY_KEY, {placeholder: '', value: ''}); - const nextServiceUrl = lockedServiceUrl ? serviceUrl : selectedOption?.value; - if (!lockedServiceUrl) setServiceUrl(nextServiceUrl); + if (!lockedServiceUrl) { + const serviceUrl= selectedOption?.value; + setServiceUrl(serviceUrl); + } setObsCoreTableModel(undefined); - setTapBrowserState({...getTapBrowserState(), serviceUrl: nextServiceUrl}); + setSrvNameKey(getServiceNamesAsKey()); + setTapBrowserState({...getTapBrowserState(), serviceUrl}); }; useEffect(() => { @@ -217,7 +212,7 @@ function TapSearchPanelImpl({initArgs= {}, titleOn=true, lockService=false, lock @@ -244,10 +239,10 @@ TapSearchPanel.propTypes= { function TapSearchPanelComponents({initArgs, serviceUrl, servicesShowing, setServicesShowing, onTapServiceOptionSelect, - lockService, lockObsCore, obsCoreLockTitle, tapOps,onDeleteServiceOption, + lockService, lockObsCore, obsCoreLockTitle, tapOps, lockedSchemaName, lockedTableName, titleOn=true, selectBy, setSelectBy}) { - const serviceLabel= tapOps.find((op) => op.value === serviceUrl)?.labelOnly || ''; + const serviceLabel= getServiceLabel(serviceUrl); const [obsCoreTableModel, setObsCoreTableModel] = useState(); const [error, setError] = useState(undefined); const hasObsCoreTable = obsCoreTableModel?.tableData?.data?.length > 0; @@ -276,7 +271,7 @@ function TapSearchPanelComponents({initArgs, serviceUrl, servicesShowing, setSer return ( {titleOn && TAP Searches } - { showWarning ? : @@ -309,7 +304,7 @@ function ServiceWarning({error,serviceUrl}) { } -function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect, onDeleteServiceOption} ) { +function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect} ) { const [extraStyle,setExtraStyle] = useState({overflow:'hidden'}); const enterUrl= useFieldGroupValue('enterUrl')[0](); @@ -369,9 +364,11 @@ function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect }, renderValue: ({value}) => - (), + (), decorator: - (label,value) => (), + (label,value) => (), }} /> )} {enterUrl ? 'Type the url of a TAP service & press enter' : 'Choose a TAP service from the list'} @@ -383,7 +380,7 @@ function Services({serviceUrl, servicesShowing, tapOps, onTapServiceOptionSelect } -function ServiceOpRender({ops, value, sx, onDeleteServiceOption, clearServiceOnDelete=false}) { +function ServiceOpRender({ops, value, onTapServiceOptionSelect, sx, clearServiceOnDelete=false}) { const op = ops.find((t) => t.value === value); if (!op) return 'none'; return ( @@ -399,15 +396,12 @@ function ServiceOpRender({ops, value, sx, onDeleteServiceOption, clearServiceOnD { op.userAdded && { - e.preventDefault(); - e.stopPropagation(); - }} - onClick={(e) => { - e.preventDefault(); - e.stopPropagation(); - onDeleteServiceOption(value, clearServiceOnDelete); - }} + onClick={(e) => { + deleteUserService(value); + if (clearServiceOnDelete) onTapServiceOptionSelect({value:''}); + else onTapServiceOptionSelect(); + e.stopPropagation?.(); + }} /> } diff --git a/src/firefly/js/ui/tap/TapUtil.js b/src/firefly/js/ui/tap/TapUtil.js index c3252e25c4..a4eff43a42 100644 --- a/src/firefly/js/ui/tap/TapUtil.js +++ b/src/firefly/js/ui/tap/TapUtil.js @@ -574,45 +574,27 @@ export function getTapServices() { const baseName= 'User Entered'; function getUserServiceAry() { - const seen = new Set(); - - return [...getPreference(USER_SERVICE_PREFS, [])] - .filter((entry) => entry?.value?.trim()) - .filter((entry) => { - const key = normalizeTapUrl(entry.value); - if (!key || seen.has(key)) return false; - seen.add(key); - return true; - }); + return getPreference(USER_SERVICE_PREFS, []); } - export function addUserService(serviceUrl) { - const userServices = getUserServiceAry(); - const normalizedServiceUrl = normalizeTapUrl(serviceUrl); - if (getTapServices().some(({value}) => normalizeTapUrl(value) === normalizedServiceUrl)) return; //don't add if service already exists - - const usedNumAry = userServices - .map((s) => s.label) + const userServices= getUserServiceAry(); + if (getTapServiceByURL(serviceUrl)) return; // don't add if service already exist + const usedNumAry= userServices + .map( (s) => s.label) .filter((title) => title && title.startsWith(baseName)) .map((title) => title.trim().split('-')?.[1]) .map(Number) .filter(Boolean); const maxNum = usedNumAry?.length ? Math.max(...usedNumAry) : 0; - const label = userServices.every((s) => s.label !== baseName) ? baseName : `${baseName} - ${maxNum + 1}`; - - dispatchAddPreference(USER_SERVICE_PREFS, [ - ...userServices, - {label, serviceId: label.replaceAll(/\s/g, ''), value: serviceUrl, userAdded: true}, - ]); + const label= userServices.every( (s) => s.label!==baseName) ? baseName : baseName + ` - ${maxNum + 1}`; + userServices.push({ label, serviceId:label.replaceAll(/\s/g,''), value: serviceUrl, userAdded: true}); + dispatchAddPreference(USER_SERVICE_PREFS, userServices); } export function deleteUserService(serviceUrl) { - const normalizedServiceUrl = normalizeTapUrl(serviceUrl); - const userServices = getUserServiceAry().filter( - (s) => normalizeTapUrl(s.value) !== normalizedServiceUrl - ); + const userServices= getUserServiceAry().filter( (s) => s.value!==serviceUrl); dispatchAddPreference(USER_SERVICE_PREFS, userServices); } @@ -717,28 +699,7 @@ export const getServiceHiPS= (serviceUrl) => getTapServiceByURL(serviceUrl)?.hip -export const normalizeTapUrl = (url='') => url.trim().replace(/\/+$/, '').toLowerCase(); - -export function makeTapServiceOptions(baseServices, additionalServices, userServices=[]) { - const startingTapServices= hasElements(baseServices) ? [...baseServices] : makeTAPDefaultServicesByName(); - const mergedServices= mergeServices(startingTapServices, additionalServices); - const allServices= [...mergedServices, ...userServices]; - const seen = new Set(); - - return allServices - .map(({label, value, userAdded=false}) => ({label:value, value, labelOnly:label, userAdded})) - .filter((op) => { - const key = normalizeTapUrl(op.value); - if (!key || seen.has(key)) return false; - seen.add(key); - return true; - }); -} - -export const getTapServiceOptions = () => { - const {tap} = getAppOptions(); - return makeTapServiceOptions(tap?.services, tap?.additional?.services, getUserServiceAry()); -}; - +export const getTapServiceOptions= () => + getTapServices().map(({label,value,userAdded=false})=>({label:value, value, labelOnly:label, userAdded})); export const getServiceNamesAsKey= () => getTapServiceOptions().map(({label}) => label).join('-');