@atlaskit/media-viewer 52.5.0 → 52.5.1
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 +6 -0
- package/dist/cjs/analytics/index.js +1 -1
- package/dist/cjs/analytics/ufoExperiences.js +1 -1
- package/dist/cjs/item-viewer.js +89 -17
- package/dist/es2019/analytics/index.js +1 -1
- package/dist/es2019/analytics/ufoExperiences.js +1 -1
- package/dist/es2019/item-viewer.js +91 -19
- package/dist/esm/analytics/index.js +1 -1
- package/dist/esm/analytics/ufoExperiences.js +1 -1
- package/dist/esm/item-viewer.js +89 -17
- package/dist/types/viewers/archiveSidebar/styleWrappers.d.ts +1 -1
- package/dist/types-ts4.5/viewers/archiveSidebar/styleWrappers.d.ts +1 -1
- package/package.json +11 -8
package/CHANGELOG.md
CHANGED
|
@@ -10,7 +10,7 @@ exports.packageVersion = exports.packageName = void 0;
|
|
|
10
10
|
var _analytics = require("@atlaskit/media-common/analytics");
|
|
11
11
|
var componentName = exports.component = exports.componentName = 'mediaViewer';
|
|
12
12
|
var packageName = exports.packageName = "@atlaskit/media-viewer";
|
|
13
|
-
var packageVersion = exports.packageVersion = "
|
|
13
|
+
var packageVersion = exports.packageVersion = "0.0.0-development";
|
|
14
14
|
function getFileAttributes(fileState) {
|
|
15
15
|
if (!fileState) {
|
|
16
16
|
return {
|
|
@@ -13,7 +13,7 @@ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
|
13
13
|
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; }
|
|
14
14
|
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; }
|
|
15
15
|
var packageName = "@atlaskit/media-viewer";
|
|
16
|
-
var packageVersion = "
|
|
16
|
+
var packageVersion = "0.0.0-development";
|
|
17
17
|
var ufoExperience;
|
|
18
18
|
var getExperience = function getExperience() {
|
|
19
19
|
if (!ufoExperience) {
|
package/dist/cjs/item-viewer.js
CHANGED
|
@@ -27,6 +27,7 @@ var _loadSucceeded = require("./analytics/events/operational/loadSucceeded");
|
|
|
27
27
|
var _analytics = require("./analytics");
|
|
28
28
|
var _interactiveImg = require("./viewers/image/interactive-img");
|
|
29
29
|
var _archiveViewerLoader = _interopRequireDefault(require("./viewers/archiveSidebar/archiveViewerLoader"));
|
|
30
|
+
var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
|
|
30
31
|
var _ufoExperiences = require("./analytics/ufoExperiences");
|
|
31
32
|
var _customViewer = require("./viewers/customViewer/customViewer");
|
|
32
33
|
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" != (0, _typeof2.default)(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 _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
|
|
@@ -113,6 +114,59 @@ var isFileStateItem = exports.isFileStateItem = function isFileStateItem(fileIte
|
|
|
113
114
|
return !isExternalImageItem(fileItem);
|
|
114
115
|
};
|
|
115
116
|
var MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER = exports.MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER = 10 * 1024 * 1024;
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Determines if a file renders natively without backend processing artifacts.
|
|
120
|
+
* These files (text/code, PDF, SVG) can be rendered directly from the original binary
|
|
121
|
+
* without waiting for transcoded artifacts from the backend.
|
|
122
|
+
*
|
|
123
|
+
* Evidence:
|
|
124
|
+
* - CodeViewer: Uses getFileBinaryURL(id) - works with both 'processing' and 'processed' status
|
|
125
|
+
* - DocViewer: Uses getDocumentContent(id) - only needs file ID, not artifacts
|
|
126
|
+
* - SvgViewer: Uses MediaSvg with identifier - never touches status or artifacts
|
|
127
|
+
*/
|
|
128
|
+
var canRenderWithoutProcessing = function canRenderWithoutProcessing(fileState) {
|
|
129
|
+
var mimeType = fileState.mimeType,
|
|
130
|
+
name = fileState.name,
|
|
131
|
+
size = fileState.size;
|
|
132
|
+
|
|
133
|
+
// Text/code files via CodeViewer (10MB limit)
|
|
134
|
+
if ((0, _codeViewer.isCodeViewerItem)(name, mimeType) && size <= MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER) {
|
|
135
|
+
return true;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// PDF files via DocViewer
|
|
139
|
+
if (mimeType === 'application/pdf') {
|
|
140
|
+
return true;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// SVG files via SvgViewer
|
|
144
|
+
if (mimeType === 'image/svg+xml') {
|
|
145
|
+
return true;
|
|
146
|
+
}
|
|
147
|
+
return false;
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Creates a synthetic ProcessedFileState for native-rendered files.
|
|
152
|
+
* These files don't need backend processing, so we normalize to 'processed'
|
|
153
|
+
* to prevent unnecessary re-renders when the actual status changes.
|
|
154
|
+
* Note: artifacts are not used by native viewers (CodeViewer, DocViewer, SvgViewer)
|
|
155
|
+
* as they fetch the original binary directly via file ID.
|
|
156
|
+
*/
|
|
157
|
+
var createProcessedFileState = function createProcessedFileState(fileState) {
|
|
158
|
+
return {
|
|
159
|
+
status: 'processed',
|
|
160
|
+
id: fileState.id,
|
|
161
|
+
name: fileState.name,
|
|
162
|
+
size: fileState.size,
|
|
163
|
+
mediaType: fileState.mediaType,
|
|
164
|
+
mimeType: fileState.mimeType,
|
|
165
|
+
artifacts: {},
|
|
166
|
+
preview: fileState.preview,
|
|
167
|
+
createdAt: fileState.createdAt
|
|
168
|
+
};
|
|
169
|
+
};
|
|
116
170
|
var ItemViewerBase = exports.ItemViewerBase = function ItemViewerBase(_ref) {
|
|
117
171
|
var identifier = _ref.identifier,
|
|
118
172
|
showControls = _ref.showControls,
|
|
@@ -161,32 +215,50 @@ var ItemViewerBase = exports.ItemViewerBase = function ItemViewerBase(_ref) {
|
|
|
161
215
|
(0, _analytics.fireAnalytics)((0, _commenced.createCommencedEvent)(identifier === null || identifier === void 0 ? void 0 : identifier.id, traceContext), createAnalyticsEventRef.current);
|
|
162
216
|
(0, _ufoExperiences.startMediaFileUfoExperience)();
|
|
163
217
|
}, [identifier, traceContext]);
|
|
218
|
+
var isNativeFileOptimizationEnabled = (0, _platformFeatureFlags.fg)('media_viewer_prevent_rerender_on_polling');
|
|
164
219
|
(0, _react.useEffect)(function () {
|
|
220
|
+
// External images don't need backend subscriptions
|
|
165
221
|
if ((0, _mediaClient.isExternalImageIdentifier)(identifier)) {
|
|
166
|
-
// external images do not need to talk to our backend,
|
|
167
|
-
// so therefore no need for media-client subscriptions.
|
|
168
|
-
// just set a successful outcome of type "external-image".
|
|
169
222
|
setItem(_domain.Outcome.successful('external-image'));
|
|
170
223
|
return;
|
|
171
224
|
}
|
|
225
|
+
if (!fileState) {
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
var status = fileState.status;
|
|
229
|
+
|
|
230
|
+
// Track status flags for analytics
|
|
231
|
+
if (status === 'processing') {
|
|
232
|
+
fileStateFlagsRef.current.wasStatusProcessing = true;
|
|
233
|
+
} else if (status === 'uploading') {
|
|
234
|
+
fileStateFlagsRef.current.wasStatusUploading = true;
|
|
235
|
+
}
|
|
172
236
|
|
|
173
|
-
//
|
|
174
|
-
if (
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
237
|
+
// Handle error state
|
|
238
|
+
if (status === 'error') {
|
|
239
|
+
setItem(_domain.Outcome.failed(new _errors.MediaViewerError('itemviewer-fetch-metadata', (0, _mediaClient.toCommonMediaClientError)(fileState))));
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
178
242
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
243
|
+
// Optimization: normalize native files to ProcessedFileState to prevent
|
|
244
|
+
// unnecessary re-renders when status changes (e.g., processing → processed)
|
|
245
|
+
if (isNativeFileOptimizationEnabled && canRenderWithoutProcessing(fileState)) {
|
|
246
|
+
setItem(function (prev) {
|
|
247
|
+
// Keep stable reference if we already have a processed state for this file
|
|
248
|
+
if (prev.status === 'SUCCESSFUL' && prev.data && isFileStateItem(prev.data) && prev.data.id === fileState.id && prev.data.status === 'processed') {
|
|
249
|
+
return prev;
|
|
183
250
|
}
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
251
|
+
// First load or different file: create normalized processed state
|
|
252
|
+
return _domain.Outcome.successful(createProcessedFileState(fileState));
|
|
253
|
+
});
|
|
254
|
+
return;
|
|
188
255
|
}
|
|
189
|
-
|
|
256
|
+
|
|
257
|
+
// Non-native files: standard behavior
|
|
258
|
+
setItem(_domain.Outcome.successful(fileState));
|
|
259
|
+
// fileState object reference changes often when polling items (especially during processing); only re-run when fileState.status changes
|
|
260
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
261
|
+
}, [isNativeFileOptimizationEnabled ? fileState === null || fileState === void 0 ? void 0 : fileState.status : fileState, identifier]);
|
|
190
262
|
var onSuccess = (0, _react.useCallback)(function () {
|
|
191
263
|
item.whenSuccessful(function (fileItem) {
|
|
192
264
|
if (isFileStateItem(fileItem)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ANALYTICS_MEDIA_CHANNEL, sanitiseAnalyticsPayload } from '@atlaskit/media-common/analytics';
|
|
2
2
|
const componentName = 'mediaViewer';
|
|
3
3
|
const packageName = "@atlaskit/media-viewer";
|
|
4
|
-
const packageVersion = "
|
|
4
|
+
const packageVersion = "0.0.0-development";
|
|
5
5
|
export { packageName, packageVersion, componentName, componentName as component };
|
|
6
6
|
export function getFileAttributes(fileState) {
|
|
7
7
|
if (!fileState) {
|
|
@@ -3,7 +3,7 @@ import { getMediaEnvironment, getMediaRegion } from '@atlaskit/media-client';
|
|
|
3
3
|
import { getFeatureFlagKeysAllProducts } from '@atlaskit/media-common';
|
|
4
4
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
5
5
|
const packageName = "@atlaskit/media-viewer";
|
|
6
|
-
const packageVersion = "
|
|
6
|
+
const packageVersion = "0.0.0-development";
|
|
7
7
|
let ufoExperience;
|
|
8
8
|
const getExperience = () => {
|
|
9
9
|
if (!ufoExperience) {
|
|
@@ -18,6 +18,7 @@ import { createLoadSucceededEvent } from './analytics/events/operational/loadSuc
|
|
|
18
18
|
import { fireAnalytics, getFileAttributes } from './analytics';
|
|
19
19
|
import { InteractiveImg } from './viewers/image/interactive-img';
|
|
20
20
|
import ArchiveViewerLoader from './viewers/archiveSidebar/archiveViewerLoader';
|
|
21
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
21
22
|
import { startMediaFileUfoExperience, succeedMediaFileUfoExperience } from './analytics/ufoExperiences';
|
|
22
23
|
import { CustomViewer } from './viewers/customViewer/customViewer';
|
|
23
24
|
const ImageViewer = Loadable({
|
|
@@ -50,6 +51,59 @@ const SvgViewer = Loadable({
|
|
|
50
51
|
export const isExternalImageItem = fileItem => fileItem === 'external-image';
|
|
51
52
|
export const isFileStateItem = fileItem => !isExternalImageItem(fileItem);
|
|
52
53
|
export const MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER = 10 * 1024 * 1024;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Determines if a file renders natively without backend processing artifacts.
|
|
57
|
+
* These files (text/code, PDF, SVG) can be rendered directly from the original binary
|
|
58
|
+
* without waiting for transcoded artifacts from the backend.
|
|
59
|
+
*
|
|
60
|
+
* Evidence:
|
|
61
|
+
* - CodeViewer: Uses getFileBinaryURL(id) - works with both 'processing' and 'processed' status
|
|
62
|
+
* - DocViewer: Uses getDocumentContent(id) - only needs file ID, not artifacts
|
|
63
|
+
* - SvgViewer: Uses MediaSvg with identifier - never touches status or artifacts
|
|
64
|
+
*/
|
|
65
|
+
const canRenderWithoutProcessing = fileState => {
|
|
66
|
+
const {
|
|
67
|
+
mimeType,
|
|
68
|
+
name,
|
|
69
|
+
size
|
|
70
|
+
} = fileState;
|
|
71
|
+
|
|
72
|
+
// Text/code files via CodeViewer (10MB limit)
|
|
73
|
+
if (isCodeViewerItem(name, mimeType) && size <= MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// PDF files via DocViewer
|
|
78
|
+
if (mimeType === 'application/pdf') {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// SVG files via SvgViewer
|
|
83
|
+
if (mimeType === 'image/svg+xml') {
|
|
84
|
+
return true;
|
|
85
|
+
}
|
|
86
|
+
return false;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Creates a synthetic ProcessedFileState for native-rendered files.
|
|
91
|
+
* These files don't need backend processing, so we normalize to 'processed'
|
|
92
|
+
* to prevent unnecessary re-renders when the actual status changes.
|
|
93
|
+
* Note: artifacts are not used by native viewers (CodeViewer, DocViewer, SvgViewer)
|
|
94
|
+
* as they fetch the original binary directly via file ID.
|
|
95
|
+
*/
|
|
96
|
+
const createProcessedFileState = fileState => ({
|
|
97
|
+
status: 'processed',
|
|
98
|
+
id: fileState.id,
|
|
99
|
+
name: fileState.name,
|
|
100
|
+
size: fileState.size,
|
|
101
|
+
mediaType: fileState.mediaType,
|
|
102
|
+
mimeType: fileState.mimeType,
|
|
103
|
+
artifacts: {},
|
|
104
|
+
preview: fileState.preview,
|
|
105
|
+
createdAt: fileState.createdAt
|
|
106
|
+
});
|
|
53
107
|
export const ItemViewerBase = ({
|
|
54
108
|
identifier,
|
|
55
109
|
showControls,
|
|
@@ -97,34 +151,52 @@ export const ItemViewerBase = ({
|
|
|
97
151
|
fireAnalytics(createCommencedEvent(identifier === null || identifier === void 0 ? void 0 : identifier.id, traceContext), createAnalyticsEventRef.current);
|
|
98
152
|
startMediaFileUfoExperience();
|
|
99
153
|
}, [identifier, traceContext]);
|
|
154
|
+
const isNativeFileOptimizationEnabled = fg('media_viewer_prevent_rerender_on_polling');
|
|
100
155
|
useEffect(() => {
|
|
156
|
+
// External images don't need backend subscriptions
|
|
101
157
|
if (isExternalImageIdentifier(identifier)) {
|
|
102
|
-
// external images do not need to talk to our backend,
|
|
103
|
-
// so therefore no need for media-client subscriptions.
|
|
104
|
-
// just set a successful outcome of type "external-image".
|
|
105
158
|
setItem(Outcome.successful('external-image'));
|
|
106
159
|
return;
|
|
107
160
|
}
|
|
161
|
+
if (!fileState) {
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
const {
|
|
165
|
+
status
|
|
166
|
+
} = fileState;
|
|
167
|
+
|
|
168
|
+
// Track status flags for analytics
|
|
169
|
+
if (status === 'processing') {
|
|
170
|
+
fileStateFlagsRef.current.wasStatusProcessing = true;
|
|
171
|
+
} else if (status === 'uploading') {
|
|
172
|
+
fileStateFlagsRef.current.wasStatusUploading = true;
|
|
173
|
+
}
|
|
108
174
|
|
|
109
|
-
//
|
|
110
|
-
if (
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
if (fileState.status !== 'error') {
|
|
115
|
-
// updateFileStateFlag
|
|
175
|
+
// Handle error state
|
|
176
|
+
if (status === 'error') {
|
|
177
|
+
setItem(Outcome.failed(new MediaViewerError('itemviewer-fetch-metadata', toCommonMediaClientError(fileState))));
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
116
180
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
181
|
+
// Optimization: normalize native files to ProcessedFileState to prevent
|
|
182
|
+
// unnecessary re-renders when status changes (e.g., processing → processed)
|
|
183
|
+
if (isNativeFileOptimizationEnabled && canRenderWithoutProcessing(fileState)) {
|
|
184
|
+
setItem(prev => {
|
|
185
|
+
// Keep stable reference if we already have a processed state for this file
|
|
186
|
+
if (prev.status === 'SUCCESSFUL' && prev.data && isFileStateItem(prev.data) && prev.data.id === fileState.id && prev.data.status === 'processed') {
|
|
187
|
+
return prev;
|
|
121
188
|
}
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
189
|
+
// First load or different file: create normalized processed state
|
|
190
|
+
return Outcome.successful(createProcessedFileState(fileState));
|
|
191
|
+
});
|
|
192
|
+
return;
|
|
126
193
|
}
|
|
127
|
-
|
|
194
|
+
|
|
195
|
+
// Non-native files: standard behavior
|
|
196
|
+
setItem(Outcome.successful(fileState));
|
|
197
|
+
// fileState object reference changes often when polling items (especially during processing); only re-run when fileState.status changes
|
|
198
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
199
|
+
}, [isNativeFileOptimizationEnabled ? fileState === null || fileState === void 0 ? void 0 : fileState.status : fileState, identifier]);
|
|
128
200
|
const onSuccess = useCallback(() => {
|
|
129
201
|
item.whenSuccessful(fileItem => {
|
|
130
202
|
if (isFileStateItem(fileItem)) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ANALYTICS_MEDIA_CHANNEL, sanitiseAnalyticsPayload } from '@atlaskit/media-common/analytics';
|
|
2
2
|
var componentName = 'mediaViewer';
|
|
3
3
|
var packageName = "@atlaskit/media-viewer";
|
|
4
|
-
var packageVersion = "
|
|
4
|
+
var packageVersion = "0.0.0-development";
|
|
5
5
|
export { packageName, packageVersion, componentName, componentName as component };
|
|
6
6
|
export function getFileAttributes(fileState) {
|
|
7
7
|
if (!fileState) {
|
|
@@ -6,7 +6,7 @@ import { getMediaEnvironment, getMediaRegion } from '@atlaskit/media-client';
|
|
|
6
6
|
import { getFeatureFlagKeysAllProducts } from '@atlaskit/media-common';
|
|
7
7
|
import { fg } from '@atlaskit/platform-feature-flags';
|
|
8
8
|
var packageName = "@atlaskit/media-viewer";
|
|
9
|
-
var packageVersion = "
|
|
9
|
+
var packageVersion = "0.0.0-development";
|
|
10
10
|
var ufoExperience;
|
|
11
11
|
var getExperience = function getExperience() {
|
|
12
12
|
if (!ufoExperience) {
|
package/dist/esm/item-viewer.js
CHANGED
|
@@ -19,6 +19,7 @@ import { createLoadSucceededEvent } from './analytics/events/operational/loadSuc
|
|
|
19
19
|
import { fireAnalytics, getFileAttributes } from './analytics';
|
|
20
20
|
import { InteractiveImg } from './viewers/image/interactive-img';
|
|
21
21
|
import ArchiveViewerLoader from './viewers/archiveSidebar/archiveViewerLoader';
|
|
22
|
+
import { fg } from '@atlaskit/platform-feature-flags';
|
|
22
23
|
import { startMediaFileUfoExperience, succeedMediaFileUfoExperience } from './analytics/ufoExperiences';
|
|
23
24
|
import { CustomViewer } from './viewers/customViewer/customViewer';
|
|
24
25
|
var ImageViewer = Loadable({
|
|
@@ -92,6 +93,59 @@ export var isFileStateItem = function isFileStateItem(fileItem) {
|
|
|
92
93
|
return !isExternalImageItem(fileItem);
|
|
93
94
|
};
|
|
94
95
|
export var MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER = 10 * 1024 * 1024;
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Determines if a file renders natively without backend processing artifacts.
|
|
99
|
+
* These files (text/code, PDF, SVG) can be rendered directly from the original binary
|
|
100
|
+
* without waiting for transcoded artifacts from the backend.
|
|
101
|
+
*
|
|
102
|
+
* Evidence:
|
|
103
|
+
* - CodeViewer: Uses getFileBinaryURL(id) - works with both 'processing' and 'processed' status
|
|
104
|
+
* - DocViewer: Uses getDocumentContent(id) - only needs file ID, not artifacts
|
|
105
|
+
* - SvgViewer: Uses MediaSvg with identifier - never touches status or artifacts
|
|
106
|
+
*/
|
|
107
|
+
var canRenderWithoutProcessing = function canRenderWithoutProcessing(fileState) {
|
|
108
|
+
var mimeType = fileState.mimeType,
|
|
109
|
+
name = fileState.name,
|
|
110
|
+
size = fileState.size;
|
|
111
|
+
|
|
112
|
+
// Text/code files via CodeViewer (10MB limit)
|
|
113
|
+
if (isCodeViewerItem(name, mimeType) && size <= MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER) {
|
|
114
|
+
return true;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// PDF files via DocViewer
|
|
118
|
+
if (mimeType === 'application/pdf') {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// SVG files via SvgViewer
|
|
123
|
+
if (mimeType === 'image/svg+xml') {
|
|
124
|
+
return true;
|
|
125
|
+
}
|
|
126
|
+
return false;
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Creates a synthetic ProcessedFileState for native-rendered files.
|
|
131
|
+
* These files don't need backend processing, so we normalize to 'processed'
|
|
132
|
+
* to prevent unnecessary re-renders when the actual status changes.
|
|
133
|
+
* Note: artifacts are not used by native viewers (CodeViewer, DocViewer, SvgViewer)
|
|
134
|
+
* as they fetch the original binary directly via file ID.
|
|
135
|
+
*/
|
|
136
|
+
var createProcessedFileState = function createProcessedFileState(fileState) {
|
|
137
|
+
return {
|
|
138
|
+
status: 'processed',
|
|
139
|
+
id: fileState.id,
|
|
140
|
+
name: fileState.name,
|
|
141
|
+
size: fileState.size,
|
|
142
|
+
mediaType: fileState.mediaType,
|
|
143
|
+
mimeType: fileState.mimeType,
|
|
144
|
+
artifacts: {},
|
|
145
|
+
preview: fileState.preview,
|
|
146
|
+
createdAt: fileState.createdAt
|
|
147
|
+
};
|
|
148
|
+
};
|
|
95
149
|
export var ItemViewerBase = function ItemViewerBase(_ref) {
|
|
96
150
|
var identifier = _ref.identifier,
|
|
97
151
|
showControls = _ref.showControls,
|
|
@@ -140,32 +194,50 @@ export var ItemViewerBase = function ItemViewerBase(_ref) {
|
|
|
140
194
|
fireAnalytics(createCommencedEvent(identifier === null || identifier === void 0 ? void 0 : identifier.id, traceContext), createAnalyticsEventRef.current);
|
|
141
195
|
startMediaFileUfoExperience();
|
|
142
196
|
}, [identifier, traceContext]);
|
|
197
|
+
var isNativeFileOptimizationEnabled = fg('media_viewer_prevent_rerender_on_polling');
|
|
143
198
|
useEffect(function () {
|
|
199
|
+
// External images don't need backend subscriptions
|
|
144
200
|
if (isExternalImageIdentifier(identifier)) {
|
|
145
|
-
// external images do not need to talk to our backend,
|
|
146
|
-
// so therefore no need for media-client subscriptions.
|
|
147
|
-
// just set a successful outcome of type "external-image".
|
|
148
201
|
setItem(Outcome.successful('external-image'));
|
|
149
202
|
return;
|
|
150
203
|
}
|
|
204
|
+
if (!fileState) {
|
|
205
|
+
return;
|
|
206
|
+
}
|
|
207
|
+
var status = fileState.status;
|
|
208
|
+
|
|
209
|
+
// Track status flags for analytics
|
|
210
|
+
if (status === 'processing') {
|
|
211
|
+
fileStateFlagsRef.current.wasStatusProcessing = true;
|
|
212
|
+
} else if (status === 'uploading') {
|
|
213
|
+
fileStateFlagsRef.current.wasStatusUploading = true;
|
|
214
|
+
}
|
|
151
215
|
|
|
152
|
-
//
|
|
153
|
-
if (
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
216
|
+
// Handle error state
|
|
217
|
+
if (status === 'error') {
|
|
218
|
+
setItem(Outcome.failed(new MediaViewerError('itemviewer-fetch-metadata', toCommonMediaClientError(fileState))));
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
157
221
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
222
|
+
// Optimization: normalize native files to ProcessedFileState to prevent
|
|
223
|
+
// unnecessary re-renders when status changes (e.g., processing → processed)
|
|
224
|
+
if (isNativeFileOptimizationEnabled && canRenderWithoutProcessing(fileState)) {
|
|
225
|
+
setItem(function (prev) {
|
|
226
|
+
// Keep stable reference if we already have a processed state for this file
|
|
227
|
+
if (prev.status === 'SUCCESSFUL' && prev.data && isFileStateItem(prev.data) && prev.data.id === fileState.id && prev.data.status === 'processed') {
|
|
228
|
+
return prev;
|
|
162
229
|
}
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
230
|
+
// First load or different file: create normalized processed state
|
|
231
|
+
return Outcome.successful(createProcessedFileState(fileState));
|
|
232
|
+
});
|
|
233
|
+
return;
|
|
167
234
|
}
|
|
168
|
-
|
|
235
|
+
|
|
236
|
+
// Non-native files: standard behavior
|
|
237
|
+
setItem(Outcome.successful(fileState));
|
|
238
|
+
// fileState object reference changes often when polling items (especially during processing); only re-run when fileState.status changes
|
|
239
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
240
|
+
}, [isNativeFileOptimizationEnabled ? fileState === null || fileState === void 0 ? void 0 : fileState.status : fileState, identifier]);
|
|
169
241
|
var onSuccess = useCallback(function () {
|
|
170
242
|
item.whenSuccessful(function (fileItem) {
|
|
171
243
|
if (isFileStateItem(fileItem)) {
|
|
@@ -10,7 +10,7 @@ type Children = {
|
|
|
10
10
|
type OnClick = {
|
|
11
11
|
onClick: (event: MouseEvent<HTMLDivElement>) => void;
|
|
12
12
|
};
|
|
13
|
-
export declare const ArchiveItemViewerWrapper: ({ children, fullHeight }: Children & {
|
|
13
|
+
export declare const ArchiveItemViewerWrapper: ({ children, fullHeight, }: Children & {
|
|
14
14
|
fullHeight?: boolean;
|
|
15
15
|
}) => JSX.Element;
|
|
16
16
|
export declare const ArchiveSideBar: import("react").ForwardRefExoticComponent<Children & import("react").RefAttributes<HTMLDivElement>>;
|
|
@@ -10,7 +10,7 @@ type Children = {
|
|
|
10
10
|
type OnClick = {
|
|
11
11
|
onClick: (event: MouseEvent<HTMLDivElement>) => void;
|
|
12
12
|
};
|
|
13
|
-
export declare const ArchiveItemViewerWrapper: ({ children, fullHeight }: Children & {
|
|
13
|
+
export declare const ArchiveItemViewerWrapper: ({ children, fullHeight, }: Children & {
|
|
14
14
|
fullHeight?: boolean;
|
|
15
15
|
}) => JSX.Element;
|
|
16
16
|
export declare const ArchiveSideBar: import("react").ForwardRefExoticComponent<Children & import("react").RefAttributes<HTMLDivElement>>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@atlaskit/media-viewer",
|
|
3
|
-
"version": "52.5.
|
|
3
|
+
"version": "52.5.1",
|
|
4
4
|
"description": "MediaViewer is Atlassian's powerful solution for viewing files on the web. It's both powerful and extendable yet easy-to-integrate",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"registry": "https://registry.npmjs.org/"
|
|
@@ -32,15 +32,15 @@
|
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
34
|
"@atlaskit/analytics-next": "^11.1.0",
|
|
35
|
-
"@atlaskit/button": "^23.
|
|
36
|
-
"@atlaskit/code": "^17.
|
|
35
|
+
"@atlaskit/button": "^23.7.0",
|
|
36
|
+
"@atlaskit/code": "^17.4.0",
|
|
37
37
|
"@atlaskit/css": "^0.17.0",
|
|
38
|
-
"@atlaskit/form": "^
|
|
38
|
+
"@atlaskit/form": "^15.0.0",
|
|
39
39
|
"@atlaskit/heading": "^5.2.0",
|
|
40
40
|
"@atlaskit/icon": "^29.0.0",
|
|
41
41
|
"@atlaskit/icon-file-type": "^7.0.0",
|
|
42
42
|
"@atlaskit/icon-lab": "^5.12.0",
|
|
43
|
-
"@atlaskit/media-client": "^35.
|
|
43
|
+
"@atlaskit/media-client": "^35.7.0",
|
|
44
44
|
"@atlaskit/media-client-react": "^4.1.0",
|
|
45
45
|
"@atlaskit/media-common": "^12.3.0",
|
|
46
46
|
"@atlaskit/media-document-viewer": "^0.6.0",
|
|
@@ -51,10 +51,10 @@
|
|
|
51
51
|
"@atlaskit/primitives": "^16.4.0",
|
|
52
52
|
"@atlaskit/side-navigation": "^11.0.0",
|
|
53
53
|
"@atlaskit/spinner": "^19.0.0",
|
|
54
|
-
"@atlaskit/textfield": "^8.
|
|
54
|
+
"@atlaskit/textfield": "^8.2.0",
|
|
55
55
|
"@atlaskit/theme": "^21.0.0",
|
|
56
56
|
"@atlaskit/tokens": "^8.4.0",
|
|
57
|
-
"@atlaskit/tooltip": "^20.
|
|
57
|
+
"@atlaskit/tooltip": "^20.11.0",
|
|
58
58
|
"@atlaskit/ufo": "^0.4.0",
|
|
59
59
|
"@babel/runtime": "^7.0.0",
|
|
60
60
|
"@codemirror/language": "6.10.8",
|
|
@@ -89,7 +89,7 @@
|
|
|
89
89
|
"@atlaskit/media-test-data": "^3.2.0",
|
|
90
90
|
"@atlaskit/media-test-helpers": "^39.0.0",
|
|
91
91
|
"@atlaskit/ssr": "workspace:^",
|
|
92
|
-
"@atlaskit/toggle": "^15.
|
|
92
|
+
"@atlaskit/toggle": "^15.2.0",
|
|
93
93
|
"@atlassian/feature-flags-test-utils": "^1.0.0",
|
|
94
94
|
"@atlassian/ufo": "^0.7.0",
|
|
95
95
|
"@testing-library/dom": "^10.1.0",
|
|
@@ -122,6 +122,9 @@
|
|
|
122
122
|
},
|
|
123
123
|
"media_document_viewer": {
|
|
124
124
|
"type": "boolean"
|
|
125
|
+
},
|
|
126
|
+
"media_viewer_prevent_rerender_on_polling": {
|
|
127
|
+
"type": "boolean"
|
|
125
128
|
}
|
|
126
129
|
},
|
|
127
130
|
"techstack": {
|