@atlaskit/editor-common
Version:
A package that contains common classes and components for editor and renderer
198 lines (197 loc) • 7.95 kB
JavaScript
/** @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
});