UNPKG

@atlaskit/renderer

Version:
365 lines (362 loc) • 12.7 kB
import _extends from "@babel/runtime/helpers/extends"; import _defineProperty from "@babel/runtime/helpers/defineProperty"; import React, { Component, useContext } from 'react'; import { filter } from '@atlaskit/adf-utils/traverse'; import { Card as CardAsync, CardSync, CardLoading, CardError } from '@atlaskit/media-card'; import { MediaClientContext } from '@atlaskit/media-client-react'; import { withImageLoader } from '@atlaskit/editor-common/utils'; import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals'; import { editorExperiment } from '@atlaskit/tmp-editor-statsig/experiments'; import AnalyticsContext from '../analytics/analyticsContext'; import { ACTION, ACTION_SUBJECT, ACTION_SUBJECT_ID, EVENT_TYPE } from '@atlaskit/editor-common/analytics'; export const mediaIdentifierMap = new Map(); export const getListOfIdentifiersFromDoc = doc => { if (!doc) { return []; } return filter(doc, node => node.type === 'media').reduce((identifierList, mediaNode) => { if (mediaNode.attrs) { const { type, url: dataURI, id } = mediaNode.attrs; if (type === 'file' && id) { identifierList.push({ mediaItemType: 'file', id }); } else if (type === 'external' && dataURI) { identifierList.push({ mediaItemType: 'external-image', dataURI, name: dataURI }); } } return identifierList; }, []); }; // Ignored via go/ees005 // eslint-disable-next-line @repo/internal/react/no-class-components export class MediaCardView extends Component { constructor(...args) { super(...args); _defineProperty(this, "state", {}); _defineProperty(this, "saveFileState", async id => { const { collection: collectionName, mediaClient } = this.props; const options = { collectionName }; try { if (mediaClient) { const fileState = await mediaClient.file.getCurrentState(id, options); this.setState({ fileState }); } } catch { // do not set state on error } }); _defineProperty(this, "onError", reason => { var _this$props$fireAnaly, _this$props; const { nestedUnder, rendererContext } = this.props; (_this$props$fireAnaly = (_this$props = this.props).fireAnalyticsEvent) === null || _this$props$fireAnaly === void 0 ? void 0 : _this$props$fireAnaly.call(_this$props, { action: ACTION.ERRORED, actionSubject: ACTION_SUBJECT.RENDERER, actionSubjectId: ACTION_SUBJECT_ID.MEDIA, eventType: EVENT_TYPE.UI, attributes: { reason, external: false, ...(nestedUnder && editorExperiment('platform_synced_block', true) ? { nestedUnder } : {}), ...(rendererContext !== null && rendererContext !== void 0 && rendererContext.nestedRendererType && editorExperiment('platform_synced_block', true) ? { nestedRendererType: rendererContext.nestedRendererType } : {}) } }); }); _defineProperty(this, "renderLoadingCard", () => { const { cardDimensions } = this.props; return /*#__PURE__*/React.createElement(CardLoading, { dimensions: cardDimensions, interactionName: "renderer-media-card-loading" }); }); /** * We want to call provided `eventHandlers.media.onClick` when it's provided, * but we also don't want to call it when it's a video and inline video player is enabled. * This is due to consumers normally process this onClick call by opening media viewer and * we don't want that to happened described above text. */ _defineProperty(this, "getOnCardClickCallback", isInlinePlayer => { const { eventHandlers } = this.props; if (eventHandlers && eventHandlers.media && eventHandlers.media.onClick) { return (result, analyticsEvent) => { const isVideo = result.mediaItemDetails && result.mediaItemDetails.mediaType === 'video'; const isVideoWithInlinePlayer = isInlinePlayer && isVideo; if (!isVideoWithInlinePlayer && eventHandlers && eventHandlers.media && eventHandlers.media.onClick) { eventHandlers.media.onClick(result, analyticsEvent); } }; } return undefined; }); } async componentDidMount() { const { rendererContext, contextIdentifierProvider, id, url, collection: collectionName } = this.props; if (contextIdentifierProvider) { this.setState({ contextIdentifierProvider: await contextIdentifierProvider }); } const nodeIsInCache = id && mediaIdentifierMap.has(id) || url && mediaIdentifierMap.has(url); if (rendererContext && rendererContext.adDoc && !nodeIsInCache) { getListOfIdentifiersFromDoc(rendererContext.adDoc).forEach(identifier => { if (identifier.mediaItemType === 'file' && identifier.id === id) { mediaIdentifierMap.set(identifier.id, { ...identifier, collectionName }); } else if (identifier.mediaItemType === 'external-image') { mediaIdentifierMap.set(identifier.dataURI, identifier); } }); } if (id) { this.saveFileState(id); } } componentDidUpdate(prevProps) { const { id: oldId } = prevProps; if (this.props.id && oldId !== this.props.id) { this.saveFileState(this.props.id); } } componentWillUnmount() { const { id, url: dataURI } = this.props; if (id) { mediaIdentifierMap.delete(id); } else if (dataURI) { mediaIdentifierMap.delete(dataURI); } } renderExternal(shouldOpenMediaViewer) { const { cardDimensions, resizeMode, appearance, url, imageStatus, disableOverlay, alt, featureFlags, ssr, mediaClient, dataAttributes, enableSyncMediaCard, localId } = this.props; if (imageStatus === 'loading' || !url) { return this.renderLoadingCard(); } const identifier = { dataURI: url, name: url, mediaItemType: 'external-image' }; // we need this statement for the mandatory mediaClientConfig below const mediaClientConfig = mediaClient === null || mediaClient === void 0 ? void 0 : mediaClient.mediaClientConfig; const Card = enableSyncMediaCard ? CardSync : CardAsync; return ( /*#__PURE__*/ // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading React.createElement("div", _extends({}, dataAttributes, { "data-node-type": "media", "data-local-id": localId }), /*#__PURE__*/React.createElement(Card // TODO: MPT-315 - clean up after we move mediaClientConfig into FileIdentifier // context is not really used when the type is external and we want to render the component asap // Ignored via go/ees005 // eslint-disable-next-line @typescript-eslint/no-non-null-assertion , { mediaClientConfig: mediaClientConfig, alt: alt, identifier: identifier, dimensions: cardDimensions, appearance: appearance, resizeMode: resizeMode, disableOverlay: disableOverlay, shouldOpenMediaViewer: shouldOpenMediaViewer, mediaViewerItems: Array.from(mediaIdentifierMap.values()), featureFlags: featureFlags, ssr: ssr === null || ssr === void 0 ? void 0 : ssr.mode, shouldHideTooltip: false, onError: expValEquals('platform_editor_media_error_analytics', 'isEnabled', true) ? this.onError : undefined })) ); } render() { const { contextIdentifierProvider, fileState } = this.state; const { id, alt, type, collection, occurrenceKey, cardDimensions, resizeMode, disableOverlay, useInlinePlayer, originalDimensions, shouldOpenMediaViewer: forceOpenMediaViewer, featureFlags, shouldEnableDownloadButton, ssr, mediaClient, dataAttributes, enableSyncMediaCard, localId, mediaViewerExtensions } = this.props; const isMobile = false; const shouldPlayInline = useInlinePlayer !== undefined ? useInlinePlayer : true; const isInlinePlayer = isMobile ? false : shouldPlayInline; const onCardClick = this.getOnCardClickCallback(isInlinePlayer); const shouldOpenMediaViewer = typeof forceOpenMediaViewer === 'boolean' ? forceOpenMediaViewer : !isMobile && !onCardClick; if (type === 'external') { return this.renderExternal(shouldOpenMediaViewer); } if (type === 'link') { return null; } const mediaClientConfig = !!ssr ? ssr.config : mediaClient === null || mediaClient === void 0 ? void 0 : mediaClient.mediaClientConfig; if (!mediaClientConfig || !id) { return this.renderLoadingCard(); } if (!id || type !== 'file') { return /*#__PURE__*/React.createElement(CardError, { dimensions: cardDimensions }); } const contextId = contextIdentifierProvider && contextIdentifierProvider.objectId; const identifier = { id, mediaItemType: 'file', collectionName: collection, occurrenceKey }; const Card = enableSyncMediaCard ? CardSync : CardAsync; // Quick solution to disable lazy loading of images on PDF export pages in Confluence to remedy an issue with images never loading // More robust solution will be implemented as part of CCPDF-233 - Link: https://hello.jira.atlassian.cloud/browse/CCPDF-233 const currentUrl = window.location.href; const shouldDisableLazyLoading = expValEquals('platform_editor_disable_lazy_load_media', 'isEnabled', true) && currentUrl.includes('/wiki/pdf/spaces/'); return /*#__PURE__*/React.createElement("div", _extends({}, getClipboardAttrs({ id, alt, collection, contextIdentifierProvider, originalDimensions, fileState }), dataAttributes, { "data-local-id": localId }), /*#__PURE__*/React.createElement(Card, { identifier: identifier, alt: alt, contextId: contextId, mediaClientConfig: mediaClientConfig, dimensions: cardDimensions, originalDimensions: originalDimensions, onClick: onCardClick, resizeMode: resizeMode, isLazy: !isMobile && !shouldDisableLazyLoading, disableOverlay: disableOverlay, useInlinePlayer: isInlinePlayer, shouldOpenMediaViewer: shouldOpenMediaViewer, mediaViewerItems: Array.from(mediaIdentifierMap.values()), featureFlags: featureFlags, shouldEnableDownloadButton: shouldEnableDownloadButton, ssr: ssr === null || ssr === void 0 ? void 0 : ssr.mode, shouldHideTooltip: isMobile, mediaViewerExtensions: mediaViewerExtensions, onError: expValEquals('platform_editor_media_error_analytics', 'isEnabled', true) ? this.onError : undefined })); } } // Needed for copy & paste export const getClipboardAttrs = ({ id, alt, collection, contextIdentifierProvider, originalDimensions, fileState }) => { const contextId = contextIdentifierProvider && contextIdentifierProvider.objectId; const width = originalDimensions && originalDimensions.width; const height = originalDimensions && originalDimensions.height; let fileName = 'file'; // default name is needed for Confluence let fileSize = 1; let fileMimeType = ''; if (fileState && fileState.status !== 'error') { fileSize = fileState.size; fileName = fileState.name; fileMimeType = fileState.mimeType; } return { 'data-context-id': contextId, 'data-type': 'file', 'data-node-type': 'media', 'data-width': width, 'data-height': height, 'data-id': id, 'data-collection': collection, 'data-file-name': fileName, 'data-file-size': fileSize, 'data-file-mime-type': fileMimeType, 'data-alt': alt }; }; export const MediaCardInternal = props => { const mediaClient = useContext(MediaClientContext); return /*#__PURE__*/React.createElement(AnalyticsContext.Consumer, null, ({ fireAnalyticsEvent }) => { return /*#__PURE__*/React.createElement(MediaCardView // Ignored via go/ees005 // eslint-disable-next-line react/jsx-props-no-spreading , _extends({}, props, { mediaClient: mediaClient, fireAnalyticsEvent: fireAnalyticsEvent })); }); }; export const MediaCard = withImageLoader(MediaCardInternal);