@atlaskit/editor-plugin-media
Version:
Media plugin for @atlaskit/editor-core
645 lines (639 loc) • 37.4 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
var _typeof = require("@babel/runtime/helpers/typeof");
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = exports.MediaNode = exports.MEDIA_HEIGHT = exports.FILE_WIDTH = void 0;
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
var _possibleConstructorReturn2 = _interopRequireDefault(require("@babel/runtime/helpers/possibleConstructorReturn"));
var _getPrototypeOf2 = _interopRequireDefault(require("@babel/runtime/helpers/getPrototypeOf"));
var _inherits2 = _interopRequireDefault(require("@babel/runtime/helpers/inherits"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _react = _interopRequireWildcard(require("react"));
var _bindEventListener = require("bind-event-listener");
var _memoizeOne = _interopRequireDefault(require("memoize-one"));
var _MediaAnalyticsContext = require("@atlaskit/analytics-namespaced-context/MediaAnalyticsContext");
var _AnalyticsContext = _interopRequireDefault(require("@atlaskit/analytics-next/AnalyticsContext"));
var _enums = require("@atlaskit/editor-common/analytics/types/enums");
var _renderedResultTracker = require("@atlaskit/editor-common/rendered-result-tracker");
var _toolbarFlagCheck = require("@atlaskit/editor-common/toolbar-flag-check");
var _utils = require("@atlaskit/editor-common/utils");
var _imageLoader = require("@atlaskit/editor-common/utils/imageLoader");
var _state = require("@atlaskit/editor-prosemirror/state");
var _utils2 = require("@atlaskit/editor-prosemirror/utils");
var _cellSelection = require("@atlaskit/editor-tables/cell-selection");
var _cardLoader = _interopRequireDefault(require("@atlaskit/media-card/cardLoader"));
var _cardLoading = require("@atlaskit/media-card/cardLoading");
var _getMediaClient2 = require("@atlaskit/media-client-react/get-media-client");
var _constants = require("@atlaskit/media-common/constants");
var _preload = require("@atlaskit/media-viewer/preload");
var _isExperimentEnabled = require("@atlaskit/platform-feature-experiments/is-experiment-enabled");
var _fg = require("@atlaskit/platform-feature-flags/fg");
var _editorExperiment = require("@atlaskit/tmp-editor-statsig/editor-experiment");
var _pluginKey = require("../../pm-plugins/plugin-key");
var _GeneratedMediaReveal = require("../../ui/GeneratedMediaReveal");
var _mediaNodeUpdater = require("../mediaNodeUpdater");
var _styles = require("../styles");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != _typeof(e) && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t4 in e) "default" !== _t4 && {}.hasOwnProperty.call(e, _t4) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t4)) && (i.get || i.set) ? o(f, _t4, i) : f[_t4] = e[_t4]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
function _callSuper(t, o, e) { return o = (0, _getPrototypeOf2.default)(o), (0, _possibleConstructorReturn2.default)(t, _isNativeReflectConstruct() ? Reflect.construct(o, e || [], (0, _getPrototypeOf2.default)(t).constructor) : o.apply(t, e)); }
function _isNativeReflectConstruct() { try { var t = !Boolean.prototype.valueOf.call(Reflect.construct(Boolean, [], function () {})); } catch (t) {} return (_isNativeReflectConstruct = function _isNativeReflectConstruct() { return !!t; })(); }
// This is being used by DropPlaceholder now
var MEDIA_HEIGHT = exports.MEDIA_HEIGHT = 125;
var FILE_WIDTH = exports.FILE_WIDTH = 156;
// eslint-disable-next-line @repo/internal/react/no-class-components
var MediaNode = exports.MediaNode = /*#__PURE__*/function (_Component) {
function MediaNode(_props) {
var _this;
(0, _classCallCheck2.default)(this, MediaNode);
_this = _callSuper(this, MediaNode, [_props]);
(0, _defineProperty2.default)(_this, "mediaInstance", {});
(0, _defineProperty2.default)(_this, "mediaElement", null);
(0, _defineProperty2.default)(_this, "setMediaElement", function (element) {
_this.mediaElement = element;
_this.updateRenderedTracking();
});
(0, _defineProperty2.default)(_this, "updateRenderedTracking", function () {
var _this$renderedTracker, _this$renderedTracker2;
(_this$renderedTracker = _this.renderedTracker) === null || _this$renderedTracker === void 0 || _this$renderedTracker.setResult(_this.props.node.attrs.id);
// Observe only when render event is deferred; observing null disconnects any existing observer
(_this$renderedTracker2 = _this.renderedTracker) === null || _this$renderedTracker2 === void 0 || _this$renderedTracker2.observe(_this.props.deferRenderedEventUntilVisible ? _this.mediaElement : null);
});
(0, _defineProperty2.default)(_this, "state", {});
(0, _defineProperty2.default)(_this, "videoControlsWrapperRef", /*#__PURE__*/_react.default.createRef());
(0, _defineProperty2.default)(_this, "unbindKeyDown", null);
(0, _defineProperty2.default)(_this, "getDataConsumerMark", function () {
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.props;
return props.node.marks.find(function (mark) {
return mark.type.name === 'dataConsumer';
});
});
(0, _defineProperty2.default)(_this, "getDataConsumerSource", function () {
var _this$getDataConsumer, _sources$find;
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : _this.props;
var sources = (_this$getDataConsumer = _this.getDataConsumerMark(props)) === null || _this$getDataConsumer === void 0 ? void 0 : _this$getDataConsumer.attrs.sources;
// Prefer remix source if present
return (_sources$find = sources === null || sources === void 0 ? void 0 : sources.find(function (source) {
return source.startsWith('remix:');
})) !== null && _sources$find !== void 0 ? _sources$find : sources === null || sources === void 0 ? void 0 : sources[0];
});
(0, _defineProperty2.default)(_this, "emitMediaRenderEvent", function (event) {
var _props$mediaOptions, _props$mediaOptions$o;
var props = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _this.props;
(_props$mediaOptions = props.mediaOptions) === null || _props$mediaOptions === void 0 || (_props$mediaOptions$o = _props$mediaOptions.onMediaRenderEvent) === null || _props$mediaOptions$o === void 0 || _props$mediaOptions$o.call(_props$mediaOptions, _objectSpread(_objectSpread({}, event), {}, {
dataConsumerSource: _this.getDataConsumerSource(props),
mediaId: props.node.attrs.id,
mediaInstance: _this.mediaInstance
}));
});
(0, _defineProperty2.default)(_this, "fireRenderedEvent", function () {
var _this$props = _this.props,
node = _this$props.node,
pluginInjectionApi = _this$props.pluginInjectionApi;
var infographicType = _this.getDataConsumerSource();
if (infographicType) {
var _pluginInjectionApi$a;
pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$a = pluginInjectionApi.analytics) === null || _pluginInjectionApi$a === void 0 || _pluginInjectionApi$a.actions.fireAnalyticsEvent({
action: _enums.ACTION.RENDERED,
actionSubject: _enums.ACTION_SUBJECT.MEDIA,
actionSubjectId: node.attrs.id,
eventType: _enums.EVENT_TYPE.TRACK,
attributes: {
infographicType: infographicType,
pageMode: 'edit',
mediaId: node.attrs.id
}
});
}
});
(0, _defineProperty2.default)(_this, "setViewMediaClientConfig", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee() {
var mediaProvider, viewMediaClientConfig, viewAndUploadMediaClientConfig;
return _regenerator.default.wrap(function (_context) {
while (1) switch (_context.prev = _context.next) {
case 0:
_context.next = 1;
return _this.props.mediaProvider;
case 1:
mediaProvider = _context.sent;
if (mediaProvider) {
viewMediaClientConfig = mediaProvider.viewMediaClientConfig;
viewAndUploadMediaClientConfig = mediaProvider.viewAndUploadMediaClientConfig; // Only update state if new configs are available and different from current state
if (viewMediaClientConfig && _this.state.viewMediaClientConfig !== viewMediaClientConfig || viewAndUploadMediaClientConfig && _this.state.viewAndUploadMediaClientConfig !== viewAndUploadMediaClientConfig) {
_this.setState({
viewMediaClientConfig: viewMediaClientConfig,
viewAndUploadMediaClientConfig: viewAndUploadMediaClientConfig
});
}
}
case 2:
case "end":
return _context.stop();
}
}, _callee);
})));
(0, _defineProperty2.default)(_this, "selectMediaSingleFromCard", function (_ref2) {
var _this$props$pluginInj;
var event = _ref2.event;
_this.selectMediaSingle(event);
// In edit mode (node content wrapper has contenteditable set to true), link redirection is disabled by default
// We need to call "stopPropagation" here in order to prevent in editor view mode, the browser from navigating to
// another URL if the media node is wrapped in a link mark.
if (_this.props.isViewOnly && (0, _toolbarFlagCheck.areToolbarFlagsEnabled)(Boolean((_this$props$pluginInj = _this.props.pluginInjectionApi) === null || _this$props$pluginInj === void 0 ? void 0 : _this$props$pluginInj.toolbar))) {
event.preventDefault();
}
});
(0, _defineProperty2.default)(_this, "selectMediaSingle", function (event) {
var propPos = _this.props.getPos();
if (typeof propPos !== 'number') {
return;
}
// NOTE: This does not prevent the link navigation in the editor view mode, .preventDefault is needed (see selectMediaSingleFromCard)
// Hence it should be removed
// We need to call "stopPropagation" here in order to prevent the browser from navigating to
// another URL if the media node is wrapped in a link mark.
if ((0, _editorExperiment.editorExperiment)('platform_editor_controls', 'control')) {
event.stopPropagation();
}
var state = _this.props.view.state;
if (event.shiftKey) {
// don't select text if there is current selection in a table (as this would override selected cells)
if (state.selection instanceof _cellSelection.CellSelection) {
return;
}
(0, _utils.setTextSelection)(_this.props.view, state.selection.from < propPos ? state.selection.from : propPos - 1,
// + 3 needed for offset of the media inside mediaSingle and cursor to make whole mediaSingle selected
state.selection.to > propPos ? state.selection.to : propPos + 2);
} else {
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_editor_improve_inp_batch_2') && state.selection instanceof _state.NodeSelection && state.selection.from === propPos - 1) {
// This mediaSingle is already selected: ProseMirror selects the media node from the
// DOM selection when the click moves it, and the media plugin's appendTransaction
// moves that selection to the parent mediaSingle in the same dispatch.
// setNodeSelection always dispatches, so re-selecting it would run another view
// update and force another style recalc and layout for no change.
return;
}
(0, _utils.setNodeSelection)(_this.props.view, propPos - 1);
}
});
(0, _defineProperty2.default)(_this, "getMediaSettings", (0, _memoizeOne.default)(function (viewAndUploadMediaClientConfig, isViewOnly) {
return {
canUpdateVideoCaptions: (0, _fg.fg)('platform_media_video_captions') ? !!viewAndUploadMediaClientConfig && !isViewOnly : false
};
}));
/**
* Media written straight into the document arrives without intrinsic dimensions, and the
* fetch that would supply them resolves `false` while the file is still processing. Only
* editor-driven uploads get a second attempt, from the plugin's media-state listener, so
* otherwise the card would keep its placeholder ratio forever. The preview rendering is the
* one moment the file is guaranteed ready, so the fetch is retried there.
*/
(0, _defineProperty2.default)(_this, "fetchMissingDimensions", /*#__PURE__*/(0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee2() {
var _pluginInjectionApi$w;
var _this$props2, node, view, mediaProvider, contextIdentifierProvider, mediaOptions, pluginInjectionApi, updater, dimensions, _t;
return _regenerator.default.wrap(function (_context2) {
while (1) switch (_context2.prev = _context2.next) {
case 0:
_this$props2 = _this.props, node = _this$props2.node, view = _this$props2.view, mediaProvider = _this$props2.mediaProvider, contextIdentifierProvider = _this$props2.contextIdentifierProvider, mediaOptions = _this$props2.mediaOptions, pluginInjectionApi = _this$props2.pluginInjectionApi;
if (!(node.attrs.type === 'external' || !node.attrs.id || node.attrs.width && node.attrs.height)) {
_context2.next = 1;
break;
}
return _context2.abrupt("return");
case 1:
updater = (0, _mediaNodeUpdater.createMediaNodeUpdater)({
view: view,
mediaProvider: mediaProvider,
contextIdentifierProvider: contextIdentifierProvider,
node: node,
// Carries `allowRemoteDimensionsFetch`, so surfaces that cannot reach the media
// APIs get the default dimensions back instead of a request.
mediaOptions: mediaOptions,
isMediaSingle: true,
lineLength: pluginInjectionApi === null || pluginInjectionApi === void 0 || (_pluginInjectionApi$w = pluginInjectionApi.width) === null || _pluginInjectionApi$w === void 0 || (_pluginInjectionApi$w = _pluginInjectionApi$w.sharedState.currentState()) === null || _pluginInjectionApi$w === void 0 ? void 0 : _pluginInjectionApi$w.lineLength
});
_context2.prev = 2;
_context2.next = 3;
return updater.getRemoteDimensions();
case 3:
dimensions = _context2.sent;
if (dimensions) {
updater.updateDimensions(dimensions);
}
_context2.next = 5;
break;
case 4:
_context2.prev = 4;
_t = _context2["catch"](2);
case 5:
case "end":
return _context2.stop();
}
}, _callee2, null, [[2, 4]]);
})));
/**
* Warms the media viewer while the card is hovered or focused, so opening it
* (double click, or the preview toolbar button) does not start by loading the
* viewer chunks and the full resolution image. Runs in both arms of the
* experiment: `preloadMediaViewer` fires the exposure.
*/
(0, _defineProperty2.default)(_this, "warmMediaViewer", function () {
var _this$props3 = _this.props,
node = _this$props3.node,
mediaOptions = _this$props3.mediaOptions;
var viewMediaClientConfig = _this.state.viewMediaClientConfig;
if (!(mediaOptions !== null && mediaOptions !== void 0 && mediaOptions.allowImagePreview) || !viewMediaClientConfig) {
return;
}
var _node$attrs = node.attrs,
id = _node$attrs.id,
collection = _node$attrs.collection,
type = _node$attrs.type,
__fileMimeType = _node$attrs.__fileMimeType;
if (type !== 'file' || !id) {
return;
}
(0, _preload.preloadMediaViewer)({
// Lazy, so the control arm never creates a client for a config the card may
// not use.
getMediaClient: function getMediaClient() {
return (0, _getMediaClient2.getMediaClient)(viewMediaClientConfig);
},
identifier: {
id: id,
mediaItemType: 'file',
collectionName: collection
},
mimeType: __fileMimeType,
onAnalyticsEvent: function onAnalyticsEvent(payload) {
var _this$props$pluginInj2;
return (_this$props$pluginInj2 = _this.props.pluginInjectionApi) === null || _this$props$pluginInj2 === void 0 || (_this$props$pluginInj2 = _this$props$pluginInj2.analytics) === null || _this$props$pluginInj2 === void 0 ? void 0 : _this$props$pluginInj2.actions.fireAnalyticsEvent(payload, _constants.ANALYTICS_MEDIA_CHANNEL);
}
});
});
(0, _defineProperty2.default)(_this, "onPreviewRender", function (fileId) {
var _this$renderedTracker3;
_this.updateRenderedTracking();
(_this$renderedTracker3 = _this.renderedTracker) === null || _this$renderedTracker3 === void 0 || _this$renderedTracker3.ready(fileId);
_this.emitMediaRenderEvent({
renderedMediaId: fileId,
type: 'preview-rendered'
});
// Only tracked where the reveal uses it. Every media card in every editor calls this,
// and the extra render it would otherwise trigger is not free.
if (_this.hasGeneratedMediaMotion && _this.state.previewRenderedFileId !== fileId) {
_this.setState({
previewRenderedFileId: fileId
});
_this.fetchMissingDimensions();
}
});
(0, _defineProperty2.default)(_this, "onError", function (reason) {
var _this$props$api;
// The card is already showing its error treatment, so let the reveal open onto
// that instead of waiting out its timeout.
_this.setState({
failedFileId: _this.props.node.attrs.id
});
_this.emitMediaRenderEvent({
reason: reason,
type: 'error'
});
var nestedUnder = _this.getNestedUnder();
(_this$props$api = _this.props.api) === null || _this$props$api === void 0 || _this$props$api.media.actions.handleMediaNodeRenderError(_this.props.node, reason, nestedUnder);
});
/**
* This function checks if the media node is nested under a certain nodes, and if so,
* returns the name of the parent node type. This is used for providing more context in media render errors.
* @returns
*/
(0, _defineProperty2.default)(_this, "getNestedUnder", function () {
var pos = _this.props.getPos();
if (typeof pos !== 'number') {
return undefined;
}
var _this$props$view$stat = _this.props.view.state,
doc = _this$props$view$stat.doc,
schema = _this$props$view$stat.schema;
var bodiedSyncBlock = schema.nodes.bodiedSyncBlock;
if (!bodiedSyncBlock) {
return undefined;
}
var resolvedPos = doc.resolve(pos);
var bodiedSyncBlockNode = (0, _utils2.findParentNodeClosestToPos)(resolvedPos, function (currentNode) {
return currentNode.type === bodiedSyncBlock;
});
return bodiedSyncBlockNode === null || bodiedSyncBlockNode === void 0 ? void 0 : bodiedSyncBlockNode.node.type.name;
});
(0, _defineProperty2.default)(_this, "onFullscreenChange", function (fullscreen) {
var _this$mediaPluginStat;
(_this$mediaPluginStat = _this.mediaPluginState) === null || _this$mediaPluginStat === void 0 || _this$mediaPluginStat.updateAndDispatch({
isFullscreen: fullscreen
});
});
(0, _defineProperty2.default)(_this, "handleNewNode", function (props) {
var _this$mediaPluginStat2;
var node = props.node;
(_this$mediaPluginStat2 = _this.mediaPluginState) === null || _this$mediaPluginStat2 === void 0 || _this$mediaPluginStat2.handleMediaNodeMount(node, function () {
return _this.props.getPos();
});
});
var _this$props4 = _this.props,
_view = _this$props4.view,
syncProvider = _this$props4.syncProvider;
_this.mediaPluginState = _pluginKey.stateKey.getState(_view.state);
if ((0, _isExperimentEnabled.isExperimentEnabled)('platform_maui_retention_analytics')) {
_this.renderedTracker = (0, _renderedResultTracker.createRenderedResultTracker)({
onRendered: _this.fireRenderedEvent
});
_this.renderedTracker.setResult(_props.node.attrs.id);
}
// Initialize state from syncProvider (available on both server and client for SSR)
if (syncProvider) {
_this.state = {
viewMediaClientConfig: syncProvider.viewMediaClientConfig,
viewAndUploadMediaClientConfig: syncProvider.viewAndUploadMediaClientConfig
};
}
return _this;
}
(0, _inherits2.default)(MediaNode, _Component);
return (0, _createClass2.default)(MediaNode, [{
key: "shouldComponentUpdate",
value: function shouldComponentUpdate(nextProps, nextState) {
var _this$props$mediaOpti, _nextProps$mediaOptio;
var hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
var hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection ||
// Patched in asynchronously once media services report the real size.
this.props.node.attrs.width !== nextProps.node.attrs.width || this.props.node.attrs.height !== nextProps.node.attrs.height || this.props.isAIGenerating !== nextProps.isAIGenerating || this.props.maxDimensions.height !== nextProps.maxDimensions.height || this.props.maxDimensions.width !== nextProps.maxDimensions.width || this.props.contextIdentifierProvider !== nextProps.contextIdentifierProvider || this.props.isLoading !== nextProps.isLoading || this.props.isViewOnly !== nextProps.isViewOnly || this.props.mediaProvider !== nextProps.mediaProvider || this.props.syncProvider !== nextProps.syncProvider || this.getDataConsumerSource() !== this.getDataConsumerSource(nextProps) || ((_this$props$mediaOpti = this.props.mediaOptions) === null || _this$props$mediaOpti === void 0 ? void 0 : _this$props$mediaOpti.onMediaRenderEvent) !== ((_nextProps$mediaOptio = nextProps.mediaOptions) === null || _nextProps$mediaOptio === void 0 ? void 0 : _nextProps$mediaOptio.onMediaRenderEvent) || this.props.deferRenderedEventUntilVisible !== nextProps.deferRenderedEventUntilVisible || this.state.previewRenderedFileId !== nextState.previewRenderedFileId || this.state.failedFileId !== nextState.failedFileId || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
return true;
}
return false;
}
}, {
key: "componentDidMount",
value: function () {
var _componentDidMount = (0, _asyncToGenerator2.default)( /*#__PURE__*/_regenerator.default.mark(function _callee3() {
var contextIdentifierProvider, _t2, _t3;
return _regenerator.default.wrap(function (_context3) {
while (1) switch (_context3.prev = _context3.next) {
case 0:
this.handleNewNode(this.props);
this.emitMediaRenderEvent({
type: 'mounted'
});
if (!this.props.deferRenderedEventUntilVisible) {
this.fireRenderedEvent();
}
contextIdentifierProvider = this.props.contextIdentifierProvider;
_t2 = this;
_context3.next = 1;
return contextIdentifierProvider;
case 1:
_t3 = _context3.sent;
_t2.setState.call(_t2, {
contextIdentifierProvider: _t3
});
_context3.next = 2;
return this.setViewMediaClientConfig();
case 2:
case "end":
return _context3.stop();
}
}, _callee3, this);
}));
function componentDidMount() {
return _componentDidMount.apply(this, arguments);
}
return componentDidMount;
}()
}, {
key: "componentWillUnmount",
value: function componentWillUnmount() {
var _this$renderedTracker4, _this$mediaPluginStat3;
(_this$renderedTracker4 = this.renderedTracker) === null || _this$renderedTracker4 === void 0 || _this$renderedTracker4.destroy();
var node = this.props.node;
this.emitMediaRenderEvent({
type: 'unmounted'
});
(_this$mediaPluginStat3 = this.mediaPluginState) === null || _this$mediaPluginStat3 === void 0 || _this$mediaPluginStat3.handleMediaNodeUnmount(node);
if (this.unbindKeyDown && typeof this.unbindKeyDown === 'function') {
this.unbindKeyDown();
}
}
}, {
key: "componentDidUpdate",
value: function componentDidUpdate(prevProps) {
var _prevProps$mediaOptio, _this$props$mediaOpti2, _this$mediaPluginStat5;
this.updateRenderedTracking();
if (prevProps.node.attrs.id !== this.props.node.attrs.id || this.getDataConsumerSource(prevProps) !== this.getDataConsumerSource() || ((_prevProps$mediaOptio = prevProps.mediaOptions) === null || _prevProps$mediaOptio === void 0 ? void 0 : _prevProps$mediaOptio.onMediaRenderEvent) !== ((_this$props$mediaOpti2 = this.props.mediaOptions) === null || _this$props$mediaOpti2 === void 0 ? void 0 : _this$props$mediaOpti2.onMediaRenderEvent)) {
this.emitMediaRenderEvent({
type: 'unmounted'
}, prevProps);
this.emitMediaRenderEvent({
type: 'mounted'
});
}
if (prevProps.node.attrs.id !== this.props.node.attrs.id) {
var _this$mediaPluginStat4;
(_this$mediaPluginStat4 = this.mediaPluginState) === null || _this$mediaPluginStat4 === void 0 || _this$mediaPluginStat4.handleMediaNodeUnmount(prevProps.node);
this.handleNewNode(this.props);
}
(_this$mediaPluginStat5 = this.mediaPluginState) === null || _this$mediaPluginStat5 === void 0 || _this$mediaPluginStat5.updateElement();
this.setViewMediaClientConfig();
// this.videoControlsWrapperRef is null on componentDidMount. We need to wait until it has value
if (this.videoControlsWrapperRef && this.videoControlsWrapperRef.current) {
var _this$mediaPluginStat6;
if (!((_this$mediaPluginStat6 = this.mediaPluginState) !== null && _this$mediaPluginStat6 !== void 0 && _this$mediaPluginStat6.videoControlsWrapperRef)) {
var _this$mediaPluginStat7;
this.bindKeydown();
(_this$mediaPluginStat7 = this.mediaPluginState) === null || _this$mediaPluginStat7 === void 0 || _this$mediaPluginStat7.updateAndDispatch({
videoControlsWrapperRef: this.videoControlsWrapperRef.current
});
}
}
}
}, {
key: "bindKeydown",
value: function bindKeydown() {
var _this2 = this,
_this$videoControlsWr;
var onKeydown = function onKeydown(event) {
if (event.key === 'Tab') {
var _this2$videoControlsW;
// Add focus trap for controls panel
var firstElement;
var lastElement;
var focusableElements = (_this2$videoControlsW = _this2.videoControlsWrapperRef) === null || _this2$videoControlsW === void 0 || (_this2$videoControlsW = _this2$videoControlsW.current) === null || _this2$videoControlsW === void 0 ? void 0 : _this2$videoControlsW.querySelectorAll('button, input, [tabindex]:not([tabindex="-1"])');
if (focusableElements && focusableElements.length) {
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
firstElement = focusableElements[0];
// Ignored via go/ees005
// eslint-disable-next-line @atlaskit/editor/no-as-casting
lastElement = focusableElements[focusableElements.length - 1];
if (event.shiftKey && document.activeElement === firstElement) {
event.preventDefault();
lastElement.focus();
} else if (!event.shiftKey && document.activeElement === lastElement) {
var _firstElement;
event.preventDefault();
(_firstElement = firstElement) === null || _firstElement === void 0 || _firstElement.focus();
}
}
}
};
if ((_this$videoControlsWr = this.videoControlsWrapperRef) !== null && _this$videoControlsWr !== void 0 && _this$videoControlsWr.current) {
this.unbindKeyDown = (0, _bindEventListener.bind)(this.videoControlsWrapperRef.current, {
type: 'keydown',
listener: onKeydown,
options: {
capture: true,
passive: false
}
});
}
}
}, {
key: "hasGeneratedMediaMotion",
get:
/**
* Surface opt-in first, so surfaces that never ask for the motion are not exposed to the
* gate. The node type is checked after it, so every node on an opted-in surface counts
* towards the same exposure population.
*/
function get() {
var _this$props$mediaOpti3;
return !!((_this$props$mediaOpti3 = this.props.mediaOptions) !== null && _this$props$mediaOpti3 !== void 0 && _this$props$mediaOpti3.allowAIGeneratedMediaMotion) && (0, _fg.fg)('aifc_page_create_defer_generated_visuals') &&
// File-backed only: `ExternalImageCard` reports neither its rendered preview nor its
// dimensions, so an external node could only ever open on the reveal's timeout.
this.props.node.attrs.type !== 'external';
}
}, {
key: "render",
value: function render() {
var _this$props5 = this.props,
node = _this$props5.node,
selected = _this$props5.selected,
originalDimensions = _this$props5.originalDimensions,
isLoading = _this$props5.isLoading,
maxDimensions = _this$props5.maxDimensions,
mediaOptions = _this$props5.mediaOptions;
var borderMark = node.marks.find(function (m) {
return m.type.name === 'border';
});
var _this$state = this.state,
viewMediaClientConfig = _this$state.viewMediaClientConfig,
viewAndUploadMediaClientConfig = _this$state.viewAndUploadMediaClientConfig,
contextIdentifierProvider = _this$state.contextIdentifierProvider;
var _node$attrs2 = node.attrs,
id = _node$attrs2.id,
type = _node$attrs2.type,
collection = _node$attrs2.collection,
url = _node$attrs2.url,
alt = _node$attrs2.alt;
// Check if we have any media client config available (syncProvider, state, or upload config)
var hasNoMediaClientConfig = !viewMediaClientConfig && ((0, _fg.fg)('platform_media_video_captions') ? !viewAndUploadMediaClientConfig : true);
var hasGeneratedMediaMotion = this.hasGeneratedMediaMotion;
// Opening before the real dimensions land would animate to the wrong height and snap
// when they correct. Nodes that never get them fall through to the reveal's timeout.
//
// A terminal error is readiness too: the card is already showing its error treatment,
// so the space opens onto that rather than hiding it until the timeout.
var isRevealReady = this.state.failedFileId === id || this.state.previewRenderedFileId === id && !!node.attrs.width && !!node.attrs.height;
if (isLoading || type !== 'external' && hasNoMediaClientConfig) {
return /*#__PURE__*/_react.default.createElement(_GeneratedMediaReveal.GeneratedMediaReveal, {
isEnabled: hasGeneratedMediaMotion,
isReady: isRevealReady,
mediaKey: id
}, /*#__PURE__*/_react.default.createElement(_styles.MediaCardWrapper, {
mediaElementRef: this.setMediaElement,
dimensions: originalDimensions,
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
selected: selected
}, /*#__PURE__*/_react.default.createElement(_cardLoading.CardLoading, {
interactionName: "editor-media-card-loading"
})));
}
var contextId = contextIdentifierProvider && contextIdentifierProvider.objectId;
var identifier = type === 'external' ? {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
dataURI: url,
name: url,
mediaItemType: 'external-image'
} : {
id: id,
mediaItemType: 'file',
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
collectionName: collection
};
var resolvedViewAndUploadMediaClientConfig = (0, _fg.fg)('platform_media_video_captions') ? viewAndUploadMediaClientConfig : undefined;
// mediaClientConfig is not needed for "external" case. So we have to cheat here.
// there is a possibility mediaClientConfig will be part of a identifier,
// so this might be not an issue
var mediaClientConfig = resolvedViewAndUploadMediaClientConfig || viewMediaClientConfig || {
// Ignored via go/ees005
// eslint-disable-next-line @typescript-eslint/no-explicit-any
authProvider: function authProvider() {
return {};
}
};
var ssr = process.env.REACT_SSR ? 'server' : 'client';
return /*#__PURE__*/_react.default.createElement(_GeneratedMediaReveal.GeneratedMediaReveal, {
isEnabled: hasGeneratedMediaMotion,
isReady: isRevealReady,
mediaKey: id
}, /*#__PURE__*/_react.default.createElement(_styles.MediaCardWrapper, {
mediaElementRef: this.setMediaElement,
dimensions: originalDimensions,
onContextMenu: this.selectMediaSingle,
onFocus: this.warmMediaViewer,
onMouseEnter: this.warmMediaViewer,
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
selected: selected
}, /*#__PURE__*/_react.default.createElement(_AnalyticsContext.default
// eslint-disable-next-line @atlassian/perf-linting/no-unstable-inline-props -- Ignored via go/ees017 (to be fixed)
, {
data: (0, _defineProperty2.default)({}, _MediaAnalyticsContext.MEDIA_CONTEXT, {
border: !!borderMark,
// Defined for media with a dataConsumer mark; prefers a Remix source when present.
// Remix format: "remix:{type}:{subtype}" e.g. "remix:infographic:corporate-doodle"
remixSource: this.getDataConsumerSource()
})
}, /*#__PURE__*/_react.default.createElement(_cardLoader.default, {
mediaClientConfig: mediaClientConfig,
resizeMode: "stretchy-fit",
dimensions: maxDimensions,
originalDimensions: originalDimensions,
identifier: identifier,
selectable: true,
selected: selected,
disableOverlay: true,
onFullscreenChange: this.onFullscreenChange,
onClick: this.selectMediaSingleFromCard,
useInlinePlayer: mediaOptions && mediaOptions.allowLazyLoading,
isLazy: mediaOptions && mediaOptions.allowLazyLoading,
featureFlags: mediaOptions && mediaOptions.featureFlags,
contextId: contextId,
alt: alt,
videoControlsWrapperRef: this.videoControlsWrapperRef,
ssr: ssr,
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig, this.props.isViewOnly),
isAIGenerating: !!this.props.isAIGenerating,
hasLoadingMotion: hasGeneratedMediaMotion,
onPreviewRender: this.onPreviewRender,
fallbackMediaNameFetcher: mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.fallbackMediaNameFetcher,
onError: this.onError
}))));
}
}]);
}(_react.Component);
var _default_1 = (0, _imageLoader.withImageLoader)(MediaNode);
var _default = exports.default = _default_1;