@atlaskit/editor-plugin-extension
Version:
editor-plugin-extension plugin for @atlaskit/editor-core
140 lines (137 loc) • 5.42 kB
JavaScript
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)
}));
}