diff --git a/frontend/src/Editor/CodeEditor/utils.js b/frontend/src/Editor/CodeEditor/utils.js index 22dc1eef40..b068ddb487 100644 --- a/frontend/src/Editor/CodeEditor/utils.js +++ b/frontend/src/Editor/CodeEditor/utils.js @@ -200,7 +200,6 @@ const resolveMultiDynamicReferences = (code, lookupTable) => { resolvedValue = resolvedValue.replace(variable, res); } else { - const currentState = useCurrentStateStore.getState(); const [resolvedCode] = resolveCode(variableToResolve, {}, true, [], true); resolvedValue = resolvedCode; @@ -241,7 +240,10 @@ export const resolveReferences = (query, validationSchema, customResolvers = {}) if (value.startsWith('#') || value.includes('table-')) { value = JSON.stringify(value); } - const { toResolveReference, jsExpression, jsExpMatch } = inferJSExpAndReferences(value, lookupTable.hints); + const { toResolveReference, jsExpression, jsExpMatch } = + lookupTable.hints || lookupTable.hints.has + ? inferJSExpAndReferences(value, lookupTable.hints) + : { toResolveReference: null, jsExpression: null, jsExpMatch: null }; if (!jsExpMatch && toResolveReference && lookupTable.hints.has(toResolveReference)) { const idToLookUp = lookupTable.hints.get(toResolveReference); @@ -307,7 +309,7 @@ const inferJSExpAndReferences = (code, hintsMap) => { const potentialReference = referenceChain ? referenceChain + '.' + segment : segment; // Check if the potential reference exists in hintsMap - if (hintsMap.has(potentialReference)) { + if (hintsMap.has && hintsMap.has(potentialReference)) { // If it does, update the referenceChain referenceChain = potentialReference; } else { diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 8c2639cc87..5e0511ed59 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -632,8 +632,18 @@ const EditorComponent = (props) => { props.switchDarkMode(newMode); }; - const handleEvent = React.useCallback((eventName, event, options) => { - return onEvent(getEditorRef(), eventName, event, options, 'edit'); + const handleEvent = React.useCallback((eventName, events, options) => { + const latestEvents = useAppDataStore.getState().events; + const filteredEvents = latestEvents.filter((event) => { + const foundEvent = events.find((e) => e.id === event.id); + return foundEvent && foundEvent.name === eventName; + }); + + try { + return onEvent(getEditorRef(), eventName, filteredEvents, options, 'edit'); + } catch (error) { + console.error(error); + } // eslint-disable-next-line react-hooks/exhaustive-deps }, []); @@ -765,117 +775,9 @@ const EditorComponent = (props) => { await fetchDataSources(data.editing_version?.id), await fetchDataQueries(data.editing_version?.id, true, true), ]) - .then(() => { - useCurrentStateStore.getState().actions.setEditorReady(true); - - const currentPageId = useEditorStore.getState().currentPageId; - const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components; - - const referenceManager = useResolveStore.getState().referenceMapper; - - const newComponents = Object.keys(currentComponents).map((componentId) => { - const component = currentComponents[componentId]; - - if (!referenceManager.get(componentId)) { - return { - id: componentId, - name: component.component.name, - }; - } - }); - - useResolveStore.getState().actions.addEntitiesToMap(newComponents); - }) - .then(() => { - const currentPageId = useEditorStore.getState().currentPageId; - const currentComponents = useEditorStore.getState().appDefinition?.pages?.[currentPageId]?.components; - let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries)); - let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events)); - - const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter( - (entity) => entity && isValidUUID(entity) - ); - - const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter( - (entity) => entity && isValidUUID(entity) - ); - - const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter( - (entity) => entity && isValidUUID(entity) - ); - - const manager = useResolveStore.getState().referenceMapper; - - if ( - Array.isArray(entityReferencesInComponentDefinitions) && - entityReferencesInComponentDefinitions?.length > 0 - ) { - let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents)); - - entityReferencesInComponentDefinitions.forEach((entity) => { - const entityrefExists = manager.has(entity); - - if (entityrefExists) { - const value = manager.get(entity); - newComponentDefinition = dfs(newComponentDefinition, entity, value); - } - }); - - const newAppDefinition = produce(appJson, (draft) => { - draft.pages[homePageId].components = newComponentDefinition; - }); - - updateEditorState({ - isUpdatingEditorStateInProcess: false, - appDefinition: newAppDefinition, - }); - } - - if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) { - let newQueryOptions = {}; - dataQueries?.forEach((query) => { - newQueryOptions[query.id] = query.options; - ``; - }); - - entityReferencesInQueryOptions.forEach((entity) => { - const entityrefExists = manager.has(entity); - - if (entityrefExists) { - const value = manager.get(entity); - newQueryOptions = dfs(newQueryOptions, entity, value); - } - }); - - dataQueries = dataQueries.map((query) => { - const queryId = query.id; - const dqOptions = newQueryOptions[queryId]; - - return { - ...query, - options: dqOptions, - }; - }); - - useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate'); - } - - if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) { - let newEvents = JSON.parse(JSON.stringify(allEvents)); - - entityReferencesInEvents.forEach((entity) => { - const entityrefExists = manager.has(entity); - - if (entityrefExists) { - const value = manager.get(entity); - newEvents = dfs(newEvents, entity, value); - } - }); - - updateState({ - events: newEvents, - }); - } + .then(async () => { + await onEditorLoad(appJson, homePageId); + updateEntityReferences(appJson, homePageId); }) .finally(async () => { const currentPageEvents = data.events.filter( @@ -1478,6 +1380,117 @@ const EditorComponent = (props) => { } }; + const onEditorLoad = (appJson, pageId, isPageSwitch = false) => { + useCurrentStateStore.getState().actions.setEditorReady(true); + const currentComponents = appJson?.pages?.[pageId]?.components; + + const referenceManager = useResolveStore.getState().referenceMapper; + + const newComponents = Object.keys(currentComponents).map((componentId) => { + const component = currentComponents[componentId]; + + if (isPageSwitch || !referenceManager.get(componentId)) { + return { + id: componentId, + name: component.component.name, + }; + } + }); + + useResolveStore.getState().actions.addEntitiesToMap(newComponents); + }; + + const updateEntityReferences = (appJson, pageId) => { + const currentComponents = appJson?.pages?.[pageId]?.components; + + let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries)); + let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events)); + + const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); + + const entityReferencesInQueryOptions = findAllEntityReferences(dataQueries, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); + + const entityReferencesInEvents = findAllEntityReferences(allEvents, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); + + const manager = useResolveStore.getState().referenceMapper; + + if (Array.isArray(entityReferencesInComponentDefinitions) && entityReferencesInComponentDefinitions?.length > 0) { + let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents)); + + entityReferencesInComponentDefinitions.forEach((entity) => { + const entityrefExists = manager.has(entity); + + if (entityrefExists) { + const value = manager.get(entity); + newComponentDefinition = dfs(newComponentDefinition, entity, value); + } + }); + + const newAppDefinition = produce(appJson, (draft) => { + draft.pages[pageId].components = newComponentDefinition; + }); + + handleLowPriorityWork(() => { + updateEditorState({ + isUpdatingEditorStateInProcess: false, + appDefinition: newAppDefinition, + }); + }); + } + + if (Array.isArray(entityReferencesInQueryOptions) && entityReferencesInQueryOptions?.length > 0) { + let newQueryOptions = {}; + dataQueries?.forEach((query) => { + newQueryOptions[query.id] = query.options; + ``; + }); + + entityReferencesInQueryOptions.forEach((entity) => { + const entityrefExists = manager.has(entity); + + if (entityrefExists) { + const value = manager.get(entity); + newQueryOptions = dfs(newQueryOptions, entity, value); + } + }); + + dataQueries = dataQueries.map((query) => { + const queryId = query.id; + const dqOptions = newQueryOptions[queryId]; + + return { + ...query, + options: dqOptions, + }; + }); + + useDataQueriesStore.getState().actions.setDataQueries(dataQueries, 'mappingUpdate'); + } + + if (Array.isArray(entityReferencesInEvents) && entityReferencesInEvents?.length > 0) { + let newEvents = JSON.parse(JSON.stringify(allEvents)); + + entityReferencesInEvents.forEach((entity) => { + const entityrefExists = manager.has(entity); + + if (entityrefExists) { + const value = manager.get(entity); + newEvents = dfs(newEvents, entity, value); + } + }); + + updateState({ + events: newEvents, + }); + } + }; + const removeComponents = () => { const selectedComponents = useEditorStore.getState()?.selectedComponents; if (!isVersionReleased && selectedComponents?.length > 1) { @@ -1592,7 +1605,9 @@ const EditorComponent = (props) => { }); }; - const switchPage = (pageId, queryParams = []) => { + const switchPage = async (pageId, queryParams = []) => { + useCurrentStateStore.getState().actions.setEditorReady(false); + useResolveStore.getState().actions.resetStore(); // This are fetched from store to handle runQueriesOnAppLoad const currentPageId = useEditorStore.getState().currentPageId; const appDefinition = useEditorStore.getState().appDefinition; @@ -1619,7 +1634,13 @@ const EditorComponent = (props) => { ...currentState.globals, urlparams: JSON.parse(JSON.stringify(queryString.parse(queryParamsString))), }; + useCurrentStateStore.getState().actions.setCurrentState({ globals, page }); + useResolveStore.getState().actions.pageSwitched(true); + + await onEditorLoad(appDefinition, pageId, true); + updateEntityReferences(appDefinition, pageId); + useResolveStore.getState().actions.updateJSHints(); setCurrentPageId(pageId); diff --git a/frontend/src/_helpers/editorHelpers.js b/frontend/src/_helpers/editorHelpers.js index 2971cd3bc0..00649a0750 100644 --- a/frontend/src/_helpers/editorHelpers.js +++ b/frontend/src/_helpers/editorHelpers.js @@ -174,7 +174,11 @@ export function findComponentsWithReferences(components, changedCurrentState) { return componentIdsWithReferences; } -export function handleLowPriorityWork(callback, timeout = null) { +export function handleLowPriorityWork(callback, timeout = null, immediate = false) { + if (immediate) { + callback(); + } + const options = timeout ? { timeout } : {}; window.requestIdleCallback(callback, options); } diff --git a/frontend/src/_hooks/useRenderCount.js b/frontend/src/_hooks/useRenderCount.js index 3ad64f6b9e..c07b5734e3 100644 --- a/frontend/src/_hooks/useRenderCount.js +++ b/frontend/src/_hooks/useRenderCount.js @@ -3,15 +3,8 @@ import { useRef, useEffect } from 'react'; function useRenderCount(componentName) { const renderCountRef = useRef(0); - useEffect(() => { - return () => { - console.log(`--Component ${componentName} rendered unmounting ${renderCountRef.current} times.`); - }; - }, []); - renderCountRef.current++; - console.log(`CountingRender- Component ${componentName} rendered ${renderCountRef.current} times.`); return renderCountRef.current; } diff --git a/frontend/src/_stores/appDataStore.js b/frontend/src/_stores/appDataStore.js index 9cf9365fe5..182fbee1b2 100644 --- a/frontend/src/_stores/appDataStore.js +++ b/frontend/src/_stores/appDataStore.js @@ -46,7 +46,6 @@ export const useAppDataStore = create( updateEditingVersion: (version) => set(() => ({ editingVersion: version })), updateApps: (apps) => set(() => ({ apps: apps })), updateState: (state) => set((prev) => ({ ...prev, ...state })), - updateAppDefinitionDiff: (appDefinitionDiff) => set(() => ({ appDefinitionDiff: appDefinitionDiff })), updateAppVersion: (appId, versionId, pageId, appDefinitionDiff, isUserSwitchedVersion = false) => { return new Promise((resolve, reject) => { diff --git a/frontend/src/_stores/currentStateStore.js b/frontend/src/_stores/currentStateStore.js index bb7af52df0..d0c82505e3 100644 --- a/frontend/src/_stores/currentStateStore.js +++ b/frontend/src/_stores/currentStateStore.js @@ -1,10 +1,7 @@ import { shallow } from 'zustand/shallow'; import { create, zustandDevTools } from './utils'; -import _, { debounce, merge, omit } from 'lodash'; +import _, { omit } from 'lodash'; import { useResolveStore } from './resolverStore'; -// eslint-disable-next-line import/no-unresolved -import { diff } from 'deep-object-diff'; -import { useEditorStore } from './editorStore'; import { handleLowPriorityWork } from '@/_helpers/editorHelpers'; const initialState = { @@ -88,19 +85,26 @@ useCurrentStateStore.subscribe((state) => { const isStoreIntialized = useResolveStore.getState().storeReady; if (!isStoreIntialized) { - handleLowPriorityWork(() => { - useResolveStore.getState().actions.updateAppSuggestions({ - queries: state.queries, - components: state.components, - globals: state.globals, - page: state.page, - variables: state.variables, - client: state.client, - server: state.server, - constants: state.constants, - }); - }); - console.log('Resolver store initialized with current state.'); + const isPageSwitched = useResolveStore.getState().isPageSwitched; + + handleLowPriorityWork( + () => { + useResolveStore.getState().actions.updateAppSuggestions({ + queries: state.queries, + components: state.components, + globals: state.globals, + page: state.page, + variables: state.variables, + client: state.client, + server: state.server, + constants: state.constants, + }); + useResolveStore.getState().actions.pageSwitched(false); + }, + null, + isPageSwitched + ); + return useResolveStore.getState().actions.updateStoreState({ storeReady: true }); } }, shallow); diff --git a/frontend/src/_stores/resolverStore.js b/frontend/src/_stores/resolverStore.js index 4fc70a1d5b..8483d4b2ce 100644 --- a/frontend/src/_stores/resolverStore.js +++ b/frontend/src/_stores/resolverStore.js @@ -60,6 +60,7 @@ const initialState = { }, lastUpdatedRefs: [], referenceMapper: new ReferencesBiMap(), + isPageSwitched: false, }; export const useResolveStore = create( @@ -69,6 +70,10 @@ export const useResolveStore = create( updateStoreState: (state) => { set(() => ({ ...state, storeReady: true })); }, + resetStore: () => { + set(() => initialState); + }, + pageSwitched: (bool) => set(() => ({ isPageSwitched: bool })), updateAppSuggestions: (refState) => { const { suggestionList, hintsMap, resolvedRefs } = createReferencesLookup(refState, false, true);