UNPKG

@atlaskit/editor-plugin-media

Version:

Media plugin for @atlaskit/editor-core

391 lines (390 loc) 13.7 kB
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; };