diff --git a/src/api/Feed.js b/src/api/Feed.js index 16e4f09b0f..023ef701e0 100644 --- a/src/api/Feed.js +++ b/src/api/Feed.js @@ -39,6 +39,7 @@ import { FILE_ACTIVITY_TYPE_COMMENT, FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION, FILE_ACTIVITY_TYPE_ENHANCED_COMMENT, + FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, FILE_ACTIVITY_TYPE_TASK, FILE_ACTIVITY_TYPE_VERSION, HTTP_STATUS_CODE_CONFLICT, @@ -173,11 +174,16 @@ export const getParsedFileActivitiesResponse = ( return taskItem; } case FILE_ACTIVITY_TYPE_COMMENT: - case FILE_ACTIVITY_TYPE_ENHANCED_COMMENT: { - const rawCommentItem = - item.activity_type === FILE_ACTIVITY_TYPE_ENHANCED_COMMENT - ? source[FILE_ACTIVITY_TYPE_ENHANCED_COMMENT] - : source[FILE_ACTIVITY_TYPE_COMMENT]; + case FILE_ACTIVITY_TYPE_ENHANCED_COMMENT: + case FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN: { + let rawCommentItem; + if (item.activity_type === FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN) { + rawCommentItem = source[FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN]; + } else if (item.activity_type === FILE_ACTIVITY_TYPE_ENHANCED_COMMENT) { + rawCommentItem = source[FILE_ACTIVITY_TYPE_ENHANCED_COMMENT]; + } else { + rawCommentItem = source[FILE_ACTIVITY_TYPE_COMMENT]; + } if (!rawCommentItem) { return null; } @@ -190,7 +196,7 @@ export const getParsedFileActivitiesResponse = ( } commentItem.tagged_message = commentItem.tagged_message || commentItem.message || ''; - // enhanced_comment is a wire-only variant; downstream consumers see the legacy type + // enhanced_comment and enhanced_comment_timespan are wire-only; downstream consumers see the legacy type commentItem.type = FEED_ITEM_TYPE_COMMENT; return commentItem; @@ -582,6 +588,7 @@ class Feed extends Base { shouldShowTasks = true, shouldShowVersions = true, shouldUseEnhancedActivities = false, + shouldUseEnhancedTimespanComments = false, shouldUseUAA = false, shouldEnableRichText = false, }: { @@ -591,6 +598,7 @@ class Feed extends Base { shouldShowTasks?: boolean, shouldShowVersions?: boolean, shouldUseEnhancedActivities?: boolean, + shouldUseEnhancedTimespanComments?: boolean, shouldUseUAA?: boolean, shouldEnableRichText?: boolean, } = {}, @@ -641,9 +649,15 @@ class Feed extends Base { const appActivityActivityType = shouldShowAppActivity ? [FILE_ACTIVITY_TYPE_APP_ACTIVITY] : []; const taskActivityType = shouldShowTasks ? [FILE_ACTIVITY_TYPE_TASK] : []; const versionsActivityType = shouldShowVersions ? [FILE_ACTIVITY_TYPE_VERSION] : []; - const commentActivityType = permissions[PERMISSION_CAN_COMMENT] - ? [shouldUseEnhancedActivities ? FILE_ACTIVITY_TYPE_ENHANCED_COMMENT : FILE_ACTIVITY_TYPE_COMMENT] - : []; + // comment: older clients. enhanced_comment: point timestamps. + // enhanced_comment_timespan: audio files when the audio player updates split is on. + let commentFileActivityType = FILE_ACTIVITY_TYPE_COMMENT; + if (shouldUseEnhancedActivities) { + commentFileActivityType = shouldUseEnhancedTimespanComments + ? FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN + : FILE_ACTIVITY_TYPE_ENHANCED_COMMENT; + } + const commentActivityType = permissions[PERMISSION_CAN_COMMENT] ? [commentFileActivityType] : []; const filteredActivityTypes = [ ...annotationActivityType, ...appActivityActivityType, diff --git a/src/api/__tests__/Feed.test.js b/src/api/__tests__/Feed.test.js index 3eda36500d..6f2437f3d1 100644 --- a/src/api/__tests__/Feed.test.js +++ b/src/api/__tests__/Feed.test.js @@ -14,6 +14,7 @@ import { FILE_ACTIVITY_TYPE_COMMENT, FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION, FILE_ACTIVITY_TYPE_ENHANCED_COMMENT, + FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, FILE_ACTIVITY_TYPE_TASK, FILE_ACTIVITY_TYPE_VERSION, IS_ERROR_DISPLAYED, @@ -707,6 +708,35 @@ describe('api/Feed', () => { done(); }); }); + + test('should request enhanced_comment_timespan when the audio player updates split is on', done => { + feed.feedItems(file, false, successCb, errorCb, errorCb, { + shouldShowAnnotations: true, + shouldShowAppActivity: true, + shouldShowReplies: true, + shouldShowTasks: true, + shouldShowVersions: true, + shouldUseEnhancedActivities: true, + shouldUseEnhancedTimespanComments: true, + shouldUseUAA: true, + }); + setImmediate(() => { + expect(feed.fetchFileActivities).toBeCalledWith( + file.permissions, + [ + FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION, + FILE_ACTIVITY_TYPE_APP_ACTIVITY, + FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, + FILE_ACTIVITY_TYPE_TASK, + FILE_ACTIVITY_TYPE_VERSION, + ], + true, + true, + false, + ); + done(); + }); + }); }); describe('fetchAnnotations()', () => { @@ -2539,6 +2569,26 @@ describe('api/Feed', () => { expect(byId['enh-annotation-1'].type).toBe(FEED_ITEM_TYPE_ANNOTATION); }); + test('should remap enhanced_comment_timespan activity types to the legacy comment type', () => { + const enhancedCommentTimespan = { + ...threadedCommentsFormatted[0], + id: 'enh-comment-timespan', + message: '#[timestamp:8055,endTimestamp:12000,versionId:1] range', + type: FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, + }; + const parsed = getParsedFileActivitiesResponse({ + entries: [ + { + activity_type: FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, + source: { [FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN]: enhancedCommentTimespan }, + }, + ], + }); + expect(parsed).toHaveLength(1); + expect(parsed[0].id).toBe('enh-comment-timespan'); + expect(parsed[0].type).toBe(FEED_ITEM_TYPE_COMMENT); + }); + test('should drop entries whose activity_type does not match a populated source key', () => { const response = { entries: [ diff --git a/src/common/types/feed.js b/src/common/types/feed.js index 8c4ed3632c..d0fe14110a 100644 --- a/src/common/types/feed.js +++ b/src/common/types/feed.js @@ -17,6 +17,7 @@ import { FILE_ACTIVITY_TYPE_COMMENT, FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION, FILE_ACTIVITY_TYPE_ENHANCED_COMMENT, + FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN, FILE_ACTIVITY_TYPE_TASK, FILE_ACTIVITY_TYPE_VERSION, } from '../../constants'; @@ -183,6 +184,7 @@ type FileActivityTypes = | typeof FILE_ACTIVITY_TYPE_COMMENT | typeof FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION | typeof FILE_ACTIVITY_TYPE_ENHANCED_COMMENT + | typeof FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN | typeof FILE_ACTIVITY_TYPE_TASK | typeof FILE_ACTIVITY_TYPE_VERSION; @@ -202,6 +204,9 @@ type FileActivitySource = | { enhanced_comment: Comment, } + | { + enhanced_comment_timespan: Comment, + } | { task: TaskNew, } diff --git a/src/constants.js b/src/constants.js index c652df2506..b83938b45f 100644 --- a/src/constants.js +++ b/src/constants.js @@ -548,6 +548,7 @@ export const FILE_ACTIVITY_TYPE_APP_ACTIVITY: 'app_activity' = 'app_activity'; export const FILE_ACTIVITY_TYPE_COMMENT: 'comment' = 'comment'; export const FILE_ACTIVITY_TYPE_ENHANCED_ANNOTATION: 'enhanced_annotation' = 'enhanced_annotation'; export const FILE_ACTIVITY_TYPE_ENHANCED_COMMENT: 'enhanced_comment' = 'enhanced_comment'; +export const FILE_ACTIVITY_TYPE_ENHANCED_COMMENT_TIMESPAN: 'enhanced_comment_timespan' = 'enhanced_comment_timespan'; export const FILE_ACTIVITY_TYPE_TASK: 'task' = 'task'; export const FILE_ACTIVITY_TYPE_VERSION: 'versions' = 'versions'; diff --git a/src/elements/content-sidebar/ActivitySidebar.js b/src/elements/content-sidebar/ActivitySidebar.js index 0da94a7a9f..f7e69e7a66 100644 --- a/src/elements/content-sidebar/ActivitySidebar.js +++ b/src/elements/content-sidebar/ActivitySidebar.js @@ -26,6 +26,8 @@ import { mark } from '../../utils/performance'; import { withAnnotatorContext } from '../common/annotator-context'; import { withAPIContext } from '../common/api-context'; import { withErrorBoundary } from '../common/error-boundary'; +// $FlowFixMe TypeScript file +import { FILE_EXTENSIONS } from '../common/item/constants'; import { withFeatureConsumer, isFeatureEnabled, getFeatureConfig } from '../common/feature-checking'; import { withLogger } from '../common/logger'; import { withRouterAndRef } from '../common/routing'; @@ -814,6 +816,8 @@ class ActivitySidebar extends React.PureComponent { hasVersions: shouldShowVersions, } = this.props; const isThreadedRepliesV2Enabled = isFeatureEnabled(features, 'activityFeed.threadedRepliesV2.enabled'); + const isAudioPlayerV2Enabled = isFeatureEnabled(features, 'audioPlayerV2.enabled'); + const isAudioFile = Boolean(file.extension && FILE_EXTENSIONS.audio.includes(file.extension)); const shouldShowReplies = hasReplies || isThreadedRepliesV2Enabled; const shouldFetchReplies = shouldRefreshCache && hasReplies && activeFeedEntryId && activeFeedEntryType === FEED_ITEM_TYPE_COMMENT; @@ -835,6 +839,7 @@ class ActivitySidebar extends React.PureComponent { shouldShowTasks, shouldShowVersions, shouldUseEnhancedActivities: isThreadedRepliesV2Enabled, + shouldUseEnhancedTimespanComments: isAudioPlayerV2Enabled && isAudioFile, shouldUseUAA, shouldEnableRichText, }, diff --git a/src/elements/content-sidebar/__tests__/ActivitySidebar.test.js b/src/elements/content-sidebar/__tests__/ActivitySidebar.test.js index 3938207e3d..69260217e4 100644 --- a/src/elements/content-sidebar/__tests__/ActivitySidebar.test.js +++ b/src/elements/content-sidebar/__tests__/ActivitySidebar.test.js @@ -846,6 +846,7 @@ describe('elements/content-sidebar/ActivitySidebar', () => { shouldShowTasks: expectedTasks, shouldShowVersions: expectedVersions, shouldUseEnhancedActivities: false, + shouldUseEnhancedTimespanComments: false, shouldUseUAA: expectedUseUAA, shouldEnableRichText: false, }, @@ -881,6 +882,7 @@ describe('elements/content-sidebar/ActivitySidebar', () => { shouldShowTasks: true, shouldShowVersions: true, shouldUseEnhancedActivities: false, + shouldUseEnhancedTimespanComments: false, shouldUseUAA: false, shouldEnableRichText: false, }, @@ -938,6 +940,60 @@ describe('elements/content-sidebar/ActivitySidebar', () => { ); }); + test('should request timespan comments for an audio file when audioPlayerV2 is enabled', () => { + const audioFile = { ...file, extension: 'mp3' }; + wrapper = getWrapper({ + file: audioFile, + features: { + activityFeed: { + threadedRepliesV2: { enabled: true }, + }, + audioPlayerV2: { enabled: true }, + }, + }); + instance = wrapper.instance(); + instance.fetchFeedItems(); + + expect(feedAPI.feedItems).toHaveBeenCalledWith( + audioFile, + false, + instance.fetchFeedItemsSuccessCallback, + instance.fetchFeedItemsErrorCallback, + instance.errorCallback, + expect.objectContaining({ + shouldUseEnhancedActivities: true, + shouldUseEnhancedTimespanComments: true, + }), + ); + }); + + test('should request enhanced_comment for a non-audio file when audioPlayerV2 is enabled', () => { + const videoFile = { ...file, extension: 'mp4' }; + wrapper = getWrapper({ + file: videoFile, + features: { + activityFeed: { + threadedRepliesV2: { enabled: true }, + }, + audioPlayerV2: { enabled: true }, + }, + }); + instance = wrapper.instance(); + instance.fetchFeedItems(); + + expect(feedAPI.feedItems).toHaveBeenCalledWith( + videoFile, + false, + instance.fetchFeedItemsSuccessCallback, + instance.fetchFeedItemsErrorCallback, + instance.errorCallback, + expect.objectContaining({ + shouldUseEnhancedActivities: true, + shouldUseEnhancedTimespanComments: false, + }), + ); + }); + test('should set shouldShowReplies and shouldUseEnhancedActivities to true when both hasReplies and threadedRepliesV2 are enabled', () => { wrapper = getWrapper({ features: {