UNPKG

@atlaskit/editor-plugin-extension

Version:

editor-plugin-extension plugin for @atlaskit/editor-core

140 lines (137 loc) 5.42 kB
import React, { useEffect, useState } from 'react'; import { bind } from 'bind-event-listener'; import { useSharedPluginStateSelector } from '@atlaskit/editor-common/use-shared-plugin-state-selector'; import { isOfflineMode } from '@atlaskit/editor-plugin-connectivity'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import ConfigPanel from './ConfigPanel'; import { useStateFromPromise } from './use-state-from-promise'; const getFieldsDefinitionFn = (extensionManifest, nodeKey) => { if (extensionManifest && extensionManifest.modules.nodes && extensionManifest.modules.nodes[nodeKey] && extensionManifest.modules.nodes[nodeKey].getFieldsDefinition) { return extensionManifest.modules.nodes[nodeKey].getFieldsDefinition; } }; // having the default value in the props instead of a reference will cause excessive rerenders const defaultEmptyObject = {}; const FieldDefinitionsPromiseResolver = props => { const { extensionManifest, nodeKey, extensionParameters, setErrorMessage } = props; const [fields, setFields] = useState(undefined); // Event listener for Forge apps that have macro config. // When an app generates a new config schema, we need to force a re-render // of the config panel so that the UI reflects the new schema. // Otherwise the panel renders a stale schema. const [forgeAppConfigLastUpdated, setForgeAppConfigLastUpdated] = useState(new Date()); useEffect(() => { if (extensionParameters !== null && extensionParameters !== void 0 && extensionParameters.extensionId && extensionParameters !== null && extensionParameters !== void 0 && extensionParameters.localId) { const { extensionId, localId } = extensionParameters; const id = `${extensionId}-${localId}`; const eventName = `forge.bridge.CONFIG_FORGE_DOC_UPDATED_${id}`; const handleForgeConfigUpdated = () => { setForgeAppConfigLastUpdated(new Date()); }; const unbind = bind(window.document, { type: eventName, listener: handleForgeConfigUpdated }); return () => { unbind(); }; } }, [extensionParameters]); // Resolve the promise // useStateFromPromise() has an issue which isn't compatible with // DynamicFieldDefinitions when it returns a function as setState() // will immediately run the function returned and pass it the currentState. useEffect(() => { if (!extensionManifest) { return; } const promiseFn = getFieldsDefinitionFn(extensionManifest, nodeKey); if (typeof promiseFn !== 'function') { // eslint-disable-next-line @atlassian/perf-linting/no-chain-state-updates -- Ignored via go/ees017 (to be fixed) setFields(undefined); return; } promiseFn(extensionParameters).catch(err => { if (err && typeof err.message === 'string') { setErrorMessage(err.message); } setFields(undefined); }).then(value => { if (Array.isArray(value)) { // value: FieldDefinition[] setFields(value); } else if (typeof value === 'function') { try { // value: DynamicFieldDefinitions const dynamicFields = value(extensionParameters); setFields(dynamicFields); } catch (err) { if (err instanceof Error) { setErrorMessage(err.message); } setFields(undefined); } } else { // value: undefined setFields(undefined); } }); }, [extensionManifest, nodeKey, extensionParameters, setErrorMessage, forgeAppConfigLastUpdated]); return /*#__PURE__*/React.createElement(React.Fragment, null, props.children(fields)); }; export default function FieldsLoader({ extensionType, extensionKey, nodeKey, extensionProvider, extensionParameters = defaultEmptyObject, parameters = defaultEmptyObject, autoSaveTrigger, autoSaveReject, closeOnEsc, showHeader, featureFlags, onChange, onCancel, api, // Remove below prop when cleaning platform_editor_ai_object_sidebar_injection FG usingObjectSidebarPanel }) { const [extensionManifest] = useStateFromPromise(() => extensionProvider.getExtension(extensionType, extensionKey), [extensionProvider, extensionType, extensionKey]); const [errorMessage, setErrorMessage] = useState(null); const connectivityState = useSharedPluginStateSelector(api, 'connectivity.mode', { disabled: editorExperiment('platform_editor_offline_editing_web', false) }); return /*#__PURE__*/React.createElement(FieldDefinitionsPromiseResolver, { setErrorMessage: setErrorMessage, extensionManifest: extensionManifest, nodeKey: nodeKey, extensionParameters: extensionParameters }, fields => /*#__PURE__*/React.createElement(ConfigPanel, { api: api, extensionManifest: extensionManifest, isLoading: !extensionManifest || errorMessage === null && !fields, fields: fields, parameters: parameters, autoSaveTrigger: autoSaveTrigger, autoSaveReject: autoSaveReject, closeOnEsc: closeOnEsc, showHeader: showHeader, onChange: onChange, onCancel: onCancel, errorMessage: errorMessage, featureFlags: featureFlags // Remove below prop when cleaning platform_editor_ai_object_sidebar_injection FG , usingObjectSidebarPanel: usingObjectSidebarPanel, disableFields: isOfflineMode(connectivityState) })); }