@atlaskit/media-card 79.12.0 → 79.13.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 +11 -0
- package/dist/cjs/card/card.js +1 -1
- package/dist/cjs/card/externalImageCard.js +17 -12
- package/dist/cjs/card/fileCard.js +34 -12
- package/dist/cjs/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/cjs/card/ui/titleBox/failedTitleBox.js +0 -2
- package/dist/cjs/inline/loader.js +1 -1
- package/dist/cjs/inline/mediaInlineAnalyticsErrorBoundary.js +0 -2
- package/dist/cjs/utils/globalScope/globalScope.js +2 -1
- package/dist/cjs/utils/mediaPerformanceObserver/durationMetrics.js +1 -0
- package/dist/cjs/utils/ufoExperiences.js +454 -68
- package/dist/es2019/card/card.js +1 -1
- package/dist/es2019/card/externalImageCard.js +16 -13
- package/dist/es2019/card/fileCard.js +33 -13
- package/dist/es2019/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/es2019/card/ui/titleBox/failedTitleBox.js +0 -2
- package/dist/es2019/inline/loader.js +1 -1
- package/dist/es2019/inline/mediaInlineAnalyticsErrorBoundary.js +0 -2
- package/dist/es2019/utils/globalScope/globalScope.js +1 -0
- package/dist/es2019/utils/mediaPerformanceObserver/durationMetrics.js +1 -0
- package/dist/es2019/utils/ufoExperiences.js +449 -72
- package/dist/esm/card/card.js +1 -1
- package/dist/esm/card/externalImageCard.js +18 -13
- package/dist/esm/card/fileCard.js +35 -13
- package/dist/esm/card/media-card-analytics-error-boundary.js +1 -1
- package/dist/esm/card/ui/titleBox/failedTitleBox.js +0 -2
- package/dist/esm/inline/loader.js +1 -1
- package/dist/esm/inline/mediaInlineAnalyticsErrorBoundary.js +0 -2
- package/dist/esm/utils/globalScope/globalScope.js +1 -0
- package/dist/esm/utils/mediaPerformanceObserver/durationMetrics.js +1 -0
- package/dist/esm/utils/ufoExperiences.js +454 -68
- package/dist/types/utils/globalScope/globalScope.d.ts +2 -0
- package/dist/types/utils/mediaPerformanceObserver/durationMetrics.d.ts +1 -0
- package/dist/types/utils/ufoExperiences.d.ts +88 -5
- package/dist/types-ts4.5/utils/globalScope/globalScope.d.ts +2 -0
- package/dist/types-ts4.5/utils/mediaPerformanceObserver/durationMetrics.d.ts +1 -0
- package/dist/types-ts4.5/utils/ufoExperiences.d.ts +88 -5
- package/package.json +6 -6
|
@@ -4,19 +4,402 @@ var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefau
|
|
|
4
4
|
Object.defineProperty(exports, "__esModule", {
|
|
5
5
|
value: true
|
|
6
6
|
});
|
|
7
|
-
exports.startUfoExperience = exports.shouldPerformanceBeSampled = exports.completeUfoExperience = exports.abortUfoExperience = void 0;
|
|
7
|
+
exports.useMediaCardUfoExperience = exports.startUfoExperience = exports.shouldPerformanceBeSampled = exports.completeUfoExperience = exports.abortUfoExperience = void 0;
|
|
8
8
|
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
+
var _toConsumableArray2 = _interopRequireDefault(require("@babel/runtime/helpers/toConsumableArray"));
|
|
10
|
+
var _react = require("react");
|
|
9
11
|
var _ufo = require("@atlaskit/ufo");
|
|
10
12
|
var _mediaCommon = require("@atlaskit/media-common");
|
|
11
13
|
var _uuidValidate = _interopRequireDefault(require("uuid-validate"));
|
|
12
14
|
var _analytics = require("./analytics");
|
|
13
15
|
var _errors = require("../errors");
|
|
14
16
|
var _mediaClient = require("@atlaskit/media-client");
|
|
17
|
+
var _interactionMetrics = require("@atlaskit/react-ufo/interaction-metrics");
|
|
18
|
+
var _globalScope = require("./globalScope/globalScope");
|
|
15
19
|
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; }
|
|
16
20
|
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; }
|
|
17
21
|
var packageName = "@atlaskit/media-card";
|
|
18
|
-
var packageVersion = "
|
|
22
|
+
var packageVersion = "79.13.0";
|
|
19
23
|
var SAMPLE_RATE = 0.05;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Determines if performance events should be sampled for this instance.
|
|
27
|
+
* Approximately 5% of instances will be sampled.
|
|
28
|
+
*/
|
|
29
|
+
var shouldPerformanceBeSampled = exports.shouldPerformanceBeSampled = function shouldPerformanceBeSampled() {
|
|
30
|
+
return Math.random() < SAMPLE_RATE;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Gets the UFO interaction start time.
|
|
35
|
+
* For page_load: returns 0 (relative to page navigation)
|
|
36
|
+
* For SPA transitions: returns performance.now() when transition started
|
|
37
|
+
*/
|
|
38
|
+
var getInteractionStartTime = function getInteractionStartTime() {
|
|
39
|
+
var _interaction$start;
|
|
40
|
+
var interaction = (0, _interactionMetrics.getActiveInteraction)();
|
|
41
|
+
return (_interaction$start = interaction === null || interaction === void 0 ? void 0 : interaction.start) !== null && _interaction$start !== void 0 ? _interaction$start : 0;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Finds a performance resource timing entry by matching a full URI.
|
|
46
|
+
* Searches from the end (most recent) to find the latest matching entry.
|
|
47
|
+
*/
|
|
48
|
+
var findPerformanceEntryByName = function findPerformanceEntryByName(name) {
|
|
49
|
+
var _getMediaGlobalScope$;
|
|
50
|
+
if (typeof performance === 'undefined' || !performance.getEntriesByType) {
|
|
51
|
+
return undefined;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// For data URIs (base64), there won't be a performance entry
|
|
55
|
+
if (name.startsWith('data:')) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
var entries = performance.getEntriesByType('resource');
|
|
59
|
+
var ssrPerformanceEntries = (_getMediaGlobalScope$ = (0, _globalScope.getMediaGlobalScope)().performanceEntries) !== null && _getMediaGlobalScope$ !== void 0 ? _getMediaGlobalScope$ : [];
|
|
60
|
+
return [].concat((0, _toConsumableArray2.default)(ssrPerformanceEntries), (0, _toConsumableArray2.default)(entries)).find(function (entry) {
|
|
61
|
+
return name.includes(entry.name);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Creates timing configuration for the UFO experience based on the performance entry.
|
|
67
|
+
* These timings will be calculated by UFO using the marks we add.
|
|
68
|
+
*/
|
|
69
|
+
var createTimingsConfig = function createTimingsConfig(prefix) {
|
|
70
|
+
return [{
|
|
71
|
+
key: "".concat(prefix, ":resourceTiming"),
|
|
72
|
+
startMark: "".concat(prefix, ":resourceTiming:start"),
|
|
73
|
+
endMark: "".concat(prefix, ":resourceTiming:end")
|
|
74
|
+
}, {
|
|
75
|
+
key: "".concat(prefix, ":dnsLookup"),
|
|
76
|
+
startMark: "".concat(prefix, ":dnsLookup:start"),
|
|
77
|
+
endMark: "".concat(prefix, ":dnsLookup:end")
|
|
78
|
+
}, {
|
|
79
|
+
key: "".concat(prefix, ":tcpHandshake"),
|
|
80
|
+
startMark: "".concat(prefix, ":tcpHandshake:start"),
|
|
81
|
+
endMark: "".concat(prefix, ":tcpHandshake:end")
|
|
82
|
+
}, {
|
|
83
|
+
key: "".concat(prefix, ":tlsNegotiation"),
|
|
84
|
+
startMark: "".concat(prefix, ":tlsNegotiation:start"),
|
|
85
|
+
endMark: "".concat(prefix, ":tlsNegotiation:end")
|
|
86
|
+
}, {
|
|
87
|
+
key: "".concat(prefix, ":ttfb"),
|
|
88
|
+
startMark: "".concat(prefix, ":ttfb:start"),
|
|
89
|
+
endMark: "".concat(prefix, ":ttfb:end")
|
|
90
|
+
}, {
|
|
91
|
+
key: "".concat(prefix, ":contentDownload"),
|
|
92
|
+
startMark: "".concat(prefix, ":contentDownload:start"),
|
|
93
|
+
endMark: "".concat(prefix, ":contentDownload:end")
|
|
94
|
+
}, {
|
|
95
|
+
key: "".concat(prefix, ":redirect"),
|
|
96
|
+
startMark: "".concat(prefix, ":redirect:start"),
|
|
97
|
+
endMark: "".concat(prefix, ":redirect:end")
|
|
98
|
+
}, {
|
|
99
|
+
key: "".concat(prefix, ":fetchTime"),
|
|
100
|
+
startMark: "".concat(prefix, ":fetchTime:start"),
|
|
101
|
+
endMark: "".concat(prefix, ":fetchTime:end")
|
|
102
|
+
}];
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Creates resource timing metadata from a performance entry.
|
|
107
|
+
* Only includes non-timing information (sizes, cache status, protocol, CDN info).
|
|
108
|
+
* Timing durations are captured via marks which feed into the timings config.
|
|
109
|
+
*/
|
|
110
|
+
var createResourceTimingMetadata = function createResourceTimingMetadata(entry) {
|
|
111
|
+
var _entry$serverTiming$s, _entry$serverTiming, _entry$serverTiming2, _entry$serverTiming3;
|
|
112
|
+
return {
|
|
113
|
+
// Size information
|
|
114
|
+
resourceTransferSize: entry.transferSize,
|
|
115
|
+
resourceDecodedBodySize: entry.decodedBodySize,
|
|
116
|
+
// Request info
|
|
117
|
+
resourceInitiatorType: entry.initiatorType,
|
|
118
|
+
resourceNextHopProtocol: entry.nextHopProtocol,
|
|
119
|
+
// Cache status
|
|
120
|
+
resourceBrowserCacheHit: entry.transferSize === 0,
|
|
121
|
+
// Server timing (CDN metrics)
|
|
122
|
+
resourceCdnCacheHit: (_entry$serverTiming$s = (_entry$serverTiming = entry.serverTiming) === null || _entry$serverTiming === void 0 ? void 0 : _entry$serverTiming.some(function (_ref) {
|
|
123
|
+
var name = _ref.name;
|
|
124
|
+
return name === 'cdn-cache-hit';
|
|
125
|
+
})) !== null && _entry$serverTiming$s !== void 0 ? _entry$serverTiming$s : false,
|
|
126
|
+
resourceCdnDownstreamFBL: (_entry$serverTiming2 = entry.serverTiming) === null || _entry$serverTiming2 === void 0 || (_entry$serverTiming2 = _entry$serverTiming2.find(function (_ref2) {
|
|
127
|
+
var name = _ref2.name;
|
|
128
|
+
return name === 'cdn-downstream-fbl';
|
|
129
|
+
})) === null || _entry$serverTiming2 === void 0 ? void 0 : _entry$serverTiming2.duration,
|
|
130
|
+
resourceCdnUpstreamFBL: (_entry$serverTiming3 = entry.serverTiming) === null || _entry$serverTiming3 === void 0 || (_entry$serverTiming3 = _entry$serverTiming3.find(function (_ref3) {
|
|
131
|
+
var name = _ref3.name;
|
|
132
|
+
return name === 'cdn-upstream-fbl';
|
|
133
|
+
})) === null || _entry$serverTiming3 === void 0 ? void 0 : _entry$serverTiming3.duration
|
|
134
|
+
};
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
/**
|
|
138
|
+
* Adds timing marks from a performance resource timing entry to the UFO experience.
|
|
139
|
+
*/
|
|
140
|
+
var addResourceTimingMarks = function addResourceTimingMarks(experience, entry, interactionStartTime, prefix) {
|
|
141
|
+
var addMark = function addMark(name, start, end) {
|
|
142
|
+
var relativeStart = start - interactionStartTime;
|
|
143
|
+
var relativeEnd = end - interactionStartTime;
|
|
144
|
+
if (relativeEnd > relativeStart && relativeStart >= 0) {
|
|
145
|
+
experience.mark("".concat(prefix, ":").concat(name, ":start"), relativeStart);
|
|
146
|
+
experience.mark("".concat(prefix, ":").concat(name, ":end"), relativeEnd);
|
|
147
|
+
}
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
// Overall resource timing
|
|
151
|
+
addMark('resourceTiming', entry.startTime, entry.responseEnd);
|
|
152
|
+
|
|
153
|
+
// DNS lookup
|
|
154
|
+
addMark('dnsLookup', entry.domainLookupStart, entry.domainLookupEnd);
|
|
155
|
+
|
|
156
|
+
// TCP handshake
|
|
157
|
+
addMark('tcpHandshake', entry.connectStart, entry.connectEnd);
|
|
158
|
+
|
|
159
|
+
// TLS negotiation (only for HTTPS)
|
|
160
|
+
if (entry.secureConnectionStart > 0) {
|
|
161
|
+
addMark('tlsNegotiation', entry.secureConnectionStart, entry.requestStart);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Request to first byte (TTFB)
|
|
165
|
+
addMark('ttfb', entry.requestStart, entry.responseStart);
|
|
166
|
+
|
|
167
|
+
// Content download
|
|
168
|
+
addMark('contentDownload', entry.responseStart, entry.responseEnd);
|
|
169
|
+
|
|
170
|
+
// Redirect time (if any)
|
|
171
|
+
if (entry.redirectStart > 0 && entry.redirectEnd > 0) {
|
|
172
|
+
addMark('redirect', entry.redirectStart, entry.redirectEnd);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Total fetch time (without redirect) - from fetchStart to responseEnd
|
|
176
|
+
addMark('fetchTime', entry.fetchStart, entry.responseEnd);
|
|
177
|
+
};
|
|
178
|
+
var sanitiseFileAttributes = function sanitiseFileAttributes(fileAttributes) {
|
|
179
|
+
var sanitisedFileId = 'INVALID_FILE_ID';
|
|
180
|
+
if (fileAttributes.fileId === 'external-image' || (0, _uuidValidate.default)(fileAttributes.fileId)) {
|
|
181
|
+
sanitisedFileId = fileAttributes.fileId;
|
|
182
|
+
}
|
|
183
|
+
return _objectSpread(_objectSpread({}, fileAttributes), {}, {
|
|
184
|
+
fileId: sanitisedFileId
|
|
185
|
+
});
|
|
186
|
+
};
|
|
187
|
+
var getBasePayloadAttributes = function getBasePayloadAttributes() {
|
|
188
|
+
return {
|
|
189
|
+
packageName: packageName,
|
|
190
|
+
packageVersion: packageVersion,
|
|
191
|
+
mediaEnvironment: (0, _mediaClient.getMediaEnvironment)(),
|
|
192
|
+
mediaRegion: (0, _mediaClient.getMediaRegion)()
|
|
193
|
+
};
|
|
194
|
+
};
|
|
195
|
+
/**
|
|
196
|
+
* Creates a new UFO experience instance with the given configuration.
|
|
197
|
+
*/
|
|
198
|
+
var createExperience = function createExperience(instanceId) {
|
|
199
|
+
var timings = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : [];
|
|
200
|
+
return new _ufo.UFOExperience('media-card-render', {
|
|
201
|
+
platform: {
|
|
202
|
+
component: 'media-card'
|
|
203
|
+
},
|
|
204
|
+
type: _ufo.ExperienceTypes.Experience,
|
|
205
|
+
performanceType: _ufo.ExperiencePerformanceTypes.InlineResult,
|
|
206
|
+
featureFlags: (0, _mediaCommon.getFeatureFlagKeysAllProducts)(),
|
|
207
|
+
timings: timings
|
|
208
|
+
}, instanceId);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Hook to create a UFO experience tied to a media card component lifecycle.
|
|
213
|
+
*
|
|
214
|
+
* This creates a unique UFOExperience instance per component, allowing:
|
|
215
|
+
* - Unique timing config per instance
|
|
216
|
+
* - Direct control over experience lifecycle
|
|
217
|
+
* - Proper cleanup on unmount
|
|
218
|
+
*
|
|
219
|
+
* @example
|
|
220
|
+
* ```tsx
|
|
221
|
+
* const ufoExperience = useMediaCardUfoExperience({
|
|
222
|
+
* instanceId: internalOccurrenceKey,
|
|
223
|
+
* enabled: shouldSendPerformanceEvent,
|
|
224
|
+
* });
|
|
225
|
+
*
|
|
226
|
+
* // On card visible
|
|
227
|
+
* ufoExperience.start();
|
|
228
|
+
*
|
|
229
|
+
* // On card complete/error
|
|
230
|
+
* ufoExperience.complete(status, fileAttributes, fileStateFlags, ssrReliability, error, ssrPreviewInfo);
|
|
231
|
+
* ```
|
|
232
|
+
*/
|
|
233
|
+
var useMediaCardUfoExperience = exports.useMediaCardUfoExperience = function useMediaCardUfoExperience(_ref4) {
|
|
234
|
+
var instanceId = _ref4.instanceId,
|
|
235
|
+
enabled = _ref4.enabled;
|
|
236
|
+
// Store the start time when start() is called - experience creation is deferred to complete()
|
|
237
|
+
var startTimeRef = (0, _react.useRef)(undefined);
|
|
238
|
+
var hasStartedRef = (0, _react.useRef)(false);
|
|
239
|
+
// Store the experience so abort() can use it after complete() has run
|
|
240
|
+
var experienceRef = (0, _react.useRef)(null);
|
|
241
|
+
|
|
242
|
+
// Reset refs when instanceId changes (new card instance)
|
|
243
|
+
(0, _react.useEffect)(function () {
|
|
244
|
+
return function () {
|
|
245
|
+
// Note: Don't clear experienceRef here as abort() might still need it
|
|
246
|
+
// The component's cleanup calls abort() which handles final state
|
|
247
|
+
hasStartedRef.current = false;
|
|
248
|
+
startTimeRef.current = undefined;
|
|
249
|
+
};
|
|
250
|
+
}, [instanceId]);
|
|
251
|
+
var start = (0, _react.useCallback)(function (options) {
|
|
252
|
+
if (!enabled) {
|
|
253
|
+
return;
|
|
254
|
+
}
|
|
255
|
+
// Store the start time - experience will be created in complete()
|
|
256
|
+
// This allows us to have the correct timings config when creating the experience
|
|
257
|
+
hasStartedRef.current = true;
|
|
258
|
+
startTimeRef.current = options !== null && options !== void 0 && options.useInteractionTime ? getInteractionStartTime() : performance.now();
|
|
259
|
+
}, [enabled]);
|
|
260
|
+
var complete = (0, _react.useCallback)(function (status, fileAttributes, fileStateFlags, ssrReliability) {
|
|
261
|
+
var error = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : new _errors.MediaCardError('missing-error-data');
|
|
262
|
+
var ssrPreviewInfo = arguments.length > 5 ? arguments[5] : undefined;
|
|
263
|
+
// Only complete for terminal statuses - ignore intermediate statuses like 'loading-preview'
|
|
264
|
+
if (!['complete', 'error', 'failed-processing'].includes(status)) {
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
if (!enabled || !hasStartedRef.current) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
var interactionStartTime = getInteractionStartTime();
|
|
271
|
+
|
|
272
|
+
// Determine timings config based on SSR status
|
|
273
|
+
var timingsConfig = [];
|
|
274
|
+
var resourceTimingEntry;
|
|
275
|
+
if (ssrPreviewInfo !== null && ssrPreviewInfo !== void 0 && ssrPreviewInfo.wasSSRSuccessful && ssrPreviewInfo.dataUri) {
|
|
276
|
+
var _ssrPreviewInfo$srcse;
|
|
277
|
+
resourceTimingEntry = findPerformanceEntryByName((_ssrPreviewInfo$srcse = ssrPreviewInfo.srcset) !== null && _ssrPreviewInfo$srcse !== void 0 ? _ssrPreviewInfo$srcse : ssrPreviewInfo.dataUri);
|
|
278
|
+
if (resourceTimingEntry) {
|
|
279
|
+
timingsConfig = createTimingsConfig('ssr');
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// Create the experience with the correct timings config
|
|
284
|
+
var experience = createExperience(instanceId, timingsConfig);
|
|
285
|
+
experienceRef.current = experience;
|
|
286
|
+
experience.start(startTimeRef.current);
|
|
287
|
+
|
|
288
|
+
// Add timing marks and metadata based on strategy
|
|
289
|
+
if (ssrPreviewInfo !== null && ssrPreviewInfo !== void 0 && ssrPreviewInfo.wasSSRSuccessful && ssrPreviewInfo.dataUri) {
|
|
290
|
+
if (resourceTimingEntry) {
|
|
291
|
+
addResourceTimingMarks(experience, resourceTimingEntry, interactionStartTime, 'ssr');
|
|
292
|
+
experience.addMetadata(_objectSpread(_objectSpread({}, createResourceTimingMetadata(resourceTimingEntry)), {}, {
|
|
293
|
+
timingStrategy: 'ssr-resource-timing'
|
|
294
|
+
}));
|
|
295
|
+
} else {
|
|
296
|
+
experience.addMetadata({
|
|
297
|
+
timingStrategy: 'ssr-no-entry-found'
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
} else if (ssrPreviewInfo !== null && ssrPreviewInfo !== void 0 && ssrPreviewInfo.wasSSRAttempted) {
|
|
301
|
+
// Strategy 2: SSR was attempted but failed - use interaction start time
|
|
302
|
+
experience.addMetadata({
|
|
303
|
+
timingStrategy: 'ssr-failed',
|
|
304
|
+
interactionStartTime: interactionStartTime
|
|
305
|
+
});
|
|
306
|
+
experience.mark('interactionStart', 0);
|
|
307
|
+
} else {
|
|
308
|
+
// Strategy 3: No SSR - CSR mount-based behavior
|
|
309
|
+
experience.addMetadata({
|
|
310
|
+
timingStrategy: 'csr-mount-based'
|
|
311
|
+
});
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
// Complete the experience with appropriate state
|
|
315
|
+
var sanitisedFileAttributes = sanitiseFileAttributes(fileAttributes);
|
|
316
|
+
switch (status) {
|
|
317
|
+
case 'complete':
|
|
318
|
+
experience.success({
|
|
319
|
+
metadata: _objectSpread({
|
|
320
|
+
fileAttributes: sanitisedFileAttributes,
|
|
321
|
+
ssrReliability: ssrReliability,
|
|
322
|
+
fileStateFlags: fileStateFlags
|
|
323
|
+
}, getBasePayloadAttributes())
|
|
324
|
+
});
|
|
325
|
+
break;
|
|
326
|
+
case 'failed-processing':
|
|
327
|
+
experience.failure({
|
|
328
|
+
metadata: _objectSpread({
|
|
329
|
+
fileAttributes: sanitisedFileAttributes,
|
|
330
|
+
ssrReliability: ssrReliability,
|
|
331
|
+
fileStateFlags: fileStateFlags,
|
|
332
|
+
failReason: 'failed-processing'
|
|
333
|
+
}, getBasePayloadAttributes())
|
|
334
|
+
});
|
|
335
|
+
break;
|
|
336
|
+
case 'error':
|
|
337
|
+
experience.failure({
|
|
338
|
+
metadata: _objectSpread(_objectSpread({
|
|
339
|
+
fileAttributes: sanitisedFileAttributes,
|
|
340
|
+
ssrReliability: ssrReliability,
|
|
341
|
+
fileStateFlags: fileStateFlags
|
|
342
|
+
}, (0, _analytics.extractErrorInfo)(error)), {}, {
|
|
343
|
+
request: (0, _analytics.getRenderErrorRequestMetadata)(error)
|
|
344
|
+
}, getBasePayloadAttributes())
|
|
345
|
+
});
|
|
346
|
+
break;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// Reset state after completion
|
|
350
|
+
hasStartedRef.current = false;
|
|
351
|
+
startTimeRef.current = undefined;
|
|
352
|
+
}, [enabled, instanceId]);
|
|
353
|
+
var abort = (0, _react.useCallback)(function (properties) {
|
|
354
|
+
if (!enabled) {
|
|
355
|
+
return;
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// Use existing experience if available (created by complete()),
|
|
359
|
+
// otherwise create new one if experience was started but not completed
|
|
360
|
+
var experience = experienceRef.current;
|
|
361
|
+
if (!experience) {
|
|
362
|
+
if (!hasStartedRef.current) {
|
|
363
|
+
return;
|
|
364
|
+
}
|
|
365
|
+
experience = createExperience(instanceId);
|
|
366
|
+
experienceRef.current = experience;
|
|
367
|
+
experience.start(startTimeRef.current);
|
|
368
|
+
}
|
|
369
|
+
var metadata = _objectSpread({}, getBasePayloadAttributes());
|
|
370
|
+
if (properties !== null && properties !== void 0 && properties.fileAttributes) {
|
|
371
|
+
metadata.fileAttributes = sanitiseFileAttributes(properties.fileAttributes);
|
|
372
|
+
}
|
|
373
|
+
if (properties !== null && properties !== void 0 && properties.fileStateFlags) {
|
|
374
|
+
metadata.fileStateFlags = properties.fileStateFlags;
|
|
375
|
+
}
|
|
376
|
+
if (properties !== null && properties !== void 0 && properties.ssrReliability) {
|
|
377
|
+
metadata.ssrReliability = properties.ssrReliability;
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// UFO will ignore abort if experience is already in final state
|
|
381
|
+
experience.abort({
|
|
382
|
+
metadata: metadata
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
// Reset state after abort
|
|
386
|
+
hasStartedRef.current = false;
|
|
387
|
+
startTimeRef.current = undefined;
|
|
388
|
+
}, [enabled, instanceId]);
|
|
389
|
+
return (0, _react.useMemo)(function () {
|
|
390
|
+
return {
|
|
391
|
+
start: start,
|
|
392
|
+
complete: complete,
|
|
393
|
+
abort: abort
|
|
394
|
+
};
|
|
395
|
+
}, [start, complete, abort]);
|
|
396
|
+
};
|
|
397
|
+
|
|
398
|
+
// ============================================================================
|
|
399
|
+
// Legacy exports for backwards compatibility
|
|
400
|
+
// These will be removed once all consumers are migrated to the hook
|
|
401
|
+
// ============================================================================
|
|
402
|
+
|
|
20
403
|
var concurrentExperience;
|
|
21
404
|
var getExperience = function getExperience(id) {
|
|
22
405
|
if (!concurrentExperience) {
|
|
@@ -32,89 +415,92 @@ var getExperience = function getExperience(id) {
|
|
|
32
415
|
}
|
|
33
416
|
return concurrentExperience.getInstance(id);
|
|
34
417
|
};
|
|
35
|
-
var
|
|
36
|
-
|
|
37
|
-
// We generate about 100M events UFOv1 events, we want to reduce this to about 5M as we can get the same info from there
|
|
38
|
-
// Math.random() generates a random floating-point number between 0 (inclusive) and 1 (exclusive).
|
|
39
|
-
// The condition Math.random() < SAMPLE_RATE (0.05) will be true approximately 5% of the time.
|
|
40
|
-
Math.random() < SAMPLE_RATE
|
|
41
|
-
);
|
|
42
|
-
};
|
|
43
|
-
var startUfoExperience = exports.startUfoExperience = function startUfoExperience(id) {
|
|
44
|
-
getExperience(id).start();
|
|
45
|
-
};
|
|
46
|
-
var sanitiseFileAttributes = function sanitiseFileAttributes(fileAttributes) {
|
|
47
|
-
/*
|
|
48
|
-
Allow external image mediaItemType as fileId
|
|
49
|
-
See ExternalImageIdentifier interface on platform/packages/media/media-client/src/identifier.ts
|
|
50
|
-
*/
|
|
51
|
-
var sanitisedFileId = 'INVALID_FILE_ID';
|
|
52
|
-
if (fileAttributes.fileId === 'external-image' || (0, _uuidValidate.default)(fileAttributes.fileId)) {
|
|
53
|
-
sanitisedFileId = fileAttributes.fileId;
|
|
54
|
-
}
|
|
55
|
-
return _objectSpread(_objectSpread({}, fileAttributes), {}, {
|
|
56
|
-
fileId: sanitisedFileId
|
|
57
|
-
});
|
|
418
|
+
var startUfoExperience = exports.startUfoExperience = function startUfoExperience(id, startTime) {
|
|
419
|
+
getExperience(id).start(startTime);
|
|
58
420
|
};
|
|
59
421
|
var completeUfoExperience = exports.completeUfoExperience = function completeUfoExperience(id, status, fileAttributes, fileStateFlags, ssrReliability) {
|
|
60
422
|
var error = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : new _errors.MediaCardError('missing-error-data');
|
|
423
|
+
var ssrPreviewInfo = arguments.length > 6 ? arguments[6] : undefined;
|
|
424
|
+
// Only complete for terminal statuses - ignore intermediate statuses like 'loading-preview'
|
|
425
|
+
if (!['complete', 'error', 'failed-processing'].includes(status)) {
|
|
426
|
+
return;
|
|
427
|
+
}
|
|
428
|
+
var experience = getExperience(id);
|
|
429
|
+
var interactionStartTime = getInteractionStartTime();
|
|
430
|
+
|
|
431
|
+
// Determine timing strategy based on SSR status
|
|
432
|
+
if (ssrPreviewInfo !== null && ssrPreviewInfo !== void 0 && ssrPreviewInfo.wasSSRSuccessful && ssrPreviewInfo.dataUri) {
|
|
433
|
+
var _ssrPreviewInfo$srcse2;
|
|
434
|
+
var entry = findPerformanceEntryByName((_ssrPreviewInfo$srcse2 = ssrPreviewInfo.srcset) !== null && _ssrPreviewInfo$srcse2 !== void 0 ? _ssrPreviewInfo$srcse2 : ssrPreviewInfo.dataUri);
|
|
435
|
+
if (entry) {
|
|
436
|
+
addResourceTimingMarks(experience, entry, interactionStartTime, 'ssr');
|
|
437
|
+
experience.addMetadata(_objectSpread(_objectSpread({}, createResourceTimingMetadata(entry)), {}, {
|
|
438
|
+
timingStrategy: 'ssr-resource-timing'
|
|
439
|
+
}));
|
|
440
|
+
} else {
|
|
441
|
+
experience.addMetadata({
|
|
442
|
+
timingStrategy: 'ssr-no-entry-found'
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
} else if (ssrPreviewInfo !== null && ssrPreviewInfo !== void 0 && ssrPreviewInfo.wasSSRAttempted) {
|
|
446
|
+
experience.addMetadata({
|
|
447
|
+
timingStrategy: 'ssr-failed',
|
|
448
|
+
interactionStartTime: interactionStartTime
|
|
449
|
+
});
|
|
450
|
+
experience.mark('interactionStart', 0);
|
|
451
|
+
} else {
|
|
452
|
+
experience.addMetadata({
|
|
453
|
+
timingStrategy: 'csr-mount-based'
|
|
454
|
+
});
|
|
455
|
+
}
|
|
456
|
+
var sanitisedFileAttributes = sanitiseFileAttributes(fileAttributes);
|
|
61
457
|
switch (status) {
|
|
62
458
|
case 'complete':
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
459
|
+
experience.success({
|
|
460
|
+
metadata: _objectSpread({
|
|
461
|
+
fileAttributes: sanitisedFileAttributes,
|
|
462
|
+
ssrReliability: ssrReliability,
|
|
463
|
+
fileStateFlags: fileStateFlags
|
|
464
|
+
}, getBasePayloadAttributes())
|
|
67
465
|
});
|
|
68
466
|
break;
|
|
69
467
|
case 'failed-processing':
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
468
|
+
experience.failure({
|
|
469
|
+
metadata: _objectSpread({
|
|
470
|
+
fileAttributes: sanitisedFileAttributes,
|
|
471
|
+
ssrReliability: ssrReliability,
|
|
472
|
+
fileStateFlags: fileStateFlags,
|
|
473
|
+
failReason: 'failed-processing'
|
|
474
|
+
}, getBasePayloadAttributes())
|
|
75
475
|
});
|
|
76
476
|
break;
|
|
77
477
|
case 'error':
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
478
|
+
experience.failure({
|
|
479
|
+
metadata: _objectSpread(_objectSpread({
|
|
480
|
+
fileAttributes: sanitisedFileAttributes,
|
|
481
|
+
ssrReliability: ssrReliability,
|
|
482
|
+
fileStateFlags: fileStateFlags
|
|
483
|
+
}, (0, _analytics.extractErrorInfo)(error)), {}, {
|
|
484
|
+
request: (0, _analytics.getRenderErrorRequestMetadata)(error)
|
|
485
|
+
}, getBasePayloadAttributes())
|
|
486
|
+
});
|
|
85
487
|
break;
|
|
86
488
|
}
|
|
87
489
|
};
|
|
88
|
-
var
|
|
89
|
-
|
|
90
|
-
packageName: packageName,
|
|
91
|
-
packageVersion: packageVersion,
|
|
92
|
-
mediaEnvironment: (0, _mediaClient.getMediaEnvironment)(),
|
|
93
|
-
mediaRegion: (0, _mediaClient.getMediaRegion)()
|
|
94
|
-
};
|
|
95
|
-
};
|
|
96
|
-
var succeedUfoExperience = function succeedUfoExperience(id, properties) {
|
|
490
|
+
var abortUfoExperience = exports.abortUfoExperience = function abortUfoExperience(id, properties) {
|
|
491
|
+
var metadata = _objectSpread({}, getBasePayloadAttributes());
|
|
97
492
|
if (properties !== null && properties !== void 0 && properties.fileAttributes) {
|
|
98
|
-
|
|
493
|
+
metadata.fileAttributes = sanitiseFileAttributes(properties.fileAttributes);
|
|
99
494
|
}
|
|
100
|
-
|
|
101
|
-
metadata
|
|
102
|
-
});
|
|
103
|
-
};
|
|
104
|
-
var failUfoExperience = function failUfoExperience(id, properties) {
|
|
105
|
-
if (properties !== null && properties !== void 0 && properties.fileAttributes) {
|
|
106
|
-
properties.fileAttributes = sanitiseFileAttributes(properties.fileAttributes);
|
|
495
|
+
if (properties !== null && properties !== void 0 && properties.fileStateFlags) {
|
|
496
|
+
metadata.fileStateFlags = properties.fileStateFlags;
|
|
107
497
|
}
|
|
108
|
-
|
|
109
|
-
metadata
|
|
110
|
-
});
|
|
111
|
-
};
|
|
112
|
-
var abortUfoExperience = exports.abortUfoExperience = function abortUfoExperience(id, properties) {
|
|
113
|
-
// UFO won't abort if it's already in a final state (succeeded, failed, aborted, etc)
|
|
114
|
-
if (properties !== null && properties !== void 0 && properties.fileAttributes) {
|
|
115
|
-
properties.fileAttributes = sanitiseFileAttributes(properties.fileAttributes);
|
|
498
|
+
if (properties !== null && properties !== void 0 && properties.ssrReliability) {
|
|
499
|
+
metadata.ssrReliability = properties.ssrReliability;
|
|
116
500
|
}
|
|
117
501
|
getExperience(id).abort({
|
|
118
|
-
metadata:
|
|
502
|
+
metadata: metadata
|
|
119
503
|
});
|
|
120
|
-
};
|
|
504
|
+
};
|
|
505
|
+
|
|
506
|
+
// Suppress unused type warnings - these are used for type documentation
|
package/dist/es2019/card/card.js
CHANGED
|
@@ -11,7 +11,7 @@ import { useAnalyticsEvents } from '@atlaskit/analytics-next';
|
|
|
11
11
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
12
12
|
import UFOLabel from '@atlaskit/react-ufo/label';
|
|
13
13
|
const packageName = "@atlaskit/media-card";
|
|
14
|
-
const packageVersion = "
|
|
14
|
+
const packageVersion = "79.13.0";
|
|
15
15
|
export const CardBase = ({
|
|
16
16
|
identifier,
|
|
17
17
|
...otherProps
|
|
@@ -7,7 +7,7 @@ import ReactDOM from 'react-dom';
|
|
|
7
7
|
import { ImageLoadError } from '../errors';
|
|
8
8
|
import { generateUniqueId } from '../utils/generateUniqueId';
|
|
9
9
|
import { getMediaCardCursor } from '../utils/getMediaCardCursor';
|
|
10
|
-
import {
|
|
10
|
+
import { shouldPerformanceBeSampled, useMediaCardUfoExperience } from '../utils/ufoExperiences';
|
|
11
11
|
import { useCurrentValueRef } from '../utils/useCurrentValueRef';
|
|
12
12
|
import { getDefaultCardDimensions } from '../utils/cardDimensions';
|
|
13
13
|
import { usePrevious } from '../utils/usePrevious';
|
|
@@ -52,10 +52,15 @@ export const ExternalImageCard = ({
|
|
|
52
52
|
wasStatusUploading: false,
|
|
53
53
|
wasStatusProcessing: false
|
|
54
54
|
});
|
|
55
|
+
const shouldSendPerformanceEvent = useMemo(() => shouldPerformanceBeSampled(), []);
|
|
56
|
+
|
|
57
|
+
// UFO experience hook - creates a unique experience instance per card
|
|
58
|
+
const ufoExperience = useMediaCardUfoExperience({
|
|
59
|
+
instanceId: internalOccurrenceKey,
|
|
60
|
+
enabled: shouldSendPerformanceEvent
|
|
61
|
+
});
|
|
55
62
|
const startUfoExperienceRef = useCurrentValueRef(() => {
|
|
56
|
-
|
|
57
|
-
startUfoExperience(internalOccurrenceKey);
|
|
58
|
-
}
|
|
63
|
+
ufoExperience.start();
|
|
59
64
|
});
|
|
60
65
|
const [status, setStatus] = useState('loading-preview');
|
|
61
66
|
const cardPreview = useMemo(() => ({
|
|
@@ -85,7 +90,6 @@ export const ExternalImageCard = ({
|
|
|
85
90
|
const [previewDidRender, setPreviewDidRender] = useState(false);
|
|
86
91
|
const [error, setError] = useState();
|
|
87
92
|
const [mediaViewerSelectedItem, setMediaViewerSelectedItem] = useState(null);
|
|
88
|
-
const shouldSendPerformanceEventRef = useRef(shouldPerformanceBeSampled());
|
|
89
93
|
|
|
90
94
|
//----------------------------------------------------------------//
|
|
91
95
|
//---------------------- Analytics ------------------------------//
|
|
@@ -101,17 +105,16 @@ export const ExternalImageCard = ({
|
|
|
101
105
|
}
|
|
102
106
|
};
|
|
103
107
|
createAnalyticsEvent && fireOperationalEvent(createAnalyticsEvent, status, fileAttributes, performanceAttributes, ssrReliability, error, traceContext, undefined);
|
|
104
|
-
|
|
108
|
+
// External images don't use SSR - no ssrPreviewInfo passed
|
|
109
|
+
ufoExperience.complete(status, fileAttributes, fileStateFlagsRef.current, ssrReliability, error, undefined);
|
|
105
110
|
});
|
|
106
111
|
const fireAbortedEventRef = useCurrentValueRef(() => {
|
|
107
112
|
// UFO won't abort if it's already in a final state (succeeded, failed, aborted, etc)
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
});
|
|
114
|
-
}
|
|
113
|
+
ufoExperience.abort({
|
|
114
|
+
fileAttributes,
|
|
115
|
+
fileStateFlags: fileStateFlagsRef === null || fileStateFlagsRef === void 0 ? void 0 : fileStateFlagsRef.current,
|
|
116
|
+
ssrReliability: ssrReliability
|
|
117
|
+
});
|
|
115
118
|
});
|
|
116
119
|
|
|
117
120
|
//----------------------------------------------------------------//
|