From 7aa0571a3c638b936400fe906af7acf4ca8b092d Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 8 Apr 2024 16:58:31 +0530 Subject: [PATCH 01/10] Fixed bug on exposing listView & Form data --- frontend/src/Editor/BoxUI.jsx | 23 +++++++-- frontend/src/Editor/Components/Form/Form.jsx | 12 ++++- frontend/src/Editor/Container.jsx | 50 ++++++++++--------- .../Editor/ControlledComponentToRender.jsx | 3 ++ frontend/src/Editor/DraggableBox.jsx | 8 +++ frontend/src/Editor/SubContainer.jsx | 1 + 6 files changed, 68 insertions(+), 29 deletions(-) diff --git a/frontend/src/Editor/BoxUI.jsx b/frontend/src/Editor/BoxUI.jsx index b8b8798e6f..be88f6504b 100644 --- a/frontend/src/Editor/BoxUI.jsx +++ b/frontend/src/Editor/BoxUI.jsx @@ -37,6 +37,10 @@ const BoxUI = (props) => { currentLayout, readOnly, currentPageId, + onOptionChanged, + onOptionsChanged, + isFromSubContainer, + childComponents, } = props; const darkMode = localStorage.getItem('darkMode') === 'true'; @@ -127,13 +131,25 @@ const BoxUI = (props) => { + isFromSubContainer + ? onOptionChanged(component, variable, value, id) + : onComponentOptionChanged(component, variable, value, id) + } + setExposedVariables={(variableSet) => { + if (isFromSubContainer) { + onOptionsChanged(component, Object.entries(variableSet), id); + } else { + onComponentOptionsChanged(component, Object.entries(variableSet), id); + } + }} height={height} component={component} containerProps={getContainerProps(id)} @@ -148,8 +164,6 @@ const BoxUI = (props) => { ? { boxShadow: generalStyles?.boxShadow } : {}), }} - setExposedVariable={(variable, value) => onComponentOptionChanged(component, variable, value, id)} - setExposedVariables={(variableSet) => onComponentOptionsChanged(component, Object.entries(variableSet), id)} fireEvent={fireEvent} validate={validate} parentId={parentId} @@ -166,6 +180,7 @@ const BoxUI = (props) => { currentState={currentState} currentPageId={currentPageId} getContainerProps={component.component === 'Form' ? getContainerProps : null} + childComponents={childComponents} /> diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 0a9e4615e6..39fa3581f7 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -31,11 +31,14 @@ export const Form = function Form(props) { mode, getContainerProps, containerProps, + childComponents, } = props; const { events: allAppEvents } = useAppInfo(); - const { childComponents } = containerProps; + console.log('childComponents--- ', childComponents); + + // const { childComponents } = containerProps; const formEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id); const { visibility, disabledState, borderRadius, borderColor, boxShadow } = styles; @@ -122,13 +125,16 @@ export const Form = function Form(props) { let formattedChildData = {}; let childValidation = true; - if (childComponents === null) { + console.log('here--- Inside useEffect--- ', containerProps.childComponents); + + if (!childComponents) { const exposedVariables = { data: formattedChildData, isValid: childValidation, ...(!advanced && { children: formattedChildData }), }; + console.log('here--- exposedVariables--- ', exposedVariables); setExposedVariables(exposedVariables); return setValidation(childValidation); } @@ -154,6 +160,7 @@ export const Form = function Form(props) { data: removeFunctionObjects(formattedChildData), isValid: childValidation, }; + console.log('here--- useEffect--- ', childComponents, exposedVariables); setValidation(childValidation); setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -211,6 +218,7 @@ export const Form = function Form(props) { } const onOptionChange = ({ component, optionName, value, componentId }) => { + console.log('here--- onOptionChange'); const optionData = { ...(childDataRef.current[componentId] ?? {}), name: component.name, diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index e6f21b601c..f93004b0f7 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -420,7 +420,7 @@ export const Container = ({ const newChildComponent = addNewWidgetToTheEditor( componentData, {}, - boxes, + { ...boxes, ...childrenBoxes }, {}, item.currentLayout, snapToGrid, @@ -754,28 +754,31 @@ export const Container = ({ return componentWithChildren; }, [components]); - const getContainerProps = React.useCallback((componentId) => { - return { - mode, - snapToGrid, - onComponentClick, - onEvent, - appDefinition, - appDefinitionChanged, - currentState, - appLoading, - zoomLevel, - setSelectedComponent, - removeComponent, - currentLayout, - selectedComponents, - darkMode, - currentPageId, - childComponents: childComponents[componentId], - parentGridWidth: gridWidth, - draggedSubContainer, - }; - }, []); + const getContainerProps = React.useCallback( + (componentId) => { + return { + mode, + snapToGrid, + onComponentClick, + onEvent, + appDefinition, + appDefinitionChanged, + currentState, + appLoading, + zoomLevel, + setSelectedComponent, + removeComponent, + currentLayout, + selectedComponents, + darkMode, + currentPageId, + childComponents: childComponents[componentId], + parentGridWidth: gridWidth, + draggedSubContainer, + }; + }, + [childComponents, selectedComponents, draggedSubContainer] + ); return ( ); diff --git a/frontend/src/Editor/ControlledComponentToRender.jsx b/frontend/src/Editor/ControlledComponentToRender.jsx index a13b27252d..e36f0cdb81 100644 --- a/frontend/src/Editor/ControlledComponentToRender.jsx +++ b/frontend/src/Editor/ControlledComponentToRender.jsx @@ -23,6 +23,9 @@ export const shouldUpdate = (prevProps, nextProps) => { } } + // Added to render the defaukt child components + if (prevProps?.childComponents === null && nextProps?.childComponents) return false; + return ( deepEqualityCheckusingLoDash(prevProps?.id, nextProps?.id) && deepEqualityCheckusingLoDash(prevProps?.component?.definition, nextProps?.component?.definition) && diff --git a/frontend/src/Editor/DraggableBox.jsx b/frontend/src/Editor/DraggableBox.jsx index d51387fa8c..19313f3d4e 100644 --- a/frontend/src/Editor/DraggableBox.jsx +++ b/frontend/src/Editor/DraggableBox.jsx @@ -53,6 +53,10 @@ const DraggableBox = React.memo( parentId, getContainerProps, currentPageId, + onComponentOptionChanged = null, + onComponentOptionsChanged = null, + isFromSubContainer = false, + childComponents = null, }) => { const isResizing = useGridStore((state) => state.resizingComponentId === id); const [canDrag, setCanDrag] = useState(true); @@ -222,6 +226,10 @@ const DraggableBox = React.memo( parentId={parentId} getContainerProps={getContainerProps} currentPageId={currentPageId} + onOptionChanged={onComponentOptionChanged} + onOptionsChanged={onComponentOptionsChanged} + isFromSubContainer={isFromSubContainer} + childComponents={childComponents} /> diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx index c664c87fea..e1ba88c1db 100644 --- a/frontend/src/Editor/SubContainer.jsx +++ b/frontend/src/Editor/SubContainer.jsx @@ -515,6 +515,7 @@ export const SubContainer = ({ isMultipleComponentsSelected={selectedComponents?.length > 1 ? true : false} exposedVariables={exposedVariables ?? {}} getContainerProps={getContainerProps} + isFromSubContainer={true} /> ); From 518b73b3565c559a3edb97901a84f51cbf4a03b3 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 8 Apr 2024 17:18:55 +0530 Subject: [PATCH 02/10] fixed custom schema bug on Form component --- frontend/src/Editor/Components/Form/Form.jsx | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 39fa3581f7..d29a7e421c 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -7,7 +7,12 @@ import _, { omit } from 'lodash'; import { Box } from '@/Editor/Box'; import { generateUIComponents } from './FormUtils'; import { useMounted } from '@/_hooks/use-mount'; -import { onComponentClick, onComponentOptionChanged, removeFunctionObjects } from '@/_helpers/appUtils'; +import { + onComponentClick, + onComponentOptionChanged, + onComponentOptionsChanged, + removeFunctionObjects, +} from '@/_helpers/appUtils'; import { useAppInfo } from '@/_stores/appDataStore'; export const Form = function Form(props) { const { @@ -125,8 +130,6 @@ export const Form = function Form(props) { let formattedChildData = {}; let childValidation = true; - console.log('here--- Inside useEffect--- ', containerProps.childComponents); - if (!childComponents) { const exposedVariables = { data: formattedChildData, @@ -134,7 +137,6 @@ export const Form = function Form(props) { ...(!advanced && { children: formattedChildData }), }; - console.log('here--- exposedVariables--- ', exposedVariables); setExposedVariables(exposedVariables); return setValidation(childValidation); } @@ -160,7 +162,6 @@ export const Form = function Form(props) { data: removeFunctionObjects(formattedChildData), isValid: childValidation, }; - console.log('here--- useEffect--- ', childComponents, exposedVariables); setValidation(childValidation); setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps @@ -218,7 +219,6 @@ export const Form = function Form(props) { } const onOptionChange = ({ component, optionName, value, componentId }) => { - console.log('here--- onOptionChange'); const optionData = { ...(childDataRef.current[componentId] ?? {}), name: component.name, @@ -290,7 +290,7 @@ export const Form = function Form(props) { ); From 82fd53d30a9c2822469e013085ebe40578757665 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Mon, 8 Apr 2024 17:20:23 +0530 Subject: [PATCH 03/10] cleanup --- frontend/src/Editor/Components/Form/Form.jsx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index d29a7e421c..09a8daf64c 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -41,10 +41,6 @@ export const Form = function Form(props) { const { events: allAppEvents } = useAppInfo(); - console.log('childComponents--- ', childComponents); - - // const { childComponents } = containerProps; - const formEvents = allAppEvents.filter((event) => event.target === 'component' && event.sourceId === id); const { visibility, disabledState, borderRadius, borderColor, boxShadow } = styles; const { buttonToSubmit, loadingState, advanced, JSONSchema } = properties; From 8a64175c0ab5e3a0b775e94234589b37828c1fd8 Mon Sep 17 00:00:00 2001 From: arpitnath Date: Mon, 8 Apr 2024 18:15:18 +0530 Subject: [PATCH 04/10] fixes: renaming entities sets the references component value to default value current state was not updating to latest state removed/deleted render count hook [dev-testing purpose] --- frontend/src/Editor/Components/Button.jsx | 3 --- frontend/src/Editor/Components/Text.jsx | 13 +------------ frontend/src/Editor/Editor.jsx | 7 ++----- frontend/src/_helpers/utils.js | 17 ++--------------- frontend/src/_hooks/useRenderCount.js | 11 ----------- 5 files changed, 5 insertions(+), 46 deletions(-) delete mode 100644 frontend/src/_hooks/useRenderCount.js diff --git a/frontend/src/Editor/Components/Button.jsx b/frontend/src/Editor/Components/Button.jsx index 29bbb7ac49..aa49466759 100644 --- a/frontend/src/Editor/Components/Button.jsx +++ b/frontend/src/Editor/Components/Button.jsx @@ -1,11 +1,8 @@ import React, { useEffect, useState } from 'react'; import cx from 'classnames'; const tinycolor = require('tinycolor2'); -import useRenderCount from '@/_hooks/useRenderCount'; export const Button = function Button(props) { - useRenderCount(`Button Main component ${props.id}`); - const { height, properties, styles, fireEvent, id, dataCy, setExposedVariable, setExposedVariables } = props; const { backgroundColor, textColor, borderRadius, loaderColor, disabledState, borderColor, boxShadow } = styles; diff --git a/frontend/src/Editor/Components/Text.jsx b/frontend/src/Editor/Components/Text.jsx index ef9a91808b..6cbd11e256 100644 --- a/frontend/src/Editor/Components/Text.jsx +++ b/frontend/src/Editor/Components/Text.jsx @@ -3,7 +3,6 @@ import DOMPurify from 'dompurify'; import Markdown from 'react-markdown'; import './text.scss'; import Loader from '@/ToolJetUI/Loader/Loader'; -import useRenderCount from '@/_hooks/useRenderCount'; const VERTICAL_ALIGNMENT_VS_CSS_VALUE = { top: 'flex-start', @@ -11,17 +10,7 @@ const VERTICAL_ALIGNMENT_VS_CSS_VALUE = { bottom: 'flex-end', }; -export const Text = function Text({ - height, - properties, - fireEvent, - styles, - darkMode, - setExposedVariable, - dataCy, - ...props -}) { - useRenderCount(`TextComponent Main component ${props.id}`); +export const Text = function Text({ height, properties, fireEvent, styles, darkMode, setExposedVariable, dataCy }) { let { textSize, textColor, diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 1d85b00343..505d284934 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -213,8 +213,6 @@ const EditorComponent = (props) => { const prevAppDefinition = useRef(appDefinition); - const onAppLoadAndPageLoadEventsAreTriggered = useRef(false); - useLayoutEffect(() => { resetAllStores(); }, []); @@ -282,9 +280,9 @@ const EditorComponent = (props) => { } if (mounted && didAppDefinitionChanged && currentPageId) { - // const components = appDefinition?.pages[currentPageId]?.components || {}; + const components = appDefinition?.pages[currentPageId]?.components || {}; - // computeComponentState(components); + computeComponentState(components); if (appDiffOptions?.skipAutoSave === true || appDiffOptions?.entityReferenceUpdated === true) return; @@ -789,7 +787,6 @@ const EditorComponent = (props) => { handleLowPriorityWork(async () => { await runQueries(useDataQueriesStore.getState().dataQueries, editorRef, true); await handleEvent('onPageLoad', currentPageEvents, {}, true); - await handleLowPriorityWork(() => (onAppLoadAndPageLoadEventsAreTriggered.current = true)); }); }); }; diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index 9ef51e0e57..70aa1bdae4 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -3,17 +3,15 @@ import moment from 'moment'; import _, { isEmpty } from 'lodash'; import axios from 'axios'; import JSON5 from 'json5'; -import { previewQuery, executeAction } from '@/_helpers/appUtils'; +import { executeAction } from '@/_helpers/appUtils'; import { toast } from 'react-hot-toast'; import { authenticationService } from '@/_services/authentication.service'; import { useDataQueriesStore } from '@/_stores/dataQueriesStore'; -import { getCurrentState, useCurrentState } from '@/_stores/currentStateStore'; +import { getCurrentState } from '@/_stores/currentStateStore'; import { getWorkspaceIdOrSlugFromURL, getSubpath, returnWorkspaceIdIfNeed } from './routes'; import { getCookie, eraseCookie } from '@/_helpers/cookie'; import { staticDataSources } from '@/Editor/QueryManager/constants'; -import { resolveReferences as newResolver } from '@/Editor/CodeEditor/utils'; -import { useResolveStore } from '@/_stores/resolverStore'; export function findProp(obj, prop, defval) { if (typeof defval === 'undefined') defval = null; @@ -329,18 +327,7 @@ export const serializeNestedObjectToQueryParams = function (obj, prefix) { export function resolveWidgetFieldValue(prop, _default = [], customResolveObjects = {}) { const widgetFieldValue = prop; - const isStoreAndEditorReady = useResolveStore.getState().updateStoreState && useCurrentState.getState().isEditorReady; - try { - if (isStoreAndEditorReady) { - const [_, _error, resolveValue] = newResolver(widgetFieldValue?.value); - - if (_error) { - return _default; - } - - return resolveValue; - } const state = getCurrentState(); return resolveReferences(widgetFieldValue, state, _default, customResolveObjects); } catch (err) { diff --git a/frontend/src/_hooks/useRenderCount.js b/frontend/src/_hooks/useRenderCount.js deleted file mode 100644 index c07b5734e3..0000000000 --- a/frontend/src/_hooks/useRenderCount.js +++ /dev/null @@ -1,11 +0,0 @@ -import { useRef, useEffect } from 'react'; - -function useRenderCount(componentName) { - const renderCountRef = useRef(0); - - renderCountRef.current++; - - return renderCountRef.current; -} - -export default useRenderCount; From 28568714dde6a3e24953c7be4e407120d7975735 Mon Sep 17 00:00:00 2001 From: arpitnath Date: Mon, 8 Apr 2024 18:25:37 +0530 Subject: [PATCH 05/10] handle entity references inside global settings. added referece mapping for future improvements also. --- frontend/src/Editor/Editor.jsx | 31 ++++++++++++++++++++++++++++--- 1 file changed, 28 insertions(+), 3 deletions(-) diff --git a/frontend/src/Editor/Editor.jsx b/frontend/src/Editor/Editor.jsx index 505d284934..ad6ec31dfa 100644 --- a/frontend/src/Editor/Editor.jsx +++ b/frontend/src/Editor/Editor.jsx @@ -19,8 +19,6 @@ import { componentTypes } from './WidgetManager/components'; import { Inspector } from './Inspector/Inspector'; import QueryPanel from './QueryPanel/QueryPanel'; import { - onComponentOptionChanged, - onComponentOptionsChanged, onEvent, onQueryConfirmOrCancel, runQuery, @@ -1402,10 +1400,15 @@ const EditorComponent = (props) => { const updateEntityReferences = (appJson, pageId) => { const currentComponents = appJson?.pages?.[pageId]?.components; + const globalSettings = appJson['globalSettings']; let dataQueries = JSON.parse(JSON.stringify(useDataQueriesStore.getState().dataQueries)); let allEvents = JSON.parse(JSON.stringify(useAppDataStore.getState().events)); + const entittyReferencesInGlobalSettings = findAllEntityReferences(globalSettings, [])?.filter( + (entity) => entity && isValidUUID(entity) + ); + const entityReferencesInComponentDefinitions = findAllEntityReferences(currentComponents, [])?.filter( (entity) => entity && isValidUUID(entity) ); @@ -1420,6 +1423,27 @@ const EditorComponent = (props) => { const manager = useResolveStore.getState().referenceMapper; + if (Array.isArray(entittyReferencesInGlobalSettings) && entittyReferencesInGlobalSettings?.length > 0) { + let newGlobalSettings = JSON.parse(JSON.stringify(globalSettings)); + entittyReferencesInGlobalSettings.forEach((entity) => { + const entityrefExists = manager.has(entity); + + if (entityrefExists) { + const value = manager.get(entity); + newGlobalSettings = dfs(newGlobalSettings, entity, value); + } + }); + + const newAppDefinition = produce(appJson, (draft) => { + draft.globalSettings = newGlobalSettings; + }); + + updateEditorState({ + isUpdatingEditorStateInProcess: false, + appDefinition: newAppDefinition, + }); + } + if (Array.isArray(entityReferencesInComponentDefinitions) && entityReferencesInComponentDefinitions?.length > 0) { let newComponentDefinition = JSON.parse(JSON.stringify(currentComponents)); @@ -1432,7 +1456,8 @@ const EditorComponent = (props) => { } }); - const newAppDefinition = produce(appJson, (draft) => { + const appDefinition = useEditorStore.getState().appDefinition; + const newAppDefinition = produce(appDefinition, (draft) => { draft.pages[pageId].components = newComponentDefinition; }); From b7444fb3ed060faf80596c4f873e4e5ce39127a4 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Tue, 9 Apr 2024 09:17:52 +0530 Subject: [PATCH 06/10] Fixed showing PDF component --- frontend/src/Editor/Container.jsx | 3 +- .../Editor/ControlledComponentToRender.jsx | 2 + frontend/src/Editor/SubContainer.jsx | 8 ++- frontend/src/_helpers/appUtils.js | 65 +++++++++++++++++++ frontend/src/_helpers/editorHelpers.js | 6 ++ frontend/src/_stores/utils.js | 64 ------------------ 6 files changed, 80 insertions(+), 68 deletions(-) diff --git a/frontend/src/Editor/Container.jsx b/frontend/src/Editor/Container.jsx index f93004b0f7..d2d742209c 100644 --- a/frontend/src/Editor/Container.jsx +++ b/frontend/src/Editor/Container.jsx @@ -12,7 +12,7 @@ import { commentsService } from '@/_services'; import config from 'config'; import Spinner from '@/_ui/Spinner'; import { useHotkeys } from 'react-hotkeys-hook'; -import { addComponents, addNewWidgetToTheEditor } from '@/_helpers/appUtils'; +import { addComponents, addNewWidgetToTheEditor, isPDFSupported } from '@/_helpers/appUtils'; import { useCurrentState } from '@/_stores/currentStateStore'; import { useAppVersionStore } from '@/_stores/appVersionStore'; import { useEditorStore } from '@/_stores/editorStore'; @@ -23,7 +23,6 @@ import _, { cloneDeep, isEmpty } from 'lodash'; import { diff } from 'deep-object-diff'; import DragContainer from './DragContainer'; import { compact, correctBounds } from './gridUtils'; -import { isPDFSupported } from '@/_stores/utils'; import toast from 'react-hot-toast'; import { isOnlyLayoutUpdate, handleLowPriorityWork } from '@/_helpers/editorHelpers'; import GhostWidget from './GhostWidget'; diff --git a/frontend/src/Editor/ControlledComponentToRender.jsx b/frontend/src/Editor/ControlledComponentToRender.jsx index e36f0cdb81..c491a05fee 100644 --- a/frontend/src/Editor/ControlledComponentToRender.jsx +++ b/frontend/src/Editor/ControlledComponentToRender.jsx @@ -38,6 +38,8 @@ export const shouldUpdate = (prevProps, nextProps) => { const ComponentWrapper = React.memo(({ componentName, ...props }) => { const ComponentToRender = getComponentToRender(componentName); + if (ComponentToRender === null) return; + return ; }, shouldUpdate); diff --git a/frontend/src/Editor/SubContainer.jsx b/frontend/src/Editor/SubContainer.jsx index e1ba88c1db..eb999fa412 100644 --- a/frontend/src/Editor/SubContainer.jsx +++ b/frontend/src/Editor/SubContainer.jsx @@ -6,7 +6,12 @@ import { DraggableBox } from './DraggableBox'; import update from 'immutability-helper'; import _, { isEmpty } from 'lodash'; import { componentTypes } from './WidgetManager/components'; -import { addNewWidgetToTheEditor, onComponentOptionChanged, onComponentOptionsChanged } from '@/_helpers/appUtils'; +import { + addNewWidgetToTheEditor, + onComponentOptionChanged, + onComponentOptionsChanged, + isPDFSupported, +} from '@/_helpers/appUtils'; import { resolveWidgetFieldValue } from '@/_helpers/utils'; import { toast } from 'react-hot-toast'; import { restrictedWidgetsObj } from '@/Editor/WidgetManager/restrictedWidgetsConfig'; @@ -18,7 +23,6 @@ import { useEditorStore } from '@/_stores/editorStore'; import { diff } from 'deep-object-diff'; // eslint-disable-next-line import/namespace import { useGridStore, useResizingComponentId } from '@/_stores/gridStore'; -import { isPDFSupported } from '@/_stores/utils'; import GhostWidget from './GhostWidget'; export const SubContainer = ({ diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index e1ddd1bc2c..309281afc2 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -2078,3 +2078,68 @@ export const removeFunctionObjects = (obj) => { } return obj; }; + +export function isPDFSupported() { + const browser = getBrowserUserAgent(); + + if (!browser) { + return true; + } + + const isChrome = browser.name === 'Chrome' && browser.major >= 92; + const isEdge = browser.name === 'Edge' && browser.major >= 92; + const isSafari = browser.name === 'Safari' && browser.major >= 15 && browser.minor >= 4; // Handle minor version check for Safari + const isFirefox = browser.name === 'Firefox' && browser.major >= 90; + + console.log('browser--', browser, isChrome || isEdge || isSafari || isFirefox); + + return isChrome || isEdge || isSafari || isFirefox; +} + +function getBrowserUserAgent(userAgent) { + var regexps = { + Chrome: [/Chrome\/(\S+)/], + Firefox: [/Firefox\/(\S+)/], + MSIE: [/MSIE (\S+);/], + Opera: [/Opera\/.*?Version\/(\S+)/ /* Opera 10 */, /Opera\/(\S+)/ /* Opera 9 and older */], + Safari: [/Version\/(\S+).*?Safari\//], + }, + re, + m, + browser, + version; + + if (userAgent === undefined) userAgent = navigator.userAgent; + + for (browser in regexps) + while ((re = regexps[browser].shift())) + if ((m = userAgent.match(re))) { + version = m[1].match(new RegExp('[^.]+(?:.[^.]+){0,1}'))[0]; + const { major, minor } = extractVersion(version); + return { + name: browser, + major, + minor, + }; + } + + return null; +} + +function extractVersion(versionStr) { + // Split the string by "." + const parts = versionStr.split('.'); + + // Check for valid input + if (parts.length === 0 || parts.some((part) => isNaN(part))) { + return { major: null, minor: null }; + } + + // Extract major version + const major = parseInt(parts[0], 10); + + // Handle minor version (default to 0) + const minor = parts.length > 1 ? parseInt(parts[1], 10) : 0; + + return { major, minor }; +} diff --git a/frontend/src/_helpers/editorHelpers.js b/frontend/src/_helpers/editorHelpers.js index 00649a0750..5ffbcb00a4 100644 --- a/frontend/src/_helpers/editorHelpers.js +++ b/frontend/src/_helpers/editorHelpers.js @@ -49,6 +49,7 @@ import { Icon } from '@/Editor/Components/Icon'; import { Link } from '@/Editor/Components/Link'; import { Form } from '@/Editor/Components/Form/Form'; import { BoundedBox } from '@/Editor/Components/BoundedBox/BoundedBox'; +import { isPDFSupported } from '@/_helpers/appUtils'; export function memoizeFunction(func) { const cache = new Map(); @@ -119,8 +120,13 @@ export const AllComponents = { Form, BoundedBox, }; +if (isPDFSupported()) { + AllComponents.PDF = await import('@/Editor/Components/PDF').then((module) => module.PDF); +} export const getComponentToRender = (componentName) => { + const shouldHideWidget = componentName === 'PDF' && !isPDFSupported(); + if (shouldHideWidget) return null; return AllComponents[componentName]; }; diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index 07899b3341..2f2adfd85e 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -333,70 +333,6 @@ function toRemoveExposedvariablesFromComponentDiff(object) { return copy; } -export function isPDFSupported() { - const browser = getBrowserUserAgent(); - - if (!browser) { - return true; - } - - const isChrome = browser.name === 'Chrome' && browser.major >= 92; - const isEdge = browser.name === 'Edge' && browser.major >= 92; - const isSafari = browser.name === 'Safari' && browser.major >= 15 && browser.minor >= 4; // Handle minor version check for Safari - const isFirefox = browser.name === 'Firefox' && browser.major >= 90; - - console.log('browser--', browser, isChrome || isEdge || isSafari || isFirefox); - - return isChrome || isEdge || isSafari || isFirefox; -} - -export function getBrowserUserAgent(userAgent) { - var regexps = { - Chrome: [/Chrome\/(\S+)/], - Firefox: [/Firefox\/(\S+)/], - MSIE: [/MSIE (\S+);/], - Opera: [/Opera\/.*?Version\/(\S+)/ /* Opera 10 */, /Opera\/(\S+)/ /* Opera 9 and older */], - Safari: [/Version\/(\S+).*?Safari\//], - }, - re, - m, - browser, - version; - - if (userAgent === undefined) userAgent = navigator.userAgent; - - for (browser in regexps) - while ((re = regexps[browser].shift())) - if ((m = userAgent.match(re))) { - version = m[1].match(new RegExp('[^.]+(?:.[^.]+){0,1}'))[0]; - const { major, minor } = extractVersion(version); - return { - name: browser, - major, - minor, - }; - } - - return null; -} - -function extractVersion(versionStr) { - // Split the string by "." - const parts = versionStr.split('.'); - - // Check for valid input - if (parts.length === 0 || parts.some((part) => isNaN(part))) { - return { major: null, minor: null }; - } - - // Extract major version - const major = parseInt(parts[0], 10); - - // Handle minor version (default to 0) - const minor = parts.length > 1 ? parseInt(parts[1], 10) : 0; - - return { major, minor }; -} export function createReferencesLookup(refState, forQueryParams = false, initalLoad = false) { if (forQueryParams && _.isEmpty(refState['parameters'])) { return { suggestionList: [] }; From 708c2e3a7d7af99295b09ba928f53a4355dec456 Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Tue, 9 Apr 2024 11:50:11 +0530 Subject: [PATCH 07/10] Fixed bug on resetting the form component --- frontend/src/Editor/Components/Form/Form.jsx | 4 ++-- frontend/src/Editor/ControlledComponentToRender.jsx | 11 ++++++++++- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/frontend/src/Editor/Components/Form/Form.jsx b/frontend/src/Editor/Components/Form/Form.jsx index 09a8daf64c..b7441409a3 100644 --- a/frontend/src/Editor/Components/Form/Form.jsx +++ b/frontend/src/Editor/Components/Form/Form.jsx @@ -80,7 +80,7 @@ export const Form = function Form(props) { }; setExposedVariables(exposedVariables); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isValid]); + }, [isValid, formEvents]); const extractData = (data) => { const result = {}; @@ -183,7 +183,7 @@ export const Form = function Form(props) { document.addEventListener('submitForm', handleFormSubmission); return () => document.removeEventListener('submitForm', handleFormSubmission); // eslint-disable-next-line react-hooks/exhaustive-deps - }, [buttonToSubmit, isValid, advanced, JSON.stringify(uiComponents)]); + }, [buttonToSubmit, isValid, advanced, JSON.stringify(uiComponents), formEvents]); const handleSubmit = (event) => { event.preventDefault(); diff --git a/frontend/src/Editor/ControlledComponentToRender.jsx b/frontend/src/Editor/ControlledComponentToRender.jsx index c491a05fee..b93d772683 100644 --- a/frontend/src/Editor/ControlledComponentToRender.jsx +++ b/frontend/src/Editor/ControlledComponentToRender.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useState, useCallback } from 'react'; import { getComponentToRender } from '@/_helpers/editorHelpers'; import _ from 'lodash'; @@ -36,9 +36,18 @@ export const shouldUpdate = (prevProps, nextProps) => { }; const ComponentWrapper = React.memo(({ componentName, ...props }) => { + const [key, setKey] = useState(Math.random()); + + const resetComponent = useCallback(() => { + setKey(Math.random()); + }, []); + const ComponentToRender = getComponentToRender(componentName); if (ComponentToRender === null) return; + if (componentName === 'Form') { + return ; + } return ; }, shouldUpdate); From afe2cc0dd38465b0f376563d550142eb30c29c8d Mon Sep 17 00:00:00 2001 From: Kavin Venkatachalam Date: Tue, 9 Apr 2024 13:21:03 +0530 Subject: [PATCH 08/10] Fixed issue on multiple component resize --- frontend/src/Editor/DragContainer.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/Editor/DragContainer.jsx b/frontend/src/Editor/DragContainer.jsx index 8355f0a678..7a6d6ba9cd 100644 --- a/frontend/src/Editor/DragContainer.jsx +++ b/frontend/src/Editor/DragContainer.jsx @@ -433,7 +433,7 @@ export default function DragContainer({ // Adding the new updates to the macro task queue to unblock UI // setTimeout(() => { // }); - onResizeStop([newBoxs]); + onResizeStop(newBoxs); } else { events.forEach((ev) => { const currentWidget = boxes.find(({ id }) => { From df92d81c782d273474b2f24e8deb2eae5029ebbf Mon Sep 17 00:00:00 2001 From: Arpit Date: Tue, 9 Apr 2024 13:52:02 +0530 Subject: [PATCH 09/10] Codehinter fixes main 2.0 (#9333) * fixes: returning or using reserved keywords in Query is crashing Need discussion on reserved keywords. * fixes: on set/unset app and page variables correctly updatees the hints and references * fixes: single line hinters do not resolve single line js code * fixes: multiline code editor suggestion should popup as per content --- frontend/src/Editor/CodeEditor/styles.scss | 2 +- frontend/src/Editor/CodeEditor/utils.js | 12 +++++++-- frontend/src/_helpers/appUtils.js | 31 +++++++++++++++++++--- frontend/src/_helpers/utils.js | 16 ++++++++++- 4 files changed, 54 insertions(+), 7 deletions(-) diff --git a/frontend/src/Editor/CodeEditor/styles.scss b/frontend/src/Editor/CodeEditor/styles.scss index f032ee08b0..a5dd955ec0 100644 --- a/frontend/src/Editor/CodeEditor/styles.scss +++ b/frontend/src/Editor/CodeEditor/styles.scss @@ -245,7 +245,7 @@ .cm-tooltip-autocomplete { @extend .cm-base-autocomplete; - top: 0px !important; + top: content-box !important; } } diff --git a/frontend/src/Editor/CodeEditor/utils.js b/frontend/src/Editor/CodeEditor/utils.js index b068ddb487..dc0ea6811e 100644 --- a/frontend/src/Editor/CodeEditor/utils.js +++ b/frontend/src/Editor/CodeEditor/utils.js @@ -202,7 +202,7 @@ const resolveMultiDynamicReferences = (code, lookupTable) => { } else { const [resolvedCode] = resolveCode(variableToResolve, {}, true, [], true); - resolvedValue = resolvedCode; + resolvedValue = resolvedValue.replace(variable, resolvedCode); } }); } @@ -210,6 +210,14 @@ const resolveMultiDynamicReferences = (code, lookupTable) => { return resolvedValue; }; +const queryHasStringOtherThanVariable = (query) => { + if (query.startsWith('{{') && query.endsWith('}}')) { + return false; + } + + return true; +}; + export const resolveReferences = (query, validationSchema, customResolvers = {}) => { if (!query || typeof query !== 'string') return [false, null, null]; let resolvedValue = query; @@ -229,7 +237,7 @@ export const resolveReferences = (query, validationSchema, customResolvers = {}) return [valid, errors, newValue, resolvedValue]; } - const hasMultiDynamicVariables = getDynamicVariables(query)?.length > 1; + const hasMultiDynamicVariables = queryHasStringOtherThanVariable(query) || getDynamicVariables(query)?.length > 1; const { lookupTable } = useResolveStore.getState(); if (hasMultiDynamicVariables) { diff --git a/frontend/src/_helpers/appUtils.js b/frontend/src/_helpers/appUtils.js index 309281afc2..c9fb7d7a1f 100644 --- a/frontend/src/_helpers/appUtils.js +++ b/frontend/src/_helpers/appUtils.js @@ -622,7 +622,11 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables); const customAppVariables = { ...getCurrentState().variables }; delete customAppVariables[key]; - // useResolveStore.getState().actions.removeAppSuggestions(key); + useResolveStore.getState().actions.removeAppSuggestions([`variables.${key}`]); + useResolveStore + .getState() + .actions.updateResolvedRefsOfHints([{ hint: 'variables', newRef: customAppVariables }]); + return useCurrentStateStore.getState().actions.setCurrentState({ variables: customAppVariables, }); @@ -635,9 +639,14 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { ...getCurrentState().page.variables, [key]: value, }; + useResolveStore.getState().actions.addAppSuggestions({ - variables: customPageVariables, + page: { + ...getCurrentState().page, + variables: customPageVariables, + }, }); + return useCurrentStateStore.getState().actions.setCurrentState({ page: { ...getCurrentState().page, @@ -657,7 +666,23 @@ function executeActionWithDebounce(_ref, event, mode, customVariables) { case 'unset-page-variable': { const key = resolveReferences(event.key, getCurrentState(), undefined, customVariables); const customPageVariables = _.omit(getCurrentState().page.variables, key); - // useResolveStore.getState().actions.removeAppSuggestions(key); + + useResolveStore.getState().actions.removeAppSuggestions([`page.variables.${key}`]); + + const pageRef = { + page: { + ...getCurrentState().page, + variables: customPageVariables, + }, + }; + + const toUpdateRefs = [ + { hint: 'page', newRef: pageRef }, + { hint: 'page.variables', newRef: customPageVariables }, + ]; + + useResolveStore.getState().actions.updateResolvedRefsOfHints(toUpdateRefs); + return useCurrentStateStore.getState().actions.setCurrentState({ page: { ...getCurrentState().page, diff --git a/frontend/src/_helpers/utils.js b/frontend/src/_helpers/utils.js index 70aa1bdae4..bd63b34908 100644 --- a/frontend/src/_helpers/utils.js +++ b/frontend/src/_helpers/utils.js @@ -13,6 +13,7 @@ import { getWorkspaceIdOrSlugFromURL, getSubpath, returnWorkspaceIdIfNeed } from import { getCookie, eraseCookie } from '@/_helpers/cookie'; import { staticDataSources } from '@/Editor/QueryManager/constants'; +const reservedKeyword = ['app', 'window']; //Keywords that slows down the app export function findProp(obj, prop, defval) { if (typeof defval === 'undefined') defval = null; prop = prop.split('.'); @@ -158,7 +159,7 @@ export function resolveReferences( forPreviewBox = false ) { if (object === '{{{}}}') return ''; - const reservedKeyword = ['app', 'window']; //Keywords that slows down the app + object = _.clone(object); const objectType = typeof object; let error; @@ -422,6 +423,19 @@ export function validateEmail(email) { // eslint-disable-next-line no-unused-vars export async function executeMultilineJS(_ref, code, queryId, isPreview, mode = '', parameters = {}) { + if ([...reservedKeyword, 'this'].some((keyword) => code.includes(keyword))) { + const message = `Code contains ${reservedKeyword.join(' or ')} or this keywords`; + const description = 'Cannot resolve code with reserved keywords in it. Please remove them and try again.'; + + return { + status: 'failed', + data: { + message, + description, + }, + }; + } + const currentState = getCurrentState(); let result = {}, error = null; From 59d4fe66f4971cd00b6f1e8a3f1010637fff9460 Mon Sep 17 00:00:00 2001 From: arpitnath Date: Tue, 9 Apr 2024 15:20:37 +0530 Subject: [PATCH 10/10] fixes: create hints and references from queries if entries are less than 2000 --- frontend/src/_stores/utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/frontend/src/_stores/utils.js b/frontend/src/_stores/utils.js index 2f2adfd85e..0f13001789 100644 --- a/frontend/src/_stores/utils.js +++ b/frontend/src/_stores/utils.js @@ -402,7 +402,7 @@ export function createReferencesLookup(refState, forQueryParams = false, initalL if (_type === 'Array') { map.set(newPath, { type: _type }); - if (path.startsWith('queries') && key === 'data' && value.length > 2) { + if (path.startsWith('queries') && key === 'data' && value.length > 2000) { // do nothing } else { buildMap(value, newPath);