UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

201 lines (199 loc) 5.79 kB
import React, { useEffect, useState } from 'react'; import { useSharedPluginState } from '@atlaskit/editor-common/hooks'; import { WithProviders } from '@atlaskit/editor-common/provider-factory'; import { SelectionBasedNodeView } from '@atlaskit/editor-common/selection-based-node-view'; import { MediaInlineCard } from '@atlaskit/media-card'; import { MediaInlineCardLoadingView } from '@atlaskit/media-ui'; import { MediaNodeUpdater } from './mediaNodeUpdater'; import { MediaInlineNodeSelector } from './styles'; export const createMediaNodeUpdater = props => { const node = props.node; return new MediaNodeUpdater({ ...props, isMediaSingle: true, node: node ? node : props.node, dispatchAnalyticsEvent: props.dispatchAnalyticsEvent, contextIdentifierProvider: props.contextIdentifierProvider }); }; /** * Handles updating the media inline node attributes * but also handling copy-paste for cross-editor of the same instance * using the contextid * */ export const updateMediaNodeAttributes = async (props, mediaNodeUpdater) => { const { addPendingTask } = props.mediaPluginState; const node = props.node; if (!node) { return; } const contextId = mediaNodeUpdater.getNodeContextId(); if (!contextId) { await mediaNodeUpdater.updateContextId(); } const hasDifferentContextId = await mediaNodeUpdater.hasDifferentContextId(); if (hasDifferentContextId) { // Copy paste flow (different pages) try { const copyNode = mediaNodeUpdater.copyNode({ traceId: node.attrs.__mediaTraceId }); addPendingTask(copyNode); await copyNode; } catch (e) { return; } } await mediaNodeUpdater.updateMediaSingleFileAttrs(); }; export const handleNewNode = props => { const { node, mediaPluginState, getPos } = props; mediaPluginState.handleMediaNodeMount(node, () => getPos()); }; export const MediaInline = props => { const [viewMediaClientConfig, setViewMediaClientConfig] = useState(); const [isContextIdUnsync, setIsContextIdUnsync] = useState(true); useEffect(() => { const mediaNodeUpdater = createMediaNodeUpdater(props); mediaNodeUpdater.hasDifferentContextId().then(setIsContextIdUnsync); handleNewNode(props); updateMediaNodeAttributes(props, mediaNodeUpdater); updateViewMediaClientConfig(props); return () => { const { mediaPluginState } = props; mediaPluginState.handleMediaNodeUnmount(props.node); }; }, [props]); const updateViewMediaClientConfig = async props => { const mediaProvider = await props.mediaProvider; if (mediaProvider) { const viewMediaClientConfig = mediaProvider.viewMediaClientConfig; setViewMediaClientConfig(viewMediaClientConfig); } }; const { id, collection } = props.node.attrs; const identifier = { id, mediaItemType: 'file', collectionName: collection }; /* * Show the loading view if * 1. The media provider is not ready * 2. Context Id is not synced * to prevent calling the media API (in mounting of `MediaInlineCard`) * before the prerequisites meet */ if (!viewMediaClientConfig || isContextIdUnsync) { return /*#__PURE__*/React.createElement(MediaInlineCardLoadingView, { message: "", isSelected: false }); } return /*#__PURE__*/React.createElement(MediaInlineCard, { isSelected: props.isSelected, identifier: identifier, mediaClientConfig: viewMediaClientConfig }); }; const MediaInlineSharedState = ({ identifier, mediaProvider, node, isSelected, getPos, contextIdentifierProvider, api, view }) => { const { mediaState } = useSharedPluginState(api, ['media']); if (!mediaState) { return null; } return /*#__PURE__*/React.createElement(MediaInline, { identifier: identifier, mediaProvider: mediaProvider, mediaPluginState: mediaState, node: node, isSelected: isSelected, view: view, getPos: getPos, contextIdentifierProvider: contextIdentifierProvider }); }; export class MediaInlineNodeView extends SelectionBasedNodeView { createDomRef() { const domRef = document.createElement('span'); domRef.contentEditable = 'false'; return domRef; } getContentDOM() { const dom = document.createElement('span'); dom.classList.add(MediaInlineNodeSelector); return { dom }; } ignoreMutation() { return true; } viewShouldUpdate(nextNode) { if (this.node.attrs !== nextNode.attrs) { return true; } return super.viewShouldUpdate(nextNode); } render(props) { const { providerFactory, api } = props; const { view } = this; const getPos = this.getPos; return /*#__PURE__*/React.createElement(WithProviders, { providers: ['mediaProvider', 'contextIdentifierProvider'], providerFactory: providerFactory, renderNode: ({ mediaProvider, contextIdentifierProvider }) => { if (!mediaProvider) { return null; } return /*#__PURE__*/React.createElement(MediaInlineSharedState, { identifier: this.node.attrs.id, mediaProvider: mediaProvider, node: this.node, isSelected: this.nodeInsideSelection(), view: view, getPos: getPos, contextIdentifierProvider: contextIdentifierProvider, api: api }); } }); } } export const ReactMediaInlineNode = (portalProviderAPI, eventDispatcher, providerFactory, api, dispatchAnalyticsEvent) => (node, view, getPos) => { return new MediaInlineNodeView(node, view, getPos, portalProviderAPI, eventDispatcher, { providerFactory, dispatchAnalyticsEvent, api }).init(); };