@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
391 lines (390 loc) • 13.7 kB
JavaScript
import _defineProperty from "@babel/runtime/helpers/defineProperty";
import uuidV4 from 'uuid/v4';
import { ACTION, ACTION_SUBJECT, EVENT_TYPE } from '@atlaskit/editor-common/analytics';
import { DEFAULT_IMAGE_HEIGHT, DEFAULT_IMAGE_WIDTH } from '@atlaskit/editor-common/media-single';
import { getAttrsFromUrl, isImageRepresentationReady, isMediaBlobUrl } from '@atlaskit/media-client';
import { getMediaClient } from '@atlaskit/media-client-react';
import { replaceExternalMedia, updateAllMediaSingleNodesAttrs, updateCurrentMediaNodeAttrs, updateMediaSingleNodeAttrs } from '../commands/helpers';
export class MediaNodeUpdater {
constructor(props) {
// Updates the node with contextId if it doesn't have one already
// TODO [MS-2258]: remove updateContextId in order to only use updateMediaSingleFileAttrs
_defineProperty(this, "updateContextId", async () => {
const attrs = this.getAttrs();
if (!attrs || attrs.type !== 'file') {
return;
}
const {
id
} = attrs;
const objectId = await this.getObjectId();
updateAllMediaSingleNodesAttrs(id, {
__contextId: objectId
})(this.props.view.state, this.props.view.dispatch);
});
_defineProperty(this, "updateNodeContextId", async getPos => {
const attrs = this.getAttrs();
if ((attrs === null || attrs === void 0 ? void 0 : attrs.type) !== 'file') {
return;
}
const objectId = await this.getObjectId();
updateCurrentMediaNodeAttrs({
__contextId: objectId
}, {
node: this.props.node,
getPos
})(this.props.view.state, this.props.view.dispatch);
});
_defineProperty(this, "hasFileAttributesDefined", attrs => {
return attrs && attrs.type === 'file' && attrs.__fileName && attrs.__fileMimeType && attrs.__fileSize && attrs.__contextId;
});
_defineProperty(this, "getNewFileAttrsForNode", async () => {
const attrs = this.getAttrs();
const mediaProvider = await this.props.mediaProvider;
if (!mediaProvider || !mediaProvider.uploadParams || !attrs || attrs.type !== 'file' || this.hasFileAttributesDefined(attrs)) {
return;
}
const mediaClientConfig = mediaProvider.viewMediaClientConfig;
const mediaClient = getMediaClient(mediaClientConfig);
let fileState;
const {
id,
collection: collectionName
} = attrs;
try {
fileState = await mediaClient.file.getCurrentState(id, {
collectionName
});
if (fileState.status === 'error') {
return;
}
} catch (err) {
return;
}
const contextId = this.getNodeContextId() || (await this.getObjectId());
const {
name,
mimeType,
size
} = fileState;
const newAttrs = {
__fileName: name,
__fileMimeType: mimeType,
__fileSize: size,
__contextId: contextId
};
if (!hasPrivateAttrsChanged(attrs, newAttrs)) {
return;
}
return newAttrs;
});
_defineProperty(this, "updateMediaSingleFileAttrs", async () => {
const newAttrs = await this.getNewFileAttrsForNode();
const {
id
} = this.getAttrs();
if (id && newAttrs) {
updateAllMediaSingleNodesAttrs(id, newAttrs)(this.props.view.state, this.props.view.dispatch);
}
});
_defineProperty(this, "updateNodeAttrs", async getPos => {
const newAttrs = await this.getNewFileAttrsForNode();
if (newAttrs) {
updateCurrentMediaNodeAttrs(newAttrs, {
node: this.props.node,
getPos
})(this.props.view.state, this.props.view.dispatch);
}
});
_defineProperty(this, "getAttrs", () => {
const {
attrs
} = this.props.node;
if (attrs) {
return attrs;
}
return undefined;
});
_defineProperty(this, "getObjectId", async () => {
const contextIdentifierProvider = await this.props.contextIdentifierProvider;
return (contextIdentifierProvider === null || contextIdentifierProvider === void 0 ? void 0 : contextIdentifierProvider.objectId) || null;
});
_defineProperty(this, "uploadExternalMedia", async getPos => {
const {
node
} = this.props;
const mediaProvider = await this.props.mediaProvider;
if (node && mediaProvider) {
const uploadMediaClientConfig = mediaProvider.uploadMediaClientConfig;
if (!uploadMediaClientConfig || !node.attrs.url) {
return;
}
const mediaClient = getMediaClient(uploadMediaClientConfig);
const collection = mediaProvider.uploadParams && mediaProvider.uploadParams.collection;
try {
const uploader = await mediaClient.file.uploadExternal(node.attrs.url, collection);
const {
uploadableFileUpfrontIds,
dimensions
} = uploader;
const pos = getPos();
if (typeof pos !== 'number') {
return;
}
replaceExternalMedia(pos + 1, {
id: uploadableFileUpfrontIds.id,
collection,
height: dimensions.height,
width: dimensions.width,
occurrenceKey: uploadableFileUpfrontIds.occurrenceKey
})(this.props.view.state, this.props.view.dispatch);
} catch (e) {
//keep it as external media
if (this.props.dispatchAnalyticsEvent) {
this.props.dispatchAnalyticsEvent({
action: ACTION.UPLOAD_EXTERNAL_FAIL,
actionSubject: ACTION_SUBJECT.EDITOR,
eventType: EVENT_TYPE.OPERATIONAL
});
}
}
}
});
_defineProperty(this, "getNodeContextId", () => {
const attrs = this.getAttrs();
if (!attrs || attrs.type !== 'file') {
return null;
}
return attrs.__contextId || null;
});
_defineProperty(this, "updateDimensions", dimensions => {
updateAllMediaSingleNodesAttrs(dimensions.id, {
height: dimensions.height,
width: dimensions.width
})(this.props.view.state, this.props.view.dispatch);
});
_defineProperty(this, "hasDifferentContextId", async () => {
const nodeContextId = this.getNodeContextId();
const currentContextId = await this.getObjectId();
if (nodeContextId && currentContextId && nodeContextId !== currentContextId) {
return true;
}
return false;
});
_defineProperty(this, "isNodeFromDifferentCollection", async () => {
const mediaProvider = await this.props.mediaProvider;
if (!mediaProvider || !mediaProvider.uploadParams) {
return false;
}
const currentCollectionName = mediaProvider.uploadParams.collection;
const attrs = this.getAttrs();
if (!attrs || attrs.type !== 'file') {
return false;
}
const {
collection: nodeCollection,
__contextId
} = attrs;
const contextId = __contextId || (await this.getObjectId());
if (contextId && currentCollectionName !== nodeCollection) {
return true;
}
return false;
});
_defineProperty(this, "copyNodeFromBlobUrl", async getPos => {
const attrs = this.getAttrs();
if (!attrs || attrs.type !== 'external') {
return;
}
const {
url
} = attrs;
const mediaAttrs = getAttrsFromUrl(url);
if (!mediaAttrs) {
return;
}
const mediaProvider = await this.props.mediaProvider;
if (!mediaProvider || !mediaProvider.uploadParams) {
return;
}
const currentCollectionName = mediaProvider.uploadParams.collection;
const {
contextId,
id,
collection,
height,
width,
mimeType,
name,
size
} = mediaAttrs;
const uploadMediaClientConfig = mediaProvider.uploadMediaClientConfig;
if (!uploadMediaClientConfig || !uploadMediaClientConfig.getAuthFromContext) {
return;
}
const mediaClient = getMediaClient(uploadMediaClientConfig);
const auth = await uploadMediaClientConfig.getAuthFromContext(contextId);
const source = {
id,
collection,
authProvider: () => Promise.resolve(auth)
};
const destination = {
collection: currentCollectionName,
authProvider: uploadMediaClientConfig.authProvider,
occurrenceKey: uuidV4()
};
const mediaFile = await mediaClient.file.copyFile(source, destination);
const pos = getPos();
if (typeof pos !== 'number') {
return;
}
replaceExternalMedia(pos + 1, {
id: mediaFile.id,
collection: currentCollectionName,
height,
width,
__fileName: name,
__fileMimeType: mimeType,
__fileSize: size
})(this.props.view.state, this.props.view.dispatch);
});
// Copies the pasted node into the current collection using a getPos handler
_defineProperty(this, "copyNodeFromPos", async (getPos, traceContext) => {
const attrs = this.getAttrs();
if ((attrs === null || attrs === void 0 ? void 0 : attrs.type) !== 'file') {
return;
}
const copiedAttrs = await this.copyFile(attrs.id, attrs.collection, traceContext);
if (!copiedAttrs) {
return;
}
updateCurrentMediaNodeAttrs(copiedAttrs, {
node: this.props.node,
getPos
})(this.props.view.state, this.props.view.dispatch);
});
// Copies the pasted node into the current collection
_defineProperty(this, "copyNode", async traceContext => {
const attrs = this.getAttrs();
const {
view
} = this.props;
if ((attrs === null || attrs === void 0 ? void 0 : attrs.type) !== 'file') {
return;
}
const copiedAttrs = await this.copyFile(attrs.id, attrs.collection, traceContext);
if (!copiedAttrs) {
return;
}
updateMediaSingleNodeAttrs(attrs.id, copiedAttrs)(view.state, view.dispatch);
});
_defineProperty(this, "copyFile", async (id, collection, traceContext) => {
const mediaProvider = await this.props.mediaProvider;
if (!(mediaProvider !== null && mediaProvider !== void 0 && mediaProvider.uploadParams)) {
return;
}
const nodeContextId = this.getNodeContextId();
const uploadMediaClientConfig = mediaProvider.uploadMediaClientConfig;
if (!(uploadMediaClientConfig !== null && uploadMediaClientConfig !== void 0 && uploadMediaClientConfig.getAuthFromContext) || !nodeContextId) {
return;
}
const mediaClient = getMediaClient(uploadMediaClientConfig);
const auth = await uploadMediaClientConfig.getAuthFromContext(nodeContextId);
const objectId = await this.getObjectId();
const source = {
id,
collection,
authProvider: () => Promise.resolve(auth)
};
const currentCollectionName = mediaProvider.uploadParams.collection;
const destination = {
collection: currentCollectionName,
authProvider: uploadMediaClientConfig.authProvider,
occurrenceKey: uuidV4()
};
const mediaFile = await mediaClient.file.copyFile(source, destination, undefined, traceContext);
return {
id: mediaFile.id,
collection: currentCollectionName,
__contextId: objectId
};
});
this.props = props;
}
setProps(newComponentProps) {
this.props = {
...this.props,
...newComponentProps
};
}
isMediaBlobUrl() {
const attrs = this.getAttrs();
return !!(attrs && attrs.type === 'external' && isMediaBlobUrl(attrs.url));
}
async getRemoteDimensions() {
const mediaProvider = await this.props.mediaProvider;
const {
mediaOptions
} = this.props;
const attrs = this.getAttrs();
if (!mediaProvider || !attrs) {
return false;
}
const {
height,
width
} = attrs;
if (attrs.type === 'external' || !attrs.id) {
return false;
}
const {
id,
collection
} = attrs;
if (height && width) {
return false;
}
// can't fetch remote dimensions on mobile, so we'll default them
if (mediaOptions && !mediaOptions.allowRemoteDimensionsFetch) {
return {
id,
height: DEFAULT_IMAGE_HEIGHT,
width: DEFAULT_IMAGE_WIDTH
};
}
const viewMediaClientConfig = mediaProvider.viewMediaClientConfig;
const mediaClient = getMediaClient(viewMediaClientConfig);
const currentState = await mediaClient.file.getCurrentState(id, {
collectionName: collection
});
if (!isImageRepresentationReady(currentState)) {
return false;
}
const imageMetadata = await mediaClient.getImageMetadata(id, {
collection
});
if (!imageMetadata || !imageMetadata.original) {
return false;
}
return {
id,
height: imageMetadata.original.height || DEFAULT_IMAGE_HEIGHT,
width: imageMetadata.original.width || DEFAULT_IMAGE_WIDTH
};
}
async handleExternalMedia(getPos) {
if (this.isMediaBlobUrl()) {
try {
await this.copyNodeFromBlobUrl(getPos);
} catch (e) {
await this.uploadExternalMedia(getPos);
}
} else {
await this.uploadExternalMedia(getPos);
}
}
}
const hasPrivateAttrsChanged = (currentAttrs, newAttrs) => {
return currentAttrs.__fileName !== newAttrs.__fileName || currentAttrs.__fileMimeType !== newAttrs.__fileMimeType || currentAttrs.__fileSize !== newAttrs.__fileSize || currentAttrs.__contextId !== newAttrs.__contextId;
};