@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
201 lines (199 loc) • 5.79 kB
JavaScript
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();
};