@atlaskit/editor-plugin-media 20.0.4 → 20.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/dist/cjs/nodeviews/mediaNodeView/media.js +145 -32
- package/dist/cjs/ui/GeneratedMediaReveal.compiled.css +12 -0
- package/dist/cjs/ui/GeneratedMediaReveal.js +124 -0
- package/dist/es2019/nodeviews/mediaNodeView/media.js +107 -7
- package/dist/es2019/ui/GeneratedMediaReveal.compiled.css +12 -0
- package/dist/es2019/ui/GeneratedMediaReveal.js +101 -0
- package/dist/esm/nodeviews/mediaNodeView/media.js +144 -31
- package/dist/esm/ui/GeneratedMediaReveal.compiled.css +12 -0
- package/dist/esm/ui/GeneratedMediaReveal.js +116 -0
- package/dist/types/nodeviews/mediaNodeView/media.d.ts +21 -0
- package/dist/types/types/index.d.ts +9 -0
- package/dist/types/ui/GeneratedMediaReveal.d.ts +25 -0
- package/package.json +8 -2
|
@@ -13,8 +13,11 @@ import { Card, CardLoading } from '@atlaskit/media-card';
|
|
|
13
13
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
14
14
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
15
15
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
16
|
+
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
16
17
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/editor-experiment';
|
|
17
18
|
import { stateKey as mediaStateKey } from '../../pm-plugins/plugin-key';
|
|
19
|
+
import { GeneratedMediaReveal } from '../../ui/GeneratedMediaReveal';
|
|
20
|
+
import { createMediaNodeUpdater } from '../mediaNodeUpdater';
|
|
18
21
|
import { MediaCardWrapper } from '../styles';
|
|
19
22
|
|
|
20
23
|
// This is being used by DropPlaceholder now
|
|
@@ -103,11 +106,59 @@ export class MediaNode extends Component {
|
|
|
103
106
|
canUpdateVideoCaptions: fg('platform_media_video_captions') ? !!viewAndUploadMediaClientConfig && !isViewOnly : false
|
|
104
107
|
};
|
|
105
108
|
}));
|
|
109
|
+
/**
|
|
110
|
+
* Media written straight into the document arrives without intrinsic dimensions, and the
|
|
111
|
+
* fetch that would supply them resolves `false` while the file is still processing. Only
|
|
112
|
+
* editor-driven uploads get a second attempt, from the plugin's media-state listener, so
|
|
113
|
+
* otherwise the card would keep its placeholder ratio forever. The preview rendering is the
|
|
114
|
+
* one moment the file is guaranteed ready, so the fetch is retried there.
|
|
115
|
+
*/
|
|
116
|
+
_defineProperty(this, "fetchMissingDimensions", async () => {
|
|
117
|
+
var _pluginInjectionApi$w, _pluginInjectionApi$w2;
|
|
118
|
+
const {
|
|
119
|
+
node,
|
|
120
|
+
view,
|
|
121
|
+
mediaProvider,
|
|
122
|
+
contextIdentifierProvider,
|
|
123
|
+
mediaOptions,
|
|
124
|
+
pluginInjectionApi
|
|
125
|
+
} = this.props;
|
|
126
|
+
if (node.attrs.type === 'external' || !node.attrs.id || node.attrs.width && node.attrs.height) {
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
const updater = createMediaNodeUpdater({
|
|
130
|
+
view,
|
|
131
|
+
mediaProvider,
|
|
132
|
+
contextIdentifierProvider,
|
|
133
|
+
node,
|
|
134
|
+
// Carries `allowRemoteDimensionsFetch`, so surfaces that cannot reach the media
|
|
135
|
+
// APIs get the default dimensions back instead of a request.
|
|
136
|
+
mediaOptions,
|
|
137
|
+
isMediaSingle: true,
|
|
138
|
+
lineLength: pluginInjectionApi === null || pluginInjectionApi === void 0 ? void 0 : (_pluginInjectionApi$w = pluginInjectionApi.width) === null || _pluginInjectionApi$w === void 0 ? void 0 : (_pluginInjectionApi$w2 = _pluginInjectionApi$w.sharedState.currentState()) === null || _pluginInjectionApi$w2 === void 0 ? void 0 : _pluginInjectionApi$w2.lineLength
|
|
139
|
+
});
|
|
140
|
+
try {
|
|
141
|
+
const dimensions = await updater.getRemoteDimensions();
|
|
142
|
+
if (dimensions) {
|
|
143
|
+
updater.updateDimensions(dimensions);
|
|
144
|
+
}
|
|
145
|
+
} catch {
|
|
146
|
+
// Leaves the card at its placeholder ratio, which the reveal's own timeout covers.
|
|
147
|
+
}
|
|
148
|
+
});
|
|
106
149
|
_defineProperty(this, "onPreviewRender", fileId => {
|
|
107
150
|
this.emitMediaRenderEvent({
|
|
108
151
|
renderedMediaId: fileId,
|
|
109
152
|
type: 'preview-rendered'
|
|
110
153
|
});
|
|
154
|
+
// Only tracked where the reveal uses it. Every media card in every editor calls this,
|
|
155
|
+
// and the extra render it would otherwise trigger is not free.
|
|
156
|
+
if (this.hasGeneratedMediaMotion && this.state.previewRenderedFileId !== fileId) {
|
|
157
|
+
this.setState({
|
|
158
|
+
previewRenderedFileId: fileId
|
|
159
|
+
});
|
|
160
|
+
this.fetchMissingDimensions();
|
|
161
|
+
}
|
|
111
162
|
if (isExperimentEnabled('aifc_page_create_with_rovo_include_infographics')) {
|
|
112
163
|
var _this$props$pluginInj2, _this$props$pluginInj3;
|
|
113
164
|
(_this$props$pluginInj2 = this.props.pluginInjectionApi) === null || _this$props$pluginInj2 === void 0 ? void 0 : (_this$props$pluginInj3 = _this$props$pluginInj2.core) === null || _this$props$pluginInj3 === void 0 ? void 0 : _this$props$pluginInj3.actions.execute(({
|
|
@@ -120,10 +171,22 @@ export class MediaNode extends Component {
|
|
|
120
171
|
});
|
|
121
172
|
_defineProperty(this, "onError", reason => {
|
|
122
173
|
var _this$props$api;
|
|
174
|
+
// The card is already showing its error treatment, so let the reveal open onto
|
|
175
|
+
// that instead of waiting out its timeout.
|
|
176
|
+
this.setState({
|
|
177
|
+
failedFileId: this.props.node.attrs.id
|
|
178
|
+
});
|
|
123
179
|
this.emitMediaRenderEvent({
|
|
124
180
|
reason,
|
|
125
181
|
type: 'error'
|
|
126
182
|
});
|
|
183
|
+
|
|
184
|
+
// `getMediaRenderErrorHandler` also wires this for surfaces the analytics
|
|
185
|
+
// experiment does not cover, so re-read without exposure — the render-time read
|
|
186
|
+
// owns that — to keep the dispatch inside the experiment's own cohort.
|
|
187
|
+
if (!expValEqualsNoExposure('platform_editor_media_error_analytics', 'isEnabled', true)) {
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
127
190
|
const nestedUnder = this.getNestedUnder();
|
|
128
191
|
(_this$props$api = this.props.api) === null || _this$props$api === void 0 ? void 0 : _this$props$api.media.actions.handleMediaNodeRenderError(this.props.node, reason, nestedUnder);
|
|
129
192
|
});
|
|
@@ -138,6 +201,12 @@ export class MediaNode extends Component {
|
|
|
138
201
|
if (expValEquals('platform_editor_media_error_analytics', 'isEnabled', true)) {
|
|
139
202
|
return this.onError;
|
|
140
203
|
}
|
|
204
|
+
// The reveal needs terminal errors too, so it can open onto the card's error treatment
|
|
205
|
+
// rather than waiting out its timeout. `onError` keeps the analytics dispatch itself
|
|
206
|
+
// inside the experiment's cohort.
|
|
207
|
+
if (this.hasGeneratedMediaMotion) {
|
|
208
|
+
return this.onError;
|
|
209
|
+
}
|
|
141
210
|
return (_this$props$mediaOpti = this.props.mediaOptions) !== null && _this$props$mediaOpti !== void 0 && _this$props$mediaOpti.onMediaRenderEvent ? this.onRemixRenderError : undefined;
|
|
142
211
|
});
|
|
143
212
|
/**
|
|
@@ -178,10 +247,10 @@ export class MediaNode extends Component {
|
|
|
178
247
|
(_this$mediaPluginStat2 = this.mediaPluginState) === null || _this$mediaPluginStat2 === void 0 ? void 0 : _this$mediaPluginStat2.handleMediaNodeMount(node, () => this.props.getPos());
|
|
179
248
|
});
|
|
180
249
|
const {
|
|
181
|
-
view,
|
|
250
|
+
view: _view,
|
|
182
251
|
syncProvider
|
|
183
252
|
} = this.props;
|
|
184
|
-
this.mediaPluginState = mediaStateKey.getState(
|
|
253
|
+
this.mediaPluginState = mediaStateKey.getState(_view.state);
|
|
185
254
|
|
|
186
255
|
// Initialize state from syncProvider (available on both server and client for SSR)
|
|
187
256
|
if (syncProvider) {
|
|
@@ -195,7 +264,9 @@ export class MediaNode extends Component {
|
|
|
195
264
|
var _this$props$mediaOpti2, _nextProps$mediaOptio;
|
|
196
265
|
const hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
|
|
197
266
|
const hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
|
|
198
|
-
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection ||
|
|
267
|
+
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection ||
|
|
268
|
+
// Patched in asynchronously once media services report the real size.
|
|
269
|
+
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.isCwrAIGenerating !== nextProps.isCwrAIGenerating || 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$mediaOpti2 = this.props.mediaOptions) === null || _this$props$mediaOpti2 === void 0 ? void 0 : _this$props$mediaOpti2.onMediaRenderEvent) !== ((_nextProps$mediaOptio = nextProps.mediaOptions) === null || _nextProps$mediaOptio === void 0 ? void 0 : _nextProps$mediaOptio.onMediaRenderEvent) || this.state.previewRenderedFileId !== nextState.previewRenderedFileId || this.state.failedFileId !== nextState.failedFileId || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
199
270
|
return true;
|
|
200
271
|
}
|
|
201
272
|
return false;
|
|
@@ -314,6 +385,18 @@ export class MediaNode extends Component {
|
|
|
314
385
|
});
|
|
315
386
|
}
|
|
316
387
|
}
|
|
388
|
+
/**
|
|
389
|
+
* Surface opt-in first, so surfaces that never ask for the motion are not exposed to the
|
|
390
|
+
* gate. The node type is checked after it, so every node on an opted-in surface counts
|
|
391
|
+
* towards the same exposure population.
|
|
392
|
+
*/
|
|
393
|
+
get hasGeneratedMediaMotion() {
|
|
394
|
+
var _this$props$mediaOpti4;
|
|
395
|
+
return !!((_this$props$mediaOpti4 = this.props.mediaOptions) !== null && _this$props$mediaOpti4 !== void 0 && _this$props$mediaOpti4.allowAIGeneratedMediaMotion) && fg('aifc_page_create_defer_generated_visuals') &&
|
|
396
|
+
// File-backed only: `ExternalImageCard` reports neither its rendered preview nor its
|
|
397
|
+
// dimensions, so an external node could only ever open on the reveal's timeout.
|
|
398
|
+
this.props.node.attrs.type !== 'external';
|
|
399
|
+
}
|
|
317
400
|
render() {
|
|
318
401
|
var _dataConsumerMark$att2;
|
|
319
402
|
const {
|
|
@@ -341,14 +424,26 @@ export class MediaNode extends Component {
|
|
|
341
424
|
|
|
342
425
|
// Check if we have any media client config available (syncProvider, state, or upload config)
|
|
343
426
|
const hasNoMediaClientConfig = !viewMediaClientConfig && (fg('platform_media_video_captions') ? !viewAndUploadMediaClientConfig : true);
|
|
427
|
+
const hasGeneratedMediaMotion = this.hasGeneratedMediaMotion;
|
|
428
|
+
|
|
429
|
+
// Opening before the real dimensions land would animate to the wrong height and snap
|
|
430
|
+
// when they correct. Nodes that never get them fall through to the reveal's timeout.
|
|
431
|
+
//
|
|
432
|
+
// A terminal error is readiness too: the card is already showing its error treatment,
|
|
433
|
+
// so the space opens onto that rather than hiding it until the timeout.
|
|
434
|
+
const isRevealReady = this.state.failedFileId === id || this.state.previewRenderedFileId === id && !!node.attrs.width && !!node.attrs.height;
|
|
344
435
|
if (isLoading || type !== 'external' && hasNoMediaClientConfig) {
|
|
345
|
-
return /*#__PURE__*/React.createElement(
|
|
436
|
+
return /*#__PURE__*/React.createElement(GeneratedMediaReveal, {
|
|
437
|
+
isEnabled: hasGeneratedMediaMotion,
|
|
438
|
+
isReady: isRevealReady,
|
|
439
|
+
mediaKey: id
|
|
440
|
+
}, /*#__PURE__*/React.createElement(MediaCardWrapper, {
|
|
346
441
|
dimensions: originalDimensions,
|
|
347
442
|
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
|
|
348
443
|
selected: selected
|
|
349
444
|
}, /*#__PURE__*/React.createElement(CardLoading, {
|
|
350
445
|
interactionName: "editor-media-card-loading"
|
|
351
|
-
}));
|
|
446
|
+
})));
|
|
352
447
|
}
|
|
353
448
|
const contextId = contextIdentifierProvider && contextIdentifierProvider.objectId;
|
|
354
449
|
const identifier = type === 'external' ? {
|
|
@@ -379,7 +474,11 @@ export class MediaNode extends Component {
|
|
|
379
474
|
// generic media type icon isn't shown while the image streams in. Gated on the
|
|
380
475
|
// existing infographics experiment.
|
|
381
476
|
const isCWR = !!this.props.isCwrAIGenerating && isExperimentEnabled('aifc_page_create_with_rovo_include_infographics');
|
|
382
|
-
return /*#__PURE__*/React.createElement(
|
|
477
|
+
return /*#__PURE__*/React.createElement(GeneratedMediaReveal, {
|
|
478
|
+
isEnabled: hasGeneratedMediaMotion,
|
|
479
|
+
isReady: isRevealReady,
|
|
480
|
+
mediaKey: id
|
|
481
|
+
}, /*#__PURE__*/React.createElement(MediaCardWrapper, {
|
|
383
482
|
dimensions: originalDimensions,
|
|
384
483
|
onContextMenu: this.selectMediaSingle,
|
|
385
484
|
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
|
|
@@ -416,10 +515,11 @@ export class MediaNode extends Component {
|
|
|
416
515
|
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig, this.props.isViewOnly),
|
|
417
516
|
isAIGenerating: !!this.props.isAIGenerating,
|
|
418
517
|
isCWR: isCWR,
|
|
518
|
+
hasLoadingMotion: hasGeneratedMediaMotion,
|
|
419
519
|
onPreviewRender: this.onPreviewRender,
|
|
420
520
|
fallbackMediaNameFetcher: mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.fallbackMediaNameFetcher,
|
|
421
521
|
onError: this.getMediaRenderErrorHandler()
|
|
422
|
-
})));
|
|
522
|
+
}))));
|
|
423
523
|
}
|
|
424
524
|
}
|
|
425
525
|
const _default_1 = withImageLoader(MediaNode);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
._18m915vq{overflow-y:hidden}
|
|
2
|
+
._1e0c11p5{display:grid}
|
|
3
|
+
._1o51eoah{animation-fill-mode:backwards}
|
|
4
|
+
._1pglindk{animation-timing-function:var(--ds-easing-out-practical,cubic-bezier(.4,1,.6,1))}
|
|
5
|
+
._1reo15vq{overflow-x:hidden}
|
|
6
|
+
._1tkeidpf{min-height:0}
|
|
7
|
+
._2z0563bf{grid-template-rows:0fr}
|
|
8
|
+
._2z05hkll{grid-template-rows:1fr}
|
|
9
|
+
._5sag15uc{animation-duration:var(--ds-duration-xlong,.4s)}
|
|
10
|
+
._j7hq8gkt{animation-name:kaq7wke}
|
|
11
|
+
@keyframes kaq7wke{0%{grid-template-rows:0fr}to{grid-template-rows:1fr}}
|
|
12
|
+
@media (prefers-reduced-motion:reduce){._1bumglyw{animation:none}}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/* GeneratedMediaReveal.tsx generated by @compiled/babel-plugin v3.0.1 */
|
|
2
|
+
import "./GeneratedMediaReveal.compiled.css";
|
|
3
|
+
import { ax, ix } from "@compiled/react/runtime";
|
|
4
|
+
import React, { useEffect, useState } from 'react';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Only `collapsed` and `revealing` size and clip the content. `open` drops back to plain
|
|
8
|
+
* block layout, so the reveal leaves nothing behind for selection outlines, resize handles
|
|
9
|
+
* or badges to collide with.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
/** Which path opened the node. Surfaced as a data attribute for debugging. */
|
|
13
|
+
|
|
14
|
+
// A `0fr -> 1fr` grid row opens out to the content's natural height without anyone having to
|
|
15
|
+
// measure it, which matters because the card's height comes from an aspect-ratio padding
|
|
16
|
+
// further down the tree.
|
|
17
|
+
const openRow = null;
|
|
18
|
+
const collapsedStyles = null;
|
|
19
|
+
const revealStyles = null;
|
|
20
|
+
const clippedStyles = null;
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Fail-safe, not a normal path: opens the node even if the preview never reports itself
|
|
24
|
+
* rendered, so a failed or hanging image still ends up visible rather than staying collapsed.
|
|
25
|
+
* Also covers lazy loading, where a card collapsed to zero height may never satisfy the
|
|
26
|
+
* IntersectionObserver that triggers its fetch.
|
|
27
|
+
*
|
|
28
|
+
* Deliberately generous, because opening early is worse than opening late — the space then
|
|
29
|
+
* reveals the card's own loading treatment instead of the image, and an AI-generated image has
|
|
30
|
+
* to be generated, uploaded, processed and fetched first. `data-media-reveal-trigger` shows
|
|
31
|
+
* which path fired; `timeout` in normal use means the readiness signal is broken.
|
|
32
|
+
*/
|
|
33
|
+
const READY_TIMEOUT_MS = 30_000;
|
|
34
|
+
|
|
35
|
+
// Backstop for reduced motion and browsers that don't interpolate grid-template-rows, where no
|
|
36
|
+
// animationend fires. Must stay comfortably longer than the reveal, or it cuts it off part way.
|
|
37
|
+
const REVEAL_TIMEOUT_MS = 1000;
|
|
38
|
+
/**
|
|
39
|
+
* Opens the document out to make room for a media node once its preview is ready, rather than
|
|
40
|
+
* having the node snap into place at full height. For AI-generated media in the Create with
|
|
41
|
+
* Rovo preview, where images are inserted after the surrounding content has streamed in:
|
|
42
|
+
* holding the node closed until the preview has rendered means the space opens exactly once,
|
|
43
|
+
* with the image already fading in, so there is no placeholder to look at in between.
|
|
44
|
+
*/
|
|
45
|
+
export const GeneratedMediaReveal = ({
|
|
46
|
+
children,
|
|
47
|
+
isEnabled,
|
|
48
|
+
isReady,
|
|
49
|
+
mediaKey
|
|
50
|
+
}) => {
|
|
51
|
+
const [state, setState] = useState('collapsed');
|
|
52
|
+
const [trigger, setTrigger] = useState();
|
|
53
|
+
|
|
54
|
+
// Reset during render rather than remounting on a `key`, which would also remount the card
|
|
55
|
+
// below and restart its fetch. The reveal's effect only runs while `collapsed`, so without
|
|
56
|
+
// this a replacement would appear at once, having inherited the previous file's `open`.
|
|
57
|
+
const [renderedMediaKey, setRenderedMediaKey] = useState(mediaKey);
|
|
58
|
+
if (mediaKey !== renderedMediaKey) {
|
|
59
|
+
setRenderedMediaKey(mediaKey);
|
|
60
|
+
setState('collapsed');
|
|
61
|
+
setTrigger(undefined);
|
|
62
|
+
}
|
|
63
|
+
useEffect(() => {
|
|
64
|
+
if (!isEnabled || state !== 'collapsed') {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
if (isReady) {
|
|
68
|
+
setTrigger('ready');
|
|
69
|
+
setState('revealing');
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
const timer = setTimeout(() => {
|
|
73
|
+
setTrigger('timeout');
|
|
74
|
+
setState('revealing');
|
|
75
|
+
}, READY_TIMEOUT_MS);
|
|
76
|
+
return () => clearTimeout(timer);
|
|
77
|
+
}, [isEnabled, isReady, state]);
|
|
78
|
+
useEffect(() => {
|
|
79
|
+
if (state !== 'revealing') {
|
|
80
|
+
return;
|
|
81
|
+
}
|
|
82
|
+
const timer = setTimeout(() => setState('open'), REVEAL_TIMEOUT_MS);
|
|
83
|
+
return () => clearTimeout(timer);
|
|
84
|
+
}, [state]);
|
|
85
|
+
if (!isEnabled) {
|
|
86
|
+
return /*#__PURE__*/React.createElement(React.Fragment, null, children);
|
|
87
|
+
}
|
|
88
|
+
return /*#__PURE__*/React.createElement("div", {
|
|
89
|
+
"data-media-reveal": state,
|
|
90
|
+
"data-media-reveal-trigger": trigger,
|
|
91
|
+
onAnimationEnd: event => {
|
|
92
|
+
// The media card runs animations of its own, which bubble through here.
|
|
93
|
+
if (event.target === event.currentTarget) {
|
|
94
|
+
setState('open');
|
|
95
|
+
}
|
|
96
|
+
},
|
|
97
|
+
className: ax([state === 'collapsed' && "_1e0c11p5 _2z0563bf", state === 'revealing' && "", state === 'revealing' && "_1e0c11p5 _2z05hkll _j7hq8gkt _5sag15uc _1pglindk _1o51eoah _1bumglyw"])
|
|
98
|
+
}, /*#__PURE__*/React.createElement("div", {
|
|
99
|
+
className: ax([state !== 'open' && "_1reo15vq _18m915vq _1tkeidpf"])
|
|
100
|
+
}, children));
|
|
101
|
+
};
|
|
@@ -24,8 +24,11 @@ import { Card, CardLoading } from '@atlaskit/media-card';
|
|
|
24
24
|
import { isExperimentEnabled } from '@atlaskit/platform-feature-experiments/is-experiment-enabled';
|
|
25
25
|
import { fg } from '@atlaskit/platform-feature-flags/fg';
|
|
26
26
|
import { expValEquals } from '@atlaskit/tmp-editor-statsig/exp-val-equals';
|
|
27
|
+
import { expValEqualsNoExposure } from '@atlaskit/tmp-editor-statsig/exp-val-equals-no-exposure';
|
|
27
28
|
import { editorExperiment } from '@atlaskit/tmp-editor-statsig/editor-experiment';
|
|
28
29
|
import { stateKey as mediaStateKey } from '../../pm-plugins/plugin-key';
|
|
30
|
+
import { GeneratedMediaReveal } from '../../ui/GeneratedMediaReveal';
|
|
31
|
+
import { createMediaNodeUpdater } from '../mediaNodeUpdater';
|
|
29
32
|
import { MediaCardWrapper } from '../styles';
|
|
30
33
|
|
|
31
34
|
// This is being used by DropPlaceholder now
|
|
@@ -130,15 +133,73 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
130
133
|
canUpdateVideoCaptions: fg('platform_media_video_captions') ? !!viewAndUploadMediaClientConfig && !isViewOnly : false
|
|
131
134
|
};
|
|
132
135
|
}));
|
|
136
|
+
/**
|
|
137
|
+
* Media written straight into the document arrives without intrinsic dimensions, and the
|
|
138
|
+
* fetch that would supply them resolves `false` while the file is still processing. Only
|
|
139
|
+
* editor-driven uploads get a second attempt, from the plugin's media-state listener, so
|
|
140
|
+
* otherwise the card would keep its placeholder ratio forever. The preview rendering is the
|
|
141
|
+
* one moment the file is guaranteed ready, so the fetch is retried there.
|
|
142
|
+
*/
|
|
143
|
+
_defineProperty(_this, "fetchMissingDimensions", /*#__PURE__*/_asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee2() {
|
|
144
|
+
var _pluginInjectionApi$w;
|
|
145
|
+
var _this$props, node, view, mediaProvider, contextIdentifierProvider, mediaOptions, pluginInjectionApi, updater, dimensions, _t;
|
|
146
|
+
return _regeneratorRuntime.wrap(function (_context2) {
|
|
147
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
148
|
+
case 0:
|
|
149
|
+
_this$props = _this.props, node = _this$props.node, view = _this$props.view, mediaProvider = _this$props.mediaProvider, contextIdentifierProvider = _this$props.contextIdentifierProvider, mediaOptions = _this$props.mediaOptions, pluginInjectionApi = _this$props.pluginInjectionApi;
|
|
150
|
+
if (!(node.attrs.type === 'external' || !node.attrs.id || node.attrs.width && node.attrs.height)) {
|
|
151
|
+
_context2.next = 1;
|
|
152
|
+
break;
|
|
153
|
+
}
|
|
154
|
+
return _context2.abrupt("return");
|
|
155
|
+
case 1:
|
|
156
|
+
updater = createMediaNodeUpdater({
|
|
157
|
+
view: view,
|
|
158
|
+
mediaProvider: mediaProvider,
|
|
159
|
+
contextIdentifierProvider: contextIdentifierProvider,
|
|
160
|
+
node: node,
|
|
161
|
+
// Carries `allowRemoteDimensionsFetch`, so surfaces that cannot reach the media
|
|
162
|
+
// APIs get the default dimensions back instead of a request.
|
|
163
|
+
mediaOptions: mediaOptions,
|
|
164
|
+
isMediaSingle: true,
|
|
165
|
+
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
|
|
166
|
+
});
|
|
167
|
+
_context2.prev = 2;
|
|
168
|
+
_context2.next = 3;
|
|
169
|
+
return updater.getRemoteDimensions();
|
|
170
|
+
case 3:
|
|
171
|
+
dimensions = _context2.sent;
|
|
172
|
+
if (dimensions) {
|
|
173
|
+
updater.updateDimensions(dimensions);
|
|
174
|
+
}
|
|
175
|
+
_context2.next = 5;
|
|
176
|
+
break;
|
|
177
|
+
case 4:
|
|
178
|
+
_context2.prev = 4;
|
|
179
|
+
_t = _context2["catch"](2);
|
|
180
|
+
case 5:
|
|
181
|
+
case "end":
|
|
182
|
+
return _context2.stop();
|
|
183
|
+
}
|
|
184
|
+
}, _callee2, null, [[2, 4]]);
|
|
185
|
+
})));
|
|
133
186
|
_defineProperty(_this, "onPreviewRender", function (fileId) {
|
|
134
187
|
_this.emitMediaRenderEvent({
|
|
135
188
|
renderedMediaId: fileId,
|
|
136
189
|
type: 'preview-rendered'
|
|
137
190
|
});
|
|
191
|
+
// Only tracked where the reveal uses it. Every media card in every editor calls this,
|
|
192
|
+
// and the extra render it would otherwise trigger is not free.
|
|
193
|
+
if (_this.hasGeneratedMediaMotion && _this.state.previewRenderedFileId !== fileId) {
|
|
194
|
+
_this.setState({
|
|
195
|
+
previewRenderedFileId: fileId
|
|
196
|
+
});
|
|
197
|
+
_this.fetchMissingDimensions();
|
|
198
|
+
}
|
|
138
199
|
if (isExperimentEnabled('aifc_page_create_with_rovo_include_infographics')) {
|
|
139
200
|
var _this$props$pluginInj2;
|
|
140
|
-
(_this$props$pluginInj2 = _this.props.pluginInjectionApi) === null || _this$props$pluginInj2 === void 0 || (_this$props$pluginInj2 = _this$props$pluginInj2.core) === null || _this$props$pluginInj2 === void 0 || _this$props$pluginInj2.actions.execute(function (
|
|
141
|
-
var tr =
|
|
201
|
+
(_this$props$pluginInj2 = _this.props.pluginInjectionApi) === null || _this$props$pluginInj2 === void 0 || (_this$props$pluginInj2 = _this$props$pluginInj2.core) === null || _this$props$pluginInj2 === void 0 || _this$props$pluginInj2.actions.execute(function (_ref4) {
|
|
202
|
+
var tr = _ref4.tr;
|
|
142
203
|
return tr.setMeta(mediaStateKey, {
|
|
143
204
|
type: 'PREVIEW_RENDERED',
|
|
144
205
|
fileId: fileId
|
|
@@ -148,10 +209,22 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
148
209
|
});
|
|
149
210
|
_defineProperty(_this, "onError", function (reason) {
|
|
150
211
|
var _this$props$api;
|
|
212
|
+
// The card is already showing its error treatment, so let the reveal open onto
|
|
213
|
+
// that instead of waiting out its timeout.
|
|
214
|
+
_this.setState({
|
|
215
|
+
failedFileId: _this.props.node.attrs.id
|
|
216
|
+
});
|
|
151
217
|
_this.emitMediaRenderEvent({
|
|
152
218
|
reason: reason,
|
|
153
219
|
type: 'error'
|
|
154
220
|
});
|
|
221
|
+
|
|
222
|
+
// `getMediaRenderErrorHandler` also wires this for surfaces the analytics
|
|
223
|
+
// experiment does not cover, so re-read without exposure — the render-time read
|
|
224
|
+
// owns that — to keep the dispatch inside the experiment's own cohort.
|
|
225
|
+
if (!expValEqualsNoExposure('platform_editor_media_error_analytics', 'isEnabled', true)) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
155
228
|
var nestedUnder = _this.getNestedUnder();
|
|
156
229
|
(_this$props$api = _this.props.api) === null || _this$props$api === void 0 || _this$props$api.media.actions.handleMediaNodeRenderError(_this.props.node, reason, nestedUnder);
|
|
157
230
|
});
|
|
@@ -166,6 +239,12 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
166
239
|
if (expValEquals('platform_editor_media_error_analytics', 'isEnabled', true)) {
|
|
167
240
|
return _this.onError;
|
|
168
241
|
}
|
|
242
|
+
// The reveal needs terminal errors too, so it can open onto the card's error treatment
|
|
243
|
+
// rather than waiting out its timeout. `onError` keeps the analytics dispatch itself
|
|
244
|
+
// inside the experiment's cohort.
|
|
245
|
+
if (_this.hasGeneratedMediaMotion) {
|
|
246
|
+
return _this.onError;
|
|
247
|
+
}
|
|
169
248
|
return (_this$props$mediaOpti = _this.props.mediaOptions) !== null && _this$props$mediaOpti !== void 0 && _this$props$mediaOpti.onMediaRenderEvent ? _this.onRemixRenderError : undefined;
|
|
170
249
|
});
|
|
171
250
|
/**
|
|
@@ -204,10 +283,10 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
204
283
|
return _this.props.getPos();
|
|
205
284
|
});
|
|
206
285
|
});
|
|
207
|
-
var _this$
|
|
208
|
-
|
|
209
|
-
syncProvider = _this$
|
|
210
|
-
_this.mediaPluginState = mediaStateKey.getState(
|
|
286
|
+
var _this$props2 = _this.props,
|
|
287
|
+
_view = _this$props2.view,
|
|
288
|
+
syncProvider = _this$props2.syncProvider;
|
|
289
|
+
_this.mediaPluginState = mediaStateKey.getState(_view.state);
|
|
211
290
|
|
|
212
291
|
// Initialize state from syncProvider (available on both server and client for SSR)
|
|
213
292
|
if (syncProvider) {
|
|
@@ -225,7 +304,9 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
225
304
|
var _this$props$mediaOpti2, _nextProps$mediaOptio;
|
|
226
305
|
var hasNewViewMediaClientConfig = !this.state.viewMediaClientConfig && nextState.viewMediaClientConfig;
|
|
227
306
|
var hasNewViewAndUploadMediaClientConfig = !this.state.viewAndUploadMediaClientConfig && nextState.viewAndUploadMediaClientConfig;
|
|
228
|
-
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection ||
|
|
307
|
+
if (this.props.selected !== nextProps.selected || this.props.node.attrs.id !== nextProps.node.attrs.id || this.props.node.attrs.collection !== nextProps.node.attrs.collection ||
|
|
308
|
+
// Patched in asynchronously once media services report the real size.
|
|
309
|
+
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.isCwrAIGenerating !== nextProps.isCwrAIGenerating || 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$mediaOpti2 = this.props.mediaOptions) === null || _this$props$mediaOpti2 === void 0 ? void 0 : _this$props$mediaOpti2.onMediaRenderEvent) !== ((_nextProps$mediaOptio = nextProps.mediaOptions) === null || _nextProps$mediaOptio === void 0 ? void 0 : _nextProps$mediaOptio.onMediaRenderEvent) || this.state.previewRenderedFileId !== nextState.previewRenderedFileId || this.state.failedFileId !== nextState.failedFileId || hasNewViewMediaClientConfig || hasNewViewAndUploadMediaClientConfig) {
|
|
229
310
|
return true;
|
|
230
311
|
}
|
|
231
312
|
return false;
|
|
@@ -233,17 +314,17 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
233
314
|
}, {
|
|
234
315
|
key: "componentDidMount",
|
|
235
316
|
value: function () {
|
|
236
|
-
var _componentDidMount = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function
|
|
317
|
+
var _componentDidMount = _asyncToGenerator( /*#__PURE__*/_regeneratorRuntime.mark(function _callee3() {
|
|
237
318
|
var _dataConsumerMark$att;
|
|
238
|
-
var _this$
|
|
239
|
-
return _regeneratorRuntime.wrap(function (
|
|
240
|
-
while (1) switch (
|
|
319
|
+
var _this$props3, node, pluginInjectionApi, dataConsumerMark, infographicType, _pluginInjectionApi$a, contextIdentifierProvider, _t2, _t3;
|
|
320
|
+
return _regeneratorRuntime.wrap(function (_context3) {
|
|
321
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
241
322
|
case 0:
|
|
242
323
|
this.handleNewNode(this.props);
|
|
243
324
|
this.emitMediaRenderEvent({
|
|
244
325
|
type: 'mounted'
|
|
245
326
|
});
|
|
246
|
-
_this$
|
|
327
|
+
_this$props3 = this.props, node = _this$props3.node, pluginInjectionApi = _this$props3.pluginInjectionApi;
|
|
247
328
|
dataConsumerMark = this.getDataConsumerMark();
|
|
248
329
|
infographicType = dataConsumerMark === null || dataConsumerMark === void 0 || (_dataConsumerMark$att = dataConsumerMark.attrs.sources) === null || _dataConsumerMark$att === void 0 ? void 0 : _dataConsumerMark$att[0];
|
|
249
330
|
if (infographicType) {
|
|
@@ -260,21 +341,21 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
260
341
|
});
|
|
261
342
|
}
|
|
262
343
|
contextIdentifierProvider = this.props.contextIdentifierProvider;
|
|
263
|
-
|
|
264
|
-
|
|
344
|
+
_t2 = this;
|
|
345
|
+
_context3.next = 1;
|
|
265
346
|
return contextIdentifierProvider;
|
|
266
347
|
case 1:
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
contextIdentifierProvider:
|
|
348
|
+
_t3 = _context3.sent;
|
|
349
|
+
_t2.setState.call(_t2, {
|
|
350
|
+
contextIdentifierProvider: _t3
|
|
270
351
|
});
|
|
271
|
-
|
|
352
|
+
_context3.next = 2;
|
|
272
353
|
return this.setViewMediaClientConfig();
|
|
273
354
|
case 2:
|
|
274
355
|
case "end":
|
|
275
|
-
return
|
|
356
|
+
return _context3.stop();
|
|
276
357
|
}
|
|
277
|
-
},
|
|
358
|
+
}, _callee3, this);
|
|
278
359
|
}));
|
|
279
360
|
function componentDidMount() {
|
|
280
361
|
return _componentDidMount.apply(this, arguments);
|
|
@@ -366,17 +447,32 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
366
447
|
});
|
|
367
448
|
}
|
|
368
449
|
}
|
|
450
|
+
}, {
|
|
451
|
+
key: "hasGeneratedMediaMotion",
|
|
452
|
+
get:
|
|
453
|
+
/**
|
|
454
|
+
* Surface opt-in first, so surfaces that never ask for the motion are not exposed to the
|
|
455
|
+
* gate. The node type is checked after it, so every node on an opted-in surface counts
|
|
456
|
+
* towards the same exposure population.
|
|
457
|
+
*/
|
|
458
|
+
function get() {
|
|
459
|
+
var _this$props$mediaOpti4;
|
|
460
|
+
return !!((_this$props$mediaOpti4 = this.props.mediaOptions) !== null && _this$props$mediaOpti4 !== void 0 && _this$props$mediaOpti4.allowAIGeneratedMediaMotion) && fg('aifc_page_create_defer_generated_visuals') &&
|
|
461
|
+
// File-backed only: `ExternalImageCard` reports neither its rendered preview nor its
|
|
462
|
+
// dimensions, so an external node could only ever open on the reveal's timeout.
|
|
463
|
+
this.props.node.attrs.type !== 'external';
|
|
464
|
+
}
|
|
369
465
|
}, {
|
|
370
466
|
key: "render",
|
|
371
467
|
value: function render() {
|
|
372
468
|
var _dataConsumerMark$att2;
|
|
373
|
-
var _this$
|
|
374
|
-
node = _this$
|
|
375
|
-
selected = _this$
|
|
376
|
-
originalDimensions = _this$
|
|
377
|
-
isLoading = _this$
|
|
378
|
-
maxDimensions = _this$
|
|
379
|
-
mediaOptions = _this$
|
|
469
|
+
var _this$props4 = this.props,
|
|
470
|
+
node = _this$props4.node,
|
|
471
|
+
selected = _this$props4.selected,
|
|
472
|
+
originalDimensions = _this$props4.originalDimensions,
|
|
473
|
+
isLoading = _this$props4.isLoading,
|
|
474
|
+
maxDimensions = _this$props4.maxDimensions,
|
|
475
|
+
mediaOptions = _this$props4.mediaOptions;
|
|
380
476
|
var borderMark = node.marks.find(function (m) {
|
|
381
477
|
return m.type.name === 'border';
|
|
382
478
|
});
|
|
@@ -394,14 +490,26 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
394
490
|
|
|
395
491
|
// Check if we have any media client config available (syncProvider, state, or upload config)
|
|
396
492
|
var hasNoMediaClientConfig = !viewMediaClientConfig && (fg('platform_media_video_captions') ? !viewAndUploadMediaClientConfig : true);
|
|
493
|
+
var hasGeneratedMediaMotion = this.hasGeneratedMediaMotion;
|
|
494
|
+
|
|
495
|
+
// Opening before the real dimensions land would animate to the wrong height and snap
|
|
496
|
+
// when they correct. Nodes that never get them fall through to the reveal's timeout.
|
|
497
|
+
//
|
|
498
|
+
// A terminal error is readiness too: the card is already showing its error treatment,
|
|
499
|
+
// so the space opens onto that rather than hiding it until the timeout.
|
|
500
|
+
var isRevealReady = this.state.failedFileId === id || this.state.previewRenderedFileId === id && !!node.attrs.width && !!node.attrs.height;
|
|
397
501
|
if (isLoading || type !== 'external' && hasNoMediaClientConfig) {
|
|
398
|
-
return /*#__PURE__*/React.createElement(
|
|
502
|
+
return /*#__PURE__*/React.createElement(GeneratedMediaReveal, {
|
|
503
|
+
isEnabled: hasGeneratedMediaMotion,
|
|
504
|
+
isReady: isRevealReady,
|
|
505
|
+
mediaKey: id
|
|
506
|
+
}, /*#__PURE__*/React.createElement(MediaCardWrapper, {
|
|
399
507
|
dimensions: originalDimensions,
|
|
400
508
|
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
|
|
401
509
|
selected: selected
|
|
402
510
|
}, /*#__PURE__*/React.createElement(CardLoading, {
|
|
403
511
|
interactionName: "editor-media-card-loading"
|
|
404
|
-
}));
|
|
512
|
+
})));
|
|
405
513
|
}
|
|
406
514
|
var contextId = contextIdentifierProvider && contextIdentifierProvider.objectId;
|
|
407
515
|
var identifier = type === 'external' ? {
|
|
@@ -434,7 +542,11 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
434
542
|
// generic media type icon isn't shown while the image streams in. Gated on the
|
|
435
543
|
// existing infographics experiment.
|
|
436
544
|
var isCWR = !!this.props.isCwrAIGenerating && isExperimentEnabled('aifc_page_create_with_rovo_include_infographics');
|
|
437
|
-
return /*#__PURE__*/React.createElement(
|
|
545
|
+
return /*#__PURE__*/React.createElement(GeneratedMediaReveal, {
|
|
546
|
+
isEnabled: hasGeneratedMediaMotion,
|
|
547
|
+
isReady: isRevealReady,
|
|
548
|
+
mediaKey: id
|
|
549
|
+
}, /*#__PURE__*/React.createElement(MediaCardWrapper, {
|
|
438
550
|
dimensions: originalDimensions,
|
|
439
551
|
onContextMenu: this.selectMediaSingle,
|
|
440
552
|
borderWidth: borderMark === null || borderMark === void 0 ? void 0 : borderMark.attrs.size,
|
|
@@ -469,10 +581,11 @@ export var MediaNode = /*#__PURE__*/function (_Component) {
|
|
|
469
581
|
mediaSettings: this.getMediaSettings(viewAndUploadMediaClientConfig, this.props.isViewOnly),
|
|
470
582
|
isAIGenerating: !!this.props.isAIGenerating,
|
|
471
583
|
isCWR: isCWR,
|
|
584
|
+
hasLoadingMotion: hasGeneratedMediaMotion,
|
|
472
585
|
onPreviewRender: this.onPreviewRender,
|
|
473
586
|
fallbackMediaNameFetcher: mediaOptions === null || mediaOptions === void 0 ? void 0 : mediaOptions.fallbackMediaNameFetcher,
|
|
474
587
|
onError: this.getMediaRenderErrorHandler()
|
|
475
|
-
})));
|
|
588
|
+
}))));
|
|
476
589
|
}
|
|
477
590
|
}]);
|
|
478
591
|
}(Component);
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
._18m915vq{overflow-y:hidden}
|
|
2
|
+
._1e0c11p5{display:grid}
|
|
3
|
+
._1o51eoah{animation-fill-mode:backwards}
|
|
4
|
+
._1pglindk{animation-timing-function:var(--ds-easing-out-practical,cubic-bezier(.4,1,.6,1))}
|
|
5
|
+
._1reo15vq{overflow-x:hidden}
|
|
6
|
+
._1tkeidpf{min-height:0}
|
|
7
|
+
._2z0563bf{grid-template-rows:0fr}
|
|
8
|
+
._2z05hkll{grid-template-rows:1fr}
|
|
9
|
+
._5sag15uc{animation-duration:var(--ds-duration-xlong,.4s)}
|
|
10
|
+
._j7hq8gkt{animation-name:kaq7wke}
|
|
11
|
+
@keyframes kaq7wke{0%{grid-template-rows:0fr}to{grid-template-rows:1fr}}
|
|
12
|
+
@media (prefers-reduced-motion:reduce){._1bumglyw{animation:none}}
|