@atlaskit/media-viewer 54.1.1 → 54.2.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 CHANGED
@@ -1,5 +1,30 @@
1
1
  # @atlaskit/media-viewer
2
2
 
3
+ ## 54.2.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [`ee28cf33718b0`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/ee28cf33718b0) -
8
+ Add @atlassian/react-compiler-gating as a runtime dependency to enable React Compiler platform
9
+ gating.
10
+ - Updated dependencies
11
+
12
+ ## 54.2.0
13
+
14
+ ### Minor Changes
15
+
16
+ - [`a56e76c8f33af`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/a56e76c8f33af) -
17
+ Add processingFailReason to media-viewer loadFailed analytics for failed-processing root-cause
18
+ analysis (BMPT-8000)
19
+ - [`758e07b536b13`](https://bitbucket.org/atlassian/atlassian-frontend-monorepo/commits/758e07b536b13) -
20
+ [ux] Show a clear "Unsupported file format" message for non-ZIP archives (e.g. 7z, tar, gzip) in
21
+ the media viewer instead of a generic error, and remove the empty sidebar gap. Behind feature gate
22
+ platform_media_archive_zip_guard.
23
+
24
+ ### Patch Changes
25
+
26
+ - Updated dependencies
27
+
3
28
  ## 54.1.1
4
29
 
5
30
  ### Patch Changes
@@ -7,11 +7,13 @@ exports.createLoadFailedEvent = void 0;
7
7
  var _ = require("../..");
8
8
  var _errors = require("../../../errors");
9
9
  var createLoadFailedEvent = exports.createLoadFailedEvent = function createLoadFailedEvent(fileId, error, fileState, traceContext) {
10
+ var _failReason;
10
11
  var _getFileAttributes = (0, _.getFileAttributes)(fileState),
11
12
  fileMediatype = _getFileAttributes.fileMediatype,
12
13
  fileMimetype = _getFileAttributes.fileMimetype,
13
14
  fileSize = _getFileAttributes.fileSize;
14
15
  var requestMetadata = (0, _errors.getRequestMetadata)(error);
16
+ var processingFailReason = (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'failed-processing' ? (_failReason = fileState.failReason) !== null && _failReason !== void 0 ? _failReason : 'not-available' : undefined;
15
17
  return {
16
18
  eventType: 'operational',
17
19
  actionSubject: 'mediaFile',
@@ -23,6 +25,7 @@ var createLoadFailedEvent = exports.createLoadFailedEvent = function createLoadF
23
25
  errorDetail: (0, _errors.getErrorDetail)(error),
24
26
  statusCode: requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.statusCode,
25
27
  request: requestMetadata,
28
+ processingFailReason: processingFailReason,
26
29
  fileMimetype: fileMimetype,
27
30
  fileAttributes: {
28
31
  fileId: fileId,
@@ -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 = "54.1.0";
13
+ var packageVersion = exports.packageVersion = "54.2.0";
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 = "54.1.0";
16
+ var packageVersion = "54.2.0";
17
17
  var ufoExperience;
18
18
  var getExperience = function getExperience() {
19
19
  if (!ufoExperience) {
@@ -51,6 +51,16 @@ var ErrorMessage = exports.ErrorMessage = /*#__PURE__*/function (_React$Componen
51
51
  var _this$props = this.props,
52
52
  formatMessage = _this$props.intl.formatMessage,
53
53
  error = _this$props.error;
54
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) aren't a load failure - the
55
+ // browser-based viewer simply can't preview them. Show a dedicated
56
+ // "Unsupported file format" heading instead of the generic
57
+ // "Something went wrong" copy.
58
+ if ((0, _errors.getPrimaryErrorReason)(error) === 'archiveviewer-not-zip') {
59
+ return {
60
+ icon: errorLoadingFileImage(formatMessage),
61
+ messages: [_mediaUi.messages.unsupported_file_format, _mediaUi.messages.archive_format_not_supported]
62
+ };
63
+ }
54
64
  var errorInfo = {
55
65
  icon: errorLoadingFileImage(formatMessage),
56
66
  messages: [_mediaUi.messages.something_went_wrong, _mediaUi.messages.couldnt_generate_preview]
@@ -11,6 +11,8 @@ var _react = _interopRequireWildcard(require("react"));
11
11
  var _mediaClient = require("@atlaskit/media-client");
12
12
  var _mediaUi = require("@atlaskit/media-ui");
13
13
  var _codeViewer = require("@atlaskit/media-ui/codeViewer");
14
+ var _isZipMimeType = require("@atlaskit/media-common/isZipMimeType");
15
+ var _platformFeatureFlags = require("@atlaskit/platform-feature-flags");
14
16
  var _reactIntl = require("react-intl");
15
17
  var _domain = require("./domain");
16
18
  var _styleWrappers = require("./styleWrappers");
@@ -83,7 +85,15 @@ var Header = exports.Header = function Header(_ref) {
83
85
  }
84
86
  if (fileState.status !== 'error') {
85
87
  var _onSetArchiveSideBarV;
86
- (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 || _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !(0, _mediaClient.isErrorFileState)(fileState) && fileState.mediaType === 'archive');
88
+ // Only reserve the archive sidebar space for previewable ZIP archives.
89
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) render a full-width "unsupported
90
+ // file format" error in the body and have no sidebar, so the header
91
+ // must not leave an empty 300px gap for them.
92
+ (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 || _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !(0, _mediaClient.isErrorFileState)(fileState) && fileState.mediaType === 'archive' && (
93
+ // When the zip guard is on, non-ZIP archives show a full-width
94
+ // "unsupported file format" error with no sidebar, so the header
95
+ // must not reserve the 300px sidebar space for them.
96
+ !(0, _platformFeatureFlags.fg)('platform_media_archive_zip_guard') || (0, _isZipMimeType.isZipMimeType)(fileState.mimeType)));
87
97
  setItem(_domain.Outcome.successful(fileState));
88
98
  } else {
89
99
  setItem(_domain.Outcome.failed(new _errors.MediaViewerError('header-fetch-metadata', (0, _mediaClient.toCommonMediaClientError)(fileState))));
@@ -68,7 +68,14 @@ var ArchiveSidebarRenderer = exports.default = /*#__PURE__*/function (_Component
68
68
  this.setState({
69
69
  status: 'loaded'
70
70
  });
71
- onError(new _errors.ArchiveViewerError('archiveviewer-read-binary', _t instanceof Error ? _t : undefined));
71
+ // Preserve archiveviewer-not-zip as-is so the generic error boundary
72
+ // can show a clear "format not supported" message instead of a
73
+ // generic read error.
74
+ if (_t instanceof _errors.ArchiveViewerError && _t.primaryReason === 'archiveviewer-not-zip') {
75
+ onError(_t);
76
+ } else {
77
+ onError(new _errors.ArchiveViewerError('archiveviewer-read-binary', _t instanceof Error ? _t : undefined));
78
+ }
72
79
  case 4:
73
80
  case "end":
74
81
  return _context.stop();
@@ -16,6 +16,8 @@ var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/
16
16
  var _react = _interopRequireDefault(require("react"));
17
17
  var _unzipit = require("unzipit");
18
18
  var _reactIntl = require("react-intl");
19
+ var _mediaClient = require("@atlaskit/media-client");
20
+ var _isZipMimeType = require("@atlaskit/media-common/isZipMimeType");
19
21
  var _mediaUi = require("@atlaskit/media-ui");
20
22
  var _codeViewer = require("@atlaskit/media-ui/codeViewer");
21
23
  var _compiled = require("@atlaskit/primitives/compiled");
@@ -35,6 +37,7 @@ var _analytics = require("../../analytics");
35
37
  var _errors = require("../../errors");
36
38
  var _zipEntryLoadSucceeded = require("../../analytics/events/operational/zipEntryLoadSucceeded");
37
39
  var _zipEntryLoadFailed = require("../../analytics/events/operational/zipEntryLoadFailed");
40
+ var _previewUnsupported = require("../../analytics/events/operational/previewUnsupported");
38
41
  var _codeViewerRenderer = require("../codeViewer/codeViewerRenderer");
39
42
  var _util = require("../codeViewer/util");
40
43
  var _itemViewer = require("../../item-viewer");
@@ -50,19 +53,25 @@ var getArchiveEntriesFromFileState = exports.getArchiveEntriesFromFileState = /*
50
53
  return _regenerator.default.wrap(function (_context) {
51
54
  while (1) switch (_context.prev = _context.next) {
52
55
  case 0:
53
- _context.next = 1;
54
- return mediaClient.file.getFileBinaryURL(fileState.id, collectionName);
56
+ if (!(!(0, _mediaClient.isErrorFileState)(fileState) && !(0, _isZipMimeType.isZipMimeType)(fileState.mimeType) && (0, _platformFeatureFlags.fg)('platform_media_archive_zip_guard'))) {
57
+ _context.next = 1;
58
+ break;
59
+ }
60
+ throw new _errors.ArchiveViewerError('archiveviewer-not-zip');
55
61
  case 1:
62
+ _context.next = 2;
63
+ return mediaClient.file.getFileBinaryURL(fileState.id, collectionName);
64
+ case 2:
56
65
  url = _context.sent;
57
66
  reader = new _unzipit.HTTPRangeReader(url);
58
- _context.next = 2;
67
+ _context.next = 3;
59
68
  return (0, _utils.rejectAfter)(function () {
60
69
  return (0, _unzipit.unzip)(reader);
61
70
  });
62
- case 2:
71
+ case 3:
63
72
  archive = _context.sent;
64
73
  return _context.abrupt("return", archive);
65
- case 3:
74
+ case 4:
66
75
  case "end":
67
76
  return _context.stop();
68
77
  }
@@ -81,6 +90,25 @@ var ArchiveViewerBase = exports.ArchiveViewerBase = /*#__PURE__*/function (_Base
81
90
  }
82
91
  _this = _callSuper(this, ArchiveViewerBase, [].concat(args));
83
92
  (0, _defineProperty2.default)(_this, "onError", function (error, entry) {
93
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) are a known limitation of the
94
+ // browser-based `unzipit` viewer, not a per-entry failure. Render a
95
+ // full-width "format not supported" message via BaseViewer's failed
96
+ // outcome (no sidebar, no 300px offset) and do NOT escalate to the
97
+ // parent item-viewer error boundary, which would double-render the
98
+ // message inside the offset archive layout.
99
+ if (error.primaryReason === 'archiveviewer-not-zip') {
100
+ // Fire a dedicated, non-SLI `previewUnsupported` event (not a
101
+ // `loadFailed`) so unsupported archives don't pollute error
102
+ // dashboards. The event's fileAttributes distinguish the two cases:
103
+ // `fileMediatype: 'unknown'` (browser-unrecognised, e.g. RAR) vs
104
+ // `fileMediatype: 'archive'` with a non-ZIP `fileMimetype` (unzipit
105
+ // limitation, e.g. 7z/tar/gzip).
106
+ (0, _analytics.fireAnalytics)((0, _previewUnsupported.createPreviewUnsupportedEvent)(_this.props.item), _this.props.createAnalyticsEvent);
107
+ _this.setState({
108
+ content: _domain.Outcome.failed(error)
109
+ });
110
+ return;
111
+ }
84
112
  _this.props.onError(error);
85
113
  _this.setState({
86
114
  content: _domain.Outcome.successful(_objectSpread(_objectSpread({}, _this.state.content.data), {}, {
@@ -1,12 +1,14 @@
1
1
  import { getFileAttributes } from '../..';
2
2
  import { getPrimaryErrorReason, getSecondaryErrorReason, getErrorDetail, getRequestMetadata } from '../../../errors';
3
3
  export const createLoadFailedEvent = (fileId, error, fileState, traceContext) => {
4
+ var _failReason;
4
5
  const {
5
6
  fileMediatype,
6
7
  fileMimetype,
7
8
  fileSize
8
9
  } = getFileAttributes(fileState);
9
10
  const requestMetadata = getRequestMetadata(error);
11
+ const processingFailReason = (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'failed-processing' ? (_failReason = fileState.failReason) !== null && _failReason !== void 0 ? _failReason : 'not-available' : undefined;
10
12
  return {
11
13
  eventType: 'operational',
12
14
  actionSubject: 'mediaFile',
@@ -18,6 +20,7 @@ export const createLoadFailedEvent = (fileId, error, fileState, traceContext) =>
18
20
  errorDetail: getErrorDetail(error),
19
21
  statusCode: requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.statusCode,
20
22
  request: requestMetadata,
23
+ processingFailReason,
21
24
  fileMimetype,
22
25
  fileAttributes: {
23
26
  fileId,
@@ -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 = "54.1.0";
4
+ const packageVersion = "54.2.0";
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 = "54.1.0";
6
+ const packageVersion = "54.2.0";
7
7
  let ufoExperience;
8
8
  const getExperience = () => {
9
9
  if (!ufoExperience) {
@@ -26,6 +26,16 @@ export class ErrorMessage extends React.Component {
26
26
  },
27
27
  error
28
28
  } = this.props;
29
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) aren't a load failure - the
30
+ // browser-based viewer simply can't preview them. Show a dedicated
31
+ // "Unsupported file format" heading instead of the generic
32
+ // "Something went wrong" copy.
33
+ if (getPrimaryErrorReason(error) === 'archiveviewer-not-zip') {
34
+ return {
35
+ icon: errorLoadingFileImage(formatMessage),
36
+ messages: [i18nMessages.unsupported_file_format, i18nMessages.archive_format_not_supported]
37
+ };
38
+ }
29
39
  const errorInfo = {
30
40
  icon: errorLoadingFileImage(formatMessage),
31
41
  messages: [i18nMessages.something_went_wrong, i18nMessages.couldnt_generate_preview]
@@ -2,6 +2,8 @@ import React, { useEffect, useRef, useState } from 'react';
2
2
  import { isExternalImageIdentifier, isErrorFileState, toCommonMediaClientError } from '@atlaskit/media-client';
3
3
  import { hideControlsClassName, messages, toHumanReadableMediaSize, MediaButton } from '@atlaskit/media-ui';
4
4
  import { getLanguageType, getExtension, isCodeViewerItem } from '@atlaskit/media-ui/codeViewer';
5
+ import { isZipMimeType } from '@atlaskit/media-common/isZipMimeType';
6
+ import { fg } from '@atlaskit/platform-feature-flags';
5
7
  import { FormattedMessage, injectIntl } from 'react-intl';
6
8
  import { Outcome } from './domain';
7
9
  import { Header as HeaderWrapper, LeftHeader, RightHeader, MetadataWrapper, MetadataSubText, MedatadataTextWrapper, MetadataIconWrapper, MetadataFileName, FormattedMessageWrapper } from './styleWrappers';
@@ -70,7 +72,15 @@ export const Header = ({
70
72
  }
71
73
  if (fileState.status !== 'error') {
72
74
  var _onSetArchiveSideBarV;
73
- (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 ? void 0 : _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !isErrorFileState(fileState) && fileState.mediaType === 'archive');
75
+ // Only reserve the archive sidebar space for previewable ZIP archives.
76
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) render a full-width "unsupported
77
+ // file format" error in the body and have no sidebar, so the header
78
+ // must not leave an empty 300px gap for them.
79
+ (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 ? void 0 : _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !isErrorFileState(fileState) && fileState.mediaType === 'archive' && (
80
+ // When the zip guard is on, non-ZIP archives show a full-width
81
+ // "unsupported file format" error with no sidebar, so the header
82
+ // must not reserve the 300px sidebar space for them.
83
+ !fg('platform_media_archive_zip_guard') || isZipMimeType(fileState.mimeType)));
74
84
  setItem(Outcome.successful(fileState));
75
85
  } else {
76
86
  setItem(Outcome.failed(new MediaViewerError('header-fetch-metadata', toCommonMediaClientError(fileState))));
@@ -35,7 +35,14 @@ export default class ArchiveSidebarRenderer extends Component {
35
35
  this.setState({
36
36
  status: 'loaded'
37
37
  });
38
- onError(new ArchiveViewerError('archiveviewer-read-binary', error instanceof Error ? error : undefined));
38
+ // Preserve archiveviewer-not-zip as-is so the generic error boundary
39
+ // can show a clear "format not supported" message instead of a
40
+ // generic read error.
41
+ if (error instanceof ArchiveViewerError && error.primaryReason === 'archiveviewer-not-zip') {
42
+ onError(error);
43
+ } else {
44
+ onError(new ArchiveViewerError('archiveviewer-read-binary', error instanceof Error ? error : undefined));
45
+ }
39
46
  }
40
47
  }
41
48
  render() {
@@ -3,6 +3,8 @@ import _defineProperty from "@babel/runtime/helpers/defineProperty";
3
3
  import React from 'react';
4
4
  import { unzip, HTTPRangeReader } from 'unzipit';
5
5
  import { FormattedMessage } from 'react-intl';
6
+ import { isErrorFileState } from '@atlaskit/media-client';
7
+ import { isZipMimeType } from '@atlaskit/media-common/isZipMimeType';
6
8
  import { CustomMediaPlayer, messages } from '@atlaskit/media-ui';
7
9
  import { getLanguageType, isCodeViewerItem } from '@atlaskit/media-ui/codeViewer';
8
10
  import { Text } from '@atlaskit/primitives/compiled';
@@ -22,12 +24,23 @@ import { fireAnalytics } from '../../analytics';
22
24
  import { ArchiveViewerError, isMediaViewerError } from '../../errors';
23
25
  import { createZipEntryLoadSucceededEvent } from '../../analytics/events/operational/zipEntryLoadSucceeded';
24
26
  import { createZipEntryLoadFailedEvent } from '../../analytics/events/operational/zipEntryLoadFailed';
27
+ import { createPreviewUnsupportedEvent } from '../../analytics/events/operational/previewUnsupported';
25
28
  import { CodeViewRenderer } from '../codeViewer/codeViewerRenderer';
26
29
  import { DEFAULT_LANGUAGE } from '../codeViewer/util';
27
30
  import { MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER } from '../../item-viewer';
28
31
  import { NativePdfViewer } from './nativePdfViewer';
29
32
  import { fg } from '@atlaskit/platform-feature-flags';
30
33
  export const getArchiveEntriesFromFileState = async (fileState, mediaClient, collectionName) => {
34
+ // The browser-based archive viewer relies on `unzipit`, which only supports
35
+ // the ZIP container format. Media classifies 40+ archive formats (.tar, .gz,
36
+ // .rar, .7z, etc.) as `archive` and routes them all through this code path,
37
+ // causing unzipit to fail with a cryptic "could not find end of central
38
+ // directory" error. We short-circuit non-ZIP archives up-front so the UI
39
+ // can show a clear "format not supported" message instead. We rely on the
40
+ // file's mime type (provided by the media backend).
41
+ if (!isErrorFileState(fileState) && !isZipMimeType(fileState.mimeType) && fg('platform_media_archive_zip_guard')) {
42
+ throw new ArchiveViewerError('archiveviewer-not-zip');
43
+ }
31
44
  const url = await mediaClient.file.getFileBinaryURL(fileState.id, collectionName);
32
45
  const reader = new HTTPRangeReader(url);
33
46
  const archive = await rejectAfter(() => unzip(reader));
@@ -37,6 +50,25 @@ export class ArchiveViewerBase extends BaseViewer {
37
50
  constructor(...args) {
38
51
  super(...args);
39
52
  _defineProperty(this, "onError", (error, entry) => {
53
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) are a known limitation of the
54
+ // browser-based `unzipit` viewer, not a per-entry failure. Render a
55
+ // full-width "format not supported" message via BaseViewer's failed
56
+ // outcome (no sidebar, no 300px offset) and do NOT escalate to the
57
+ // parent item-viewer error boundary, which would double-render the
58
+ // message inside the offset archive layout.
59
+ if (error.primaryReason === 'archiveviewer-not-zip') {
60
+ // Fire a dedicated, non-SLI `previewUnsupported` event (not a
61
+ // `loadFailed`) so unsupported archives don't pollute error
62
+ // dashboards. The event's fileAttributes distinguish the two cases:
63
+ // `fileMediatype: 'unknown'` (browser-unrecognised, e.g. RAR) vs
64
+ // `fileMediatype: 'archive'` with a non-ZIP `fileMimetype` (unzipit
65
+ // limitation, e.g. 7z/tar/gzip).
66
+ fireAnalytics(createPreviewUnsupportedEvent(this.props.item), this.props.createAnalyticsEvent);
67
+ this.setState({
68
+ content: Outcome.failed(error)
69
+ });
70
+ return;
71
+ }
40
72
  this.props.onError(error);
41
73
  this.setState({
42
74
  content: Outcome.successful({
@@ -1,11 +1,13 @@
1
1
  import { getFileAttributes } from '../..';
2
2
  import { getPrimaryErrorReason, getSecondaryErrorReason, getErrorDetail, getRequestMetadata } from '../../../errors';
3
3
  export var createLoadFailedEvent = function createLoadFailedEvent(fileId, error, fileState, traceContext) {
4
+ var _failReason;
4
5
  var _getFileAttributes = getFileAttributes(fileState),
5
6
  fileMediatype = _getFileAttributes.fileMediatype,
6
7
  fileMimetype = _getFileAttributes.fileMimetype,
7
8
  fileSize = _getFileAttributes.fileSize;
8
9
  var requestMetadata = getRequestMetadata(error);
10
+ var processingFailReason = (fileState === null || fileState === void 0 ? void 0 : fileState.status) === 'failed-processing' ? (_failReason = fileState.failReason) !== null && _failReason !== void 0 ? _failReason : 'not-available' : undefined;
9
11
  return {
10
12
  eventType: 'operational',
11
13
  actionSubject: 'mediaFile',
@@ -17,6 +19,7 @@ export var createLoadFailedEvent = function createLoadFailedEvent(fileId, error,
17
19
  errorDetail: getErrorDetail(error),
18
20
  statusCode: requestMetadata === null || requestMetadata === void 0 ? void 0 : requestMetadata.statusCode,
19
21
  request: requestMetadata,
22
+ processingFailReason: processingFailReason,
20
23
  fileMimetype: fileMimetype,
21
24
  fileAttributes: {
22
25
  fileId: fileId,
@@ -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 = "54.1.0";
4
+ var packageVersion = "54.2.0";
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 = "54.1.0";
9
+ var packageVersion = "54.2.0";
10
10
  var ufoExperience;
11
11
  var getExperience = function getExperience() {
12
12
  if (!ufoExperience) {
@@ -44,6 +44,16 @@ export var ErrorMessage = /*#__PURE__*/function (_React$Component) {
44
44
  var _this$props = this.props,
45
45
  formatMessage = _this$props.intl.formatMessage,
46
46
  error = _this$props.error;
47
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) aren't a load failure - the
48
+ // browser-based viewer simply can't preview them. Show a dedicated
49
+ // "Unsupported file format" heading instead of the generic
50
+ // "Something went wrong" copy.
51
+ if (getPrimaryErrorReason(error) === 'archiveviewer-not-zip') {
52
+ return {
53
+ icon: errorLoadingFileImage(formatMessage),
54
+ messages: [i18nMessages.unsupported_file_format, i18nMessages.archive_format_not_supported]
55
+ };
56
+ }
47
57
  var errorInfo = {
48
58
  icon: errorLoadingFileImage(formatMessage),
49
59
  messages: [i18nMessages.something_went_wrong, i18nMessages.couldnt_generate_preview]
@@ -3,6 +3,8 @@ import React, { useEffect, useRef, useState } from 'react';
3
3
  import { isExternalImageIdentifier, isErrorFileState, toCommonMediaClientError } from '@atlaskit/media-client';
4
4
  import { hideControlsClassName, messages, toHumanReadableMediaSize, MediaButton } from '@atlaskit/media-ui';
5
5
  import { getLanguageType, getExtension, isCodeViewerItem } from '@atlaskit/media-ui/codeViewer';
6
+ import { isZipMimeType } from '@atlaskit/media-common/isZipMimeType';
7
+ import { fg } from '@atlaskit/platform-feature-flags';
6
8
  import { FormattedMessage, injectIntl } from 'react-intl';
7
9
  import { Outcome } from './domain';
8
10
  import { Header as HeaderWrapper, LeftHeader, RightHeader, MetadataWrapper, MetadataSubText, MedatadataTextWrapper, MetadataIconWrapper, MetadataFileName, FormattedMessageWrapper } from './styleWrappers';
@@ -74,7 +76,15 @@ export var Header = function Header(_ref) {
74
76
  }
75
77
  if (fileState.status !== 'error') {
76
78
  var _onSetArchiveSideBarV;
77
- (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 || _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !isErrorFileState(fileState) && fileState.mediaType === 'archive');
79
+ // Only reserve the archive sidebar space for previewable ZIP archives.
80
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) render a full-width "unsupported
81
+ // file format" error in the body and have no sidebar, so the header
82
+ // must not leave an empty 300px gap for them.
83
+ (_onSetArchiveSideBarV = onSetArchiveSideBarVisibleRef.current) === null || _onSetArchiveSideBarV === void 0 || _onSetArchiveSideBarV.call(onSetArchiveSideBarVisibleRef, !isErrorFileState(fileState) && fileState.mediaType === 'archive' && (
84
+ // When the zip guard is on, non-ZIP archives show a full-width
85
+ // "unsupported file format" error with no sidebar, so the header
86
+ // must not reserve the 300px sidebar space for them.
87
+ !fg('platform_media_archive_zip_guard') || isZipMimeType(fileState.mimeType)));
78
88
  setItem(Outcome.successful(fileState));
79
89
  } else {
80
90
  setItem(Outcome.failed(new MediaViewerError('header-fetch-metadata', toCommonMediaClientError(fileState))));
@@ -59,7 +59,14 @@ var ArchiveSidebarRenderer = /*#__PURE__*/function (_Component) {
59
59
  this.setState({
60
60
  status: 'loaded'
61
61
  });
62
- onError(new ArchiveViewerError('archiveviewer-read-binary', _t instanceof Error ? _t : undefined));
62
+ // Preserve archiveviewer-not-zip as-is so the generic error boundary
63
+ // can show a clear "format not supported" message instead of a
64
+ // generic read error.
65
+ if (_t instanceof ArchiveViewerError && _t.primaryReason === 'archiveviewer-not-zip') {
66
+ onError(_t);
67
+ } else {
68
+ onError(new ArchiveViewerError('archiveviewer-read-binary', _t instanceof Error ? _t : undefined));
69
+ }
63
70
  case 4:
64
71
  case "end":
65
72
  return _context.stop();
@@ -14,6 +14,8 @@ import _regeneratorRuntime from "@babel/runtime/regenerator";
14
14
  import React from 'react';
15
15
  import { unzip, HTTPRangeReader } from 'unzipit';
16
16
  import { FormattedMessage } from 'react-intl';
17
+ import { isErrorFileState } from '@atlaskit/media-client';
18
+ import { isZipMimeType } from '@atlaskit/media-common/isZipMimeType';
17
19
  import { CustomMediaPlayer, messages } from '@atlaskit/media-ui';
18
20
  import { getLanguageType, isCodeViewerItem } from '@atlaskit/media-ui/codeViewer';
19
21
  import { Text } from '@atlaskit/primitives/compiled';
@@ -33,6 +35,7 @@ import { fireAnalytics } from '../../analytics';
33
35
  import { ArchiveViewerError, isMediaViewerError } from '../../errors';
34
36
  import { createZipEntryLoadSucceededEvent } from '../../analytics/events/operational/zipEntryLoadSucceeded';
35
37
  import { createZipEntryLoadFailedEvent } from '../../analytics/events/operational/zipEntryLoadFailed';
38
+ import { createPreviewUnsupportedEvent } from '../../analytics/events/operational/previewUnsupported';
36
39
  import { CodeViewRenderer } from '../codeViewer/codeViewerRenderer';
37
40
  import { DEFAULT_LANGUAGE } from '../codeViewer/util';
38
41
  import { MAX_FILE_SIZE_SUPPORTED_BY_CODEVIEWER } from '../../item-viewer';
@@ -44,19 +47,25 @@ export var getArchiveEntriesFromFileState = /*#__PURE__*/function () {
44
47
  return _regeneratorRuntime.wrap(function (_context) {
45
48
  while (1) switch (_context.prev = _context.next) {
46
49
  case 0:
47
- _context.next = 1;
48
- return mediaClient.file.getFileBinaryURL(fileState.id, collectionName);
50
+ if (!(!isErrorFileState(fileState) && !isZipMimeType(fileState.mimeType) && fg('platform_media_archive_zip_guard'))) {
51
+ _context.next = 1;
52
+ break;
53
+ }
54
+ throw new ArchiveViewerError('archiveviewer-not-zip');
49
55
  case 1:
56
+ _context.next = 2;
57
+ return mediaClient.file.getFileBinaryURL(fileState.id, collectionName);
58
+ case 2:
50
59
  url = _context.sent;
51
60
  reader = new HTTPRangeReader(url);
52
- _context.next = 2;
61
+ _context.next = 3;
53
62
  return rejectAfter(function () {
54
63
  return unzip(reader);
55
64
  });
56
- case 2:
65
+ case 3:
57
66
  archive = _context.sent;
58
67
  return _context.abrupt("return", archive);
59
- case 3:
68
+ case 4:
60
69
  case "end":
61
70
  return _context.stop();
62
71
  }
@@ -75,6 +84,25 @@ export var ArchiveViewerBase = /*#__PURE__*/function (_BaseViewer) {
75
84
  }
76
85
  _this = _callSuper(this, ArchiveViewerBase, [].concat(args));
77
86
  _defineProperty(_this, "onError", function (error, entry) {
87
+ // Non-ZIP archives (e.g. RAR, TAR, 7z) are a known limitation of the
88
+ // browser-based `unzipit` viewer, not a per-entry failure. Render a
89
+ // full-width "format not supported" message via BaseViewer's failed
90
+ // outcome (no sidebar, no 300px offset) and do NOT escalate to the
91
+ // parent item-viewer error boundary, which would double-render the
92
+ // message inside the offset archive layout.
93
+ if (error.primaryReason === 'archiveviewer-not-zip') {
94
+ // Fire a dedicated, non-SLI `previewUnsupported` event (not a
95
+ // `loadFailed`) so unsupported archives don't pollute error
96
+ // dashboards. The event's fileAttributes distinguish the two cases:
97
+ // `fileMediatype: 'unknown'` (browser-unrecognised, e.g. RAR) vs
98
+ // `fileMediatype: 'archive'` with a non-ZIP `fileMimetype` (unzipit
99
+ // limitation, e.g. 7z/tar/gzip).
100
+ fireAnalytics(createPreviewUnsupportedEvent(_this.props.item), _this.props.createAnalyticsEvent);
101
+ _this.setState({
102
+ content: Outcome.failed(error)
103
+ });
104
+ return;
105
+ }
78
106
  _this.props.onError(error);
79
107
  _this.setState({
80
108
  content: Outcome.successful(_objectSpread(_objectSpread({}, _this.state.content.data), {}, {
@@ -1,6 +1,7 @@
1
1
  import { type FileState, type RequestMetadata } from '@atlaskit/media-client';
2
2
  import { type FileAttributes, type WithFileAttributes, type FailureAttributes, type WithTraceContext } from '@atlaskit/media-common/analytics';
3
3
  import { type CreateUIAnalyticsEvent } from '@atlaskit/analytics-next';
4
+ import { type ProcessingFailReason } from '@atlaskit/media-state';
4
5
  import { type PrimaryErrorReason } from '../errors';
5
6
  import { type MediaViewerEventPayload } from './events';
6
7
  declare const componentName = "mediaViewer";
@@ -13,5 +14,6 @@ export type MediaViewerFailureAttributes = Omit<FailureAttributes, 'failReason'>
13
14
  failReason: PrimaryErrorReason;
14
15
  statusCode?: number;
15
16
  request?: RequestMetadata;
17
+ processingFailReason?: ProcessingFailReason | 'not-available';
16
18
  } & WithFileAttributes & WithTraceContext;
17
19
  export declare function fireAnalytics(payload: MediaViewerEventPayload, createAnalyticsEvent?: CreateUIAnalyticsEvent): void;
@@ -14,7 +14,7 @@ export declare class ArchiveViewerError extends MediaViewerError {
14
14
  }
15
15
  export declare function isArchiveViewerError(err: Error): err is ArchiveViewerError;
16
16
  export type MediaViewerErrorReason = 'collection-fetch-metadata' | 'header-fetch-metadata' | 'itemviewer-onerror' | 'itemviewer-fetch-metadata' | 'itemviewer-file-error-status' | 'itemviewer-file-failed-processing-status' | 'imageviewer-external-onerror' | 'imageviewer-fetch-url' | 'imageviewer-src-onerror' | 'audioviewer-fetch-url' | 'audioviewer-missing-artefact' | 'audioviewer-playback' | 'videoviewer-fetch-url' | 'videoviewer-missing-artefact' | 'videoviewer-playback' | 'docviewer-fetch-url' | 'docviewer-content-fetch-failed' | 'docviewer-fetch-pdf' | 'codeviewer-fetch-src' | 'codeviewer-load-src' | 'codeviewer-file-size-exceeds' | 'codeviewer-parse-email' | 'svg-img-error' | 'svg-binary-fetch' | 'svg-unknown-error' | 'svg-blob-to-datauri' | 'unsupported' | 'custom-viewer-error' | 'download';
17
- export type ArchiveViewerErrorReason = 'archiveviewer-bundle-loader' | 'archiveviewer-read-binary' | 'archiveviewer-create-url' | 'archiveviewer-imageviewer-onerror' | 'archiveviewer-videoviewer-onerror' | 'archiveviewer-audioviewer-onerror' | 'archiveviewer-docviewer-onerror' | 'archiveviewer-codeviewer-onerror' | 'archiveviewer-codeviewer-file-size-exceeds' | 'archiveviewer-missing-name-src' | 'archiveviewer-unsupported' | 'archiveviewer-encrypted-entry' | 'archiveviewer-customrenderer-onerror';
17
+ export type ArchiveViewerErrorReason = 'archiveviewer-bundle-loader' | 'archiveviewer-read-binary' | 'archiveviewer-not-zip' | 'archiveviewer-create-url' | 'archiveviewer-imageviewer-onerror' | 'archiveviewer-videoviewer-onerror' | 'archiveviewer-audioviewer-onerror' | 'archiveviewer-docviewer-onerror' | 'archiveviewer-codeviewer-onerror' | 'archiveviewer-codeviewer-file-size-exceeds' | 'archiveviewer-missing-name-src' | 'archiveviewer-unsupported' | 'archiveviewer-encrypted-entry' | 'archiveviewer-customrenderer-onerror';
18
18
  export type PrimaryErrorReason = MediaViewerErrorReason | ArchiveViewerErrorReason;
19
19
  export type SecondaryErrorReason = MediaClientErrorReason | 'unknown' | 'nativeError' | undefined;
20
20
  export declare function getPrimaryErrorReason(error: MediaViewerError): PrimaryErrorReason;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atlaskit/media-viewer",
3
- "version": "54.1.1",
3
+ "version": "54.2.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/"
@@ -38,13 +38,14 @@
38
38
  "@atlaskit/heading": "^6.0.0",
39
39
  "@atlaskit/icon": "^36.0.0",
40
40
  "@atlaskit/icon-file-type": "^8.0.0",
41
- "@atlaskit/icon-lab": "^7.0.0",
42
- "@atlaskit/media-client": "^37.0.0",
41
+ "@atlaskit/icon-lab": "^7.1.0",
42
+ "@atlaskit/media-client": "^37.1.0",
43
43
  "@atlaskit/media-client-react": "^6.0.0",
44
- "@atlaskit/media-common": "^14.0.0",
44
+ "@atlaskit/media-common": "^14.1.0",
45
45
  "@atlaskit/media-document-viewer": "^1.0.0",
46
+ "@atlaskit/media-state": "^3.0.0",
46
47
  "@atlaskit/media-svg": "^3.0.0",
47
- "@atlaskit/media-ui": "^30.1.0",
48
+ "@atlaskit/media-ui": "^30.3.0",
48
49
  "@atlaskit/platform-feature-flags": "^2.0.0",
49
50
  "@atlaskit/portal": "^6.0.0",
50
51
  "@atlaskit/primitives": "^20.0.0",
@@ -52,10 +53,11 @@
52
53
  "@atlaskit/spinner": "^20.0.0",
53
54
  "@atlaskit/textfield": "^9.0.0",
54
55
  "@atlaskit/theme": "^26.0.0",
55
- "@atlaskit/tmp-editor-statsig": "^109.0.0",
56
- "@atlaskit/tokens": "^14.0.0",
56
+ "@atlaskit/tmp-editor-statsig": "^110.0.0",
57
+ "@atlaskit/tokens": "^15.0.0",
57
58
  "@atlaskit/tooltip": "^23.0.0",
58
59
  "@atlaskit/ufo": "^1.0.0",
60
+ "@atlassian/react-compiler-gating": "^0.2.0",
59
61
  "@babel/runtime": "^7.0.0",
60
62
  "@codemirror/language": "6.10.8",
61
63
  "@codemirror/language-data": "6.5.1",
@@ -86,14 +88,12 @@
86
88
  "@af/visual-regression": "workspace:^",
87
89
  "@atlaskit/media-core": "^38.0.0",
88
90
  "@atlaskit/media-integration-test-helpers": "workspace:^",
89
- "@atlaskit/media-state": "^3.0.0",
90
91
  "@atlaskit/media-test-data": "^4.0.0",
91
92
  "@atlaskit/media-test-helpers": "^42.0.0",
92
93
  "@atlaskit/ssr": "workspace:^",
93
94
  "@atlaskit/toggle": "^17.0.0",
94
95
  "@atlassian/a11y-jest-testing": "^0.12.0",
95
96
  "@atlassian/feature-flags-test-utils": "^1.1.0",
96
- "@atlassian/react-compiler-gating": "workspace:^",
97
97
  "@atlassian/testing-library": "^0.6.0",
98
98
  "@atlassian/ufo": "^0.8.0",
99
99
  "@testing-library/dom": "^10.1.0",
@@ -106,7 +106,7 @@
106
106
  "jsverify": "^0.8.3",
107
107
  "react": "^18.2.0",
108
108
  "react-dom": "^18.2.0",
109
- "react-intl": "^6.6.2",
109
+ "react-intl": "^7.0.0",
110
110
  "rxjs": "^5.5.0",
111
111
  "wait-for-expect": "^1.2.0"
112
112
  },
@@ -140,6 +140,9 @@
140
140
  },
141
141
  "platform_media_excel_lazy_load": {
142
142
  "type": "boolean"
143
+ },
144
+ "platform_media_archive_zip_guard": {
145
+ "type": "boolean"
143
146
  }
144
147
  },
145
148
  "techstack": {