diff --git a/package.json b/package.json index 533f58a06e..350c49bd7b 100644 --- a/package.json +++ b/package.json @@ -202,7 +202,7 @@ "babel-plugin-react-remove-properties": "^0.3.0", "babel-plugin-rewire": "^1.0.0", "babel-plugin-styled-components": "^1.10.7", - "box-content-preview": "3.79.0", + "box-content-preview": "3.90.0", "chromatic": "^18.0.1", "circular-dependency-plugin": "^5.2.2", "classnames": "^2.5.1", @@ -334,7 +334,7 @@ "@hapi/address": "^2.1.4", "@tanstack/react-virtual": "^3.13.12", "axios": "^0.33.0", - "box-content-preview": "^3.79.0", + "box-content-preview": "^3.90.0", "classnames": "^2.5.1", "color": "^3.2.1", "draft-js": "^0.11.7", diff --git a/src/api/File.js b/src/api/File.js index 5967848f3c..687ed47154 100644 --- a/src/api/File.js +++ b/src/api/File.js @@ -231,7 +231,7 @@ class File extends Item { const xhrOptions: Object = { id: getTypedFileId(id), url: this.getUrl(id), - headers: { 'X-Rep-Hints': X_REP_HINTS }, + headers: { 'X-Rep-Hints': `${X_REP_HINTS}${options.repHints || ''}` }, }; this.errorCode = ERROR_CODE_FETCH_FILE; this.successCallback = successCallback; diff --git a/src/api/__tests__/File.test.js b/src/api/__tests__/File.test.js index a8bb129c7f..7fc44a973e 100644 --- a/src/api/__tests__/File.test.js +++ b/src/api/__tests__/File.test.js @@ -472,10 +472,7 @@ describe('api/File', () => { test('should make xhr to get file and not call success callback when destroyed', async () => { fields.findMissingProperties = jest.fn().mockReturnValueOnce([]); - file.isDestroyed = jest - .fn() - .mockReturnValueOnce(false) - .mockReturnValueOnce(true); + file.isDestroyed = jest.fn().mockReturnValueOnce(false).mockReturnValueOnce(true); file.xhr = { get: jest.fn().mockReturnValueOnce(Promise.resolve({ data: { file: 'new file' } })), }; @@ -581,6 +578,29 @@ describe('api/File', () => { }, }); }); + + test('should append extra repHints to X-Rep-Hints when provided', async () => { + file.options = { cache }; + file.getCache = jest.fn().mockReturnValueOnce(cache); + file.getCacheKey = jest.fn().mockReturnValueOnce('key'); + fields.findMissingProperties = jest.fn().mockReturnValueOnce(['missing']); + fields.fillMissingProperties = jest.fn().mockReturnValueOnce({ id: 'id' }); + file.xhr = { + get: jest.fn().mockReturnValueOnce(Promise.resolve({ data: { id: 'id' } })), + }; + + await file.getFile('id', jest.fn(), jest.fn(), { repHints: '[waveform]' }); + expect(file.xhr.get).toHaveBeenCalledWith({ + id: 'file_id', + url: 'https://api.box.com/2.0/files/id', + params: { + fields: 'missing', + }, + headers: { + 'X-Rep-Hints': `${X_REP_HINTS}[waveform]`, + }, + }); + }); }); describe('getFileExtension()', () => { diff --git a/src/common/types/api.js b/src/common/types/api.js index cf747291ba..4aa79992ce 100644 --- a/src/common/types/api.js +++ b/src/common/types/api.js @@ -30,6 +30,8 @@ type RequestOptions = { forceFetch?: boolean, noPagination?: boolean, refreshCache?: boolean, + // Extra X-Rep-Hints brackets appended to the default header (e.g. '[waveform]') + repHints?: string, }; type PayloadType = StringAnyMap | Array; diff --git a/src/constants.js b/src/constants.js index 8aa6696a74..ce2e2579f9 100644 --- a/src/constants.js +++ b/src/constants.js @@ -393,6 +393,7 @@ const X_REP_HINT_IMAGE = '[webp?dimensions=1024x1024][jpg?dimensions=2048x2048,p const X_REP_HINT_VIDEO_DASH = '[dash,mp4][filmstrip]'; const X_REP_HINT_VIDEO_MP4 = '[mp4]'; const videoHint = Browser.canPlayDash() ? X_REP_HINT_VIDEO_DASH : X_REP_HINT_VIDEO_MP4; +export const X_REP_HINT_WAVEFORM = '[waveform]'; export const X_REP_HINTS = `${X_REP_HINT_BASE}${X_REP_HINT_DOC_THUMBNAIL}${X_REP_HINT_IMAGE}${videoHint}`; /* ------------------ Uploader ---------------------- */ diff --git a/src/elements/content-preview/ContentPreview.js b/src/elements/content-preview/ContentPreview.js index 7bf6b8f0e3..b2b371fa87 100644 --- a/src/elements/content-preview/ContentPreview.js +++ b/src/elements/content-preview/ContentPreview.js @@ -64,6 +64,7 @@ import { ORIGIN_PREVIEW, ORIGIN_CONTENT_PREVIEW, ERROR_CODE_UNKNOWN, + X_REP_HINT_WAVEFORM, } from '../../constants'; import type { Annotation } from '../../common/types/feed'; import type { Target } from '../../common/types/annotations'; @@ -1268,6 +1269,9 @@ class ContentPreview extends React.PureComponent { this.fetchFileStartTime = performance.now(); this.fetchFileEndTime = null; + const { features }: Props = this.props; + const repHints = isFeatureEnabled(features, 'audioPlayerV2.enabled') ? X_REP_HINT_WAVEFORM : ''; + this.api .getFileAPI() .getFile( @@ -1277,6 +1281,7 @@ class ContentPreview extends React.PureComponent { { ...fetchOptions, fields: PREVIEW_FIELDS_TO_FETCH, + ...(repHints ? { repHints } : {}), }, ); } diff --git a/src/elements/content-preview/__tests__/ContentPreview.test.js b/src/elements/content-preview/__tests__/ContentPreview.test.js index f919cf17ba..07903b72f8 100644 --- a/src/elements/content-preview/__tests__/ContentPreview.test.js +++ b/src/elements/content-preview/__tests__/ContentPreview.test.js @@ -804,6 +804,47 @@ describe('elements/content-preview/ContentPreview', () => { ); }); + test('should append waveform rep hint when audio player v2 is enabled', () => { + const wrapper = getWrapper({ + ...props, + features: { audioPlayerV2: { enabled: true } }, + }); + const v2Instance = wrapper.instance(); + v2Instance.api = instance.api; + v2Instance.fetchFileSuccessCallback = jest.fn(); + v2Instance.fetchFileErrorCallback = jest.fn(); + v2Instance.fetchFile(file.id); + expect(getFileStub).toBeCalledWith( + file.id, + v2Instance.fetchFileSuccessCallback, + v2Instance.fetchFileErrorCallback, + { + fields: PREVIEW_FIELDS_TO_FETCH, + repHints: '[waveform]', + }, + ); + }); + + test('should not append waveform rep hint when audio player v2 is disabled', () => { + const wrapper = getWrapper({ + ...props, + features: { audioPlayerV2: { enabled: false } }, + }); + const v2Instance = wrapper.instance(); + v2Instance.api = instance.api; + v2Instance.fetchFileSuccessCallback = jest.fn(); + v2Instance.fetchFileErrorCallback = jest.fn(); + v2Instance.fetchFile(file.id); + expect(getFileStub).toBeCalledWith( + file.id, + v2Instance.fetchFileSuccessCallback, + v2Instance.fetchFileErrorCallback, + { + fields: PREVIEW_FIELDS_TO_FETCH, + }, + ); + }); + test('should fetch the file without sidebar fields', () => { instance.fetchFileSuccessCallback = jest.fn(); instance.fetchFileErrorCallback = jest.fn(); diff --git a/yarn.lock b/yarn.lock index 4a611831fd..83b7e9c1a9 100644 --- a/yarn.lock +++ b/yarn.lock @@ -7380,12 +7380,13 @@ bottleneck@^2.15.3: resolved "https://registry.yarnpkg.com/bottleneck/-/bottleneck-2.19.5.tgz#5df0b90f59fd47656ebe63c78a98419205cadd91" integrity sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw== -box-content-preview@3.79.0: - version "3.79.0" - resolved "https://registry.yarnpkg.com/box-content-preview/-/box-content-preview-3.79.0.tgz#4888779a5c79bcb40e242544f274b734170f1b24" - integrity sha512-xOuVmxVnCOySzWCE7eTpV5Mud/qdJCy0Q0cPGwdfjU2nY+zd5ZjlmYzjU66mT8usiRgBoANNM4rOgc6LaQWMDw== +box-content-preview@3.90.0: + version "3.90.0" + resolved "https://registry.yarnpkg.com/box-content-preview/-/box-content-preview-3.90.0.tgz#11293ed2e3898b6f884d36bd34287eb78f033f69" + integrity sha512-/TLCDYhelKr7+NLkVPJQQA2MyyxAy+T8OBWXPktl9JtHi6uouBAt2pOfExMRt9jkfTUo/xs0HpsfDiF2kR+XLA== dependencies: pdfjs-dist "4.3.136" + wavesurfer.js "7.12.11" brace-expansion@^1.1.7: version "1.1.16" @@ -19794,6 +19795,11 @@ watchpack@^2.5.1: glob-to-regexp "^0.4.1" graceful-fs "^4.1.2" +wavesurfer.js@7.12.11: + version "7.12.11" + resolved "https://registry.yarnpkg.com/wavesurfer.js/-/wavesurfer.js-7.12.11.tgz#e82811da84d3796c3dbe41f440fdc6177304febe" + integrity sha512-Sx0yZIz7jRJ9J9p7UL+pl9y0UQBB6UN/XNo+R7Gy4tCR5xZI9jMyA0dnt1R8TMVSQ4LZ10SeB2HJYQ+dV2dPSA== + wbuf@^1.1.0, wbuf@^1.7.3: version "1.7.3" resolved "https://registry.yarnpkg.com/wbuf/-/wbuf-1.7.3.tgz#c1d8d149316d3ea852848895cb6a0bfe887b87df"