diff --git a/src/components/App.tsx b/src/components/App.tsx index da0c180..bbfbf86 100755 --- a/src/components/App.tsx +++ b/src/components/App.tsx @@ -1,8 +1,8 @@ -import React, { useEffect, useState, useRef } from "react"; +import React, { useEffect, useState, useRef, useCallback } from "react"; import { clsx } from "clsx"; import { ILocation } from "../types"; import { debounce } from "../grasp-seasons/utils/utils"; -import { kInitialDimensions, kVersion, kPluginName, kDefaultOnAttributes, kSimulationTabDimensions, kDataContextName } from "../constants"; +import { kInitialDimensions, kVersion, kPluginName, kDefaultOnAttributes, kSimulationTabDimensions, kDataContextName, kChildCollectionName } from "../constants"; import { initializePlugin, codapInterface, selectSelf, addDataContextChangeListener, ClientNotification, getCaseByID } from "@concord-consortium/codap-plugin-api"; import { useCodapData } from "../hooks/useCodapData"; import { LocationTab } from "./location-tab"; @@ -11,20 +11,40 @@ import { Header } from "./header"; import "../assets/scss/App.scss"; -const debouncedUpdateRowSelectionInCodap = debounce((latitude: string, longitude: string, day: number) => { - console.log("\n| SIM day is: ", day, " so issue API request to update selected row in CODAP ", - "\n latitude: ", latitude, - "\n longitude: ", longitude, - "\n day: ", day, - "\n Math.floor(day) ", Math.floor(day)); - // Here, you would add the actual API call to update the row selection in CODAP - // For example: - // codapInterface.sendRequest({ - // action: "update", - // resource: `dataContext[${kDataContextName}].selectionList`, - // values: [{ latitude, longitude, dayOfYear: Math.floor(day) }] - // }); +const debouncedUpdateRowSelectionInCodap = debounce(( + latitude: string, + longitude: string, + day: number +) => { + //console.log(`\n| Selecting case in ${kDataContextName} where latitude: ${latitude}, longitude: ${longitude}, day: ${day}`); + // TODO: It seems roundabout to look up the case id and then go back to ask for it + // But I was unable to find an example of how to select a case by its attribute values + // Next thing for this might be to see if the case ids might be mapped somewhere to the dayOfYear + // It could be a more direct way to select the case rather than two api requests + codapInterface.sendRequest({ + action: "get", + resource: `dataContext[${kDataContextName}].collection[${kChildCollectionName}].caseSearch[dayOfYear==${Math.floor(day)}]` + }).then((result: any) => { + console.log("| case search result: ", result); + if (result.success && result.values.length > 0) { + const caseID = result.values[0].id; + return codapInterface.sendRequest({ + action: "create", + resource: `dataContext[${kDataContextName}].selectionList`, + values: [caseID] + }); + } else { + console.log("No matching case found"); + return null; + } + }).then((selectionResult: any) => { + if (selectionResult) { + console.log("Selection result:", selectionResult); + } + }).catch((error: any) => { + console.error("Error in selection process:", error); + }); }, 250); export const App: React.FC = () => { @@ -37,35 +57,35 @@ export const App: React.FC = () => { const [selectedAttrs, setSelectedAttributes] = useState(kDefaultOnAttributes); const [dataContext, setDataContext] = useState(null); + // Store a ref to getUniqueLocationsInCodapData so we can call inside useEffect without triggering unnecessary re-runs + const { getUniqueLocationsInCodapData } = useCodapData(); + const getUniqueLocationsRef = useRef(getUniqueLocationsInCodapData); + const handleDayUpdateInTheSimTab = (day: number) => { debouncedUpdateRowSelectionInCodap(latitude, longitude, day); - // NOTE: not need update dayOfYear state variable. It's useful only for the opposite direction - // it's useful when user select a row in CODAP and we want to update the day in the sim }; - const handleCaseSelectionInCodap = (_latitude: string, _longitude: string, day: number) => { - console.log("| calling handleCaseSelectionInCodap with selected values: ", _latitude, _longitude, day); - console.log("| matching lat long?: \n current location: ", latitude, longitude, "\n selected location: ", _latitude, _longitude); - console.log("| matching day?: \n current dayOfYear in sim: ", dayOfYear, "\n selected day in codap: ", day); - const correctLocation = latitude === _latitude && longitude === _longitude; - const isNewDay = dayOfYear !== day; - if (correctLocation && isNewDay) { - console.log("| we should update the dayOfYear in the sim tab to day, ", day); - //setDayOfYear(day); + const handleCaseSelectionInCodap = ( + selectedLatitude: string, + selectedLongitude: string, + selectedDay: number, + currentLatitude: string, + currentLongitude: string, + currentDayOfYear: number + ) => { + // TODO: this is a little hacky - we cooerce the numbers to strings to compare them + const rowInLocation = `${currentLatitude},${currentLongitude}` === `${selectedLatitude},${selectedLongitude}`; + const newDayChoice = `${currentDayOfYear}` !== `${selectedDay}`; + if (rowInLocation && newDayChoice) { + setDayOfYear(selectedDay); + // TODO: this works, but CODAP is also resetting the case table so we scroll away from selected row + // Perhaps because it is re-rendering the table with the new case selected? } - // if user actually selected the case from the same location, then update the day of the year. - // if (latitude === _latitude && longitude === _longitude) { - // setDayOfYear(day); - // } - } - - - // Store a ref to getUniqueLocationsInCodapData so we can call inside useEffect without triggering unnecessary re-runs - const { getUniqueLocationsInCodapData } = useCodapData(); - const getUniqueLocationsRef = useRef(getUniqueLocationsInCodapData); + }; useEffect(() => { const initialize = async () => { + console.log("| !! Initializing plugin"); try { await initializePlugin({ pluginName: kPluginName, @@ -75,35 +95,45 @@ export const App: React.FC = () => { } catch (e) { console.error("Failed to initialize plugin, error:", e); } - - // TODO: make this more general, and then decipher what to do with the data - const handleDataContextChange = async (listenerRes: ClientNotification) => { - const { resource, values } = listenerRes; - const isResource = resource === `dataContextChangeNotice[${kDataContextName}]`; - if (!isResource || !values.result.success) return; - - // Checking if cases were deleted. If they were, we need to update the locations list. - const casesDeleted = values.operation === "selectCases" && values.result.cases.length === 0 - const caseSelected = values.operation === "selectCases" && values.result.cases.length === 1; - - if ( casesDeleted ) { - const uniqeLocations = await getUniqueLocationsRef.current(); - if (uniqeLocations) setLocations(uniqeLocations); - } else if (caseSelected) { - const parentCaseId = values.result.cases[0].parent; - const selectedDay = values.result.cases[0].values.dayOfYear; - const parentCase = await getCaseByID(kDataContextName, parentCaseId); - const selectedLatitude = parentCase.values.case.values.latitude; - const selectedLongitude = parentCase.values.case.values.longitude; - handleCaseSelectionInCodap(selectedLatitude, selectedLongitude, selectedDay); - } - }; - addDataContextChangeListener(kDataContextName, handleDataContextChange); }; initialize(); }, []); + useEffect(() => { + const handleDataContextChange = async (listenerRes: ClientNotification) => { + console.log("| dataContextChangeNotice: ", listenerRes); + const { resource, values } = listenerRes; + const isResource = resource === `dataContextChangeNotice[${kDataContextName}]`; + if (!isResource || !values.result.success) return; + + const casesDeleted = values.operation === "selectCases" && values.result.cases.length === 0 + const caseSelected = values.operation === "selectCases" && values.result.cases.length === 1; + + if (casesDeleted) { + const uniqeLocations = await getUniqueLocationsRef.current(); + if (uniqeLocations) setLocations(uniqeLocations); + } + else if (caseSelected) { + const parentCaseId = values.result.cases[0].parent; + const selectedDay = values.result.cases[0].values.dayOfYear; + const parentCase = await getCaseByID(kDataContextName, parentCaseId); + const selectedLatitude = parentCase.values.case.values.latitude; + const selectedLongitude = parentCase.values.case.values.longitude; + handleCaseSelectionInCodap( + selectedLatitude, + selectedLongitude, + selectedDay, + latitude, + longitude, + dayOfYear + ); + } + }; + addDataContextChangeListener(kDataContextName, handleDataContextChange); + // TODO: this is kind of working but not sure about how to clean up the listener? + }, [latitude, longitude, dayOfYear]); + const handleTabClick = (tab: "location" | "simulation") => { setActiveTab(tab); // Update dimensions of the plugin window when switching tabs.