UNPKG

@atlaskit/editor-common

Version:

A package that contains common classes and components for editor and renderer

198 lines (197 loc) • 7.95 kB
/** @jsx jsx */ import { Fragment, useCallback, useEffect, useMemo, useState } from 'react'; import { jsx } from '@emotion/react'; import ReactDOM from 'react-dom'; import { createIntl, injectIntl } from 'react-intl-next'; import { useAnalyticsEvents } from '@atlaskit/analytics-next'; import { fireFailedMediaInlineEvent, fireSucceededMediaInlineEvent, MediaCardError } from '@atlaskit/media-card'; import { FileFetcherError } from '@atlaskit/media-client'; import { MediaClientContext } from '@atlaskit/media-client-react'; import { MediaViewer } from '@atlaskit/media-viewer'; import { getBooleanFF } from '@atlaskit/platform-feature-flags'; import { messages } from '../messages/media-inline-card'; import { referenceHeights } from './constants'; import { InlineImageCard } from './inline-image-card'; import { InlineImageWrapper } from './inline-image-wrapper'; import { InlineImageCardErrorView } from './views/error-view'; import { InlineImageCardLoadingView } from './views/loading-view'; export const MediaInlineImageCardInternal = ({ mediaClient, identifier, isSelected, intl, alt, isLazy, width, height, border, ssr, serializeDataAttrs, shouldOpenMediaViewer }) => { const [fileState, setFileState] = useState(); const [subscribeError, setSubscribeError] = useState(); const [isFailedEventSent, setIsFailedEventSent] = useState(false); const [isSucceededEventSent, setIsSucceededEventSent] = useState(false); const [isMediaViewerVisible, setMediaViewerVisible] = useState(false); const { formatMessage } = intl || createIntl({ locale: 'en' }); const { createAnalyticsEvent } = useAnalyticsEvents(); const fireFailedOperationalEvent = (error = new MediaCardError('missing-error-data'), failReason) => { if (!isFailedEventSent && fileState) { setIsFailedEventSent(true); fireFailedMediaInlineEvent(fileState, error, failReason, createAnalyticsEvent); } }; const fireSucceededOperationalEvent = () => { if (!isSucceededEventSent && fileState) { setIsSucceededEventSent(true); fireSucceededMediaInlineEvent(fileState, createAnalyticsEvent); } }; useEffect(() => { if (mediaClient) { const subscription = mediaClient.file.getFileState(identifier.id, { collectionName: identifier.collectionName }).subscribe({ next: fileState => { setFileState(fileState); setSubscribeError(undefined); }, error: e => { setSubscribeError(e); } }); return () => { subscription.unsubscribe(); }; } }, [identifier, mediaClient]); const content = dimensions => { if (!mediaClient) { return jsx(InlineImageCardLoadingView, null); } if (!ssr) { if (subscribeError) { const isUploading = (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'uploading'; const errorMessage = isUploading ? messages.failedToUpload : messages.unableToLoadContent; const errorReason = (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'uploading' ? 'upload' : 'metadata-fetch'; fireFailedOperationalEvent(new MediaCardError(errorReason, subscribeError)); return jsx(InlineImageCardErrorView, { message: formatMessage(errorMessage) }); } if (!fileState || (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'uploading') { return jsx(InlineImageCardLoadingView, null); } if (fileState.status === 'error') { fireFailedOperationalEvent(new MediaCardError('error-file-state', new Error(fileState.message))); return jsx(InlineImageCardErrorView, { message: formatMessage(messages.unableToLoadContent) }); } else if (fileState.status === 'failed-processing') { fireFailedOperationalEvent(undefined, 'failed-processing'); return jsx(InlineImageCardErrorView, { message: formatMessage(messages.unableToLoadContent) }); } else if (!fileState.name) { fireFailedOperationalEvent(new MediaCardError('metadata-fetch', new FileFetcherError('emptyFileName', fileState.id))); return jsx(InlineImageCardErrorView, { message: formatMessage(messages.unableToLoadContent) }); } if (fileState.status === 'processed') { fireSucceededOperationalEvent(); } } return jsx(MediaClientContext.Provider, { value: mediaClient }, jsx(InlineImageCard, { dimensions: dimensions, identifier: identifier, renderError: () => jsx(InlineImageCardErrorView, { message: formatMessage(messages.unableToLoadContent) }), alt: alt, ssr: ssr === null || ssr === void 0 ? void 0 : ssr.mode, isLazy: isLazy, crop: true, stretch: false })); }; const aspectRatio = useMemo(() => width && height ? width / height : undefined, [width, height]); /** * scaledDimensions is used to define the correct media size fetched from media service * inline images will only ever be rendered at a maximum height of H1 and so scaled dimensions * will only ever return a width and height where the height has a maximum height of H1 */ const scaledDimension = useMemo(() => { if (!width || !height || !aspectRatio) { return { width, height }; } return { width: Math.round(aspectRatio * referenceHeights['h1']), height: referenceHeights['h1'] }; }, [width, height, aspectRatio]); const htmlAttributes = useMemo(() => { if (serializeDataAttrs) { const resolvedAttrs = fileState && fileState.status !== 'error' ? { 'data-file-size': fileState.size, 'data-file-mime-type': fileState.mimeType, 'data-file-name': fileState.name } : {}; return { 'data-type': 'image', 'data-node-type': 'mediaInline', 'data-id': identifier.id, 'data-collection': identifier.collectionName, 'data-width': width, 'data-height': height, 'data-alt': alt, ...resolvedAttrs }; } return {}; }, [alt, fileState, height, identifier, width, serializeDataAttrs]); const onMediaInlineImageClick = useCallback(() => { if (shouldOpenMediaViewer && getBooleanFF('platform.editor.media.inline-image.renderer-preview-support_3w1ju')) { setMediaViewerVisible(true); } }, [shouldOpenMediaViewer]); const onMediaInlinePreviewClose = useCallback(() => { setMediaViewerVisible(false); }, []); const mediaViewer = useMemo(() => { if (isMediaViewerVisible && mediaClient !== null && mediaClient !== void 0 && mediaClient.mediaClientConfig) { return /*#__PURE__*/ReactDOM.createPortal(jsx(MediaViewer, { collectionName: identifier.collectionName || '', items: [identifier], mediaClientConfig: mediaClient === null || mediaClient === void 0 ? void 0 : mediaClient.mediaClientConfig, selectedItem: identifier, onClose: onMediaInlinePreviewClose }), document.body); } return null; }, [identifier, isMediaViewerVisible, mediaClient === null || mediaClient === void 0 ? void 0 : mediaClient.mediaClientConfig, onMediaInlinePreviewClose]); return jsx(Fragment, null, jsx(InlineImageWrapper, { isSelected: isSelected, isInteractive: getBooleanFF('platform.editor.media.inline-image.renderer-preview-support_3w1ju') && shouldOpenMediaViewer, aspectRatio: aspectRatio, borderColor: border === null || border === void 0 ? void 0 : border.borderColor, borderSize: border === null || border === void 0 ? void 0 : border.borderSize, htmlAttrs: htmlAttributes, onClick: onMediaInlineImageClick }, content(scaledDimension)), mediaViewer); }; export const MediaInlineImageCard = injectIntl(MediaInlineImageCardInternal, { enforceContext: false });