diff --git a/src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx b/src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
index cd2af75786..8be0c04ade 100644
--- a/src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
+++ b/src/elements/content-sidebar/activity-feed-v2/FeedItemRow.tsx
@@ -118,7 +118,10 @@ const FeedItemRow = ({
const serialized = serializeEditorContent(content);
if (!serialized || !serialized.text.trim()) return;
if (id === item.id) {
- onCommentUpdate?.(id, serialized.text, undefined, serialized.hasMention, permissions);
+ const text = item.annotationTimestampMarkup
+ ? `${item.annotationTimestampMarkup} ${serialized.text}`
+ : serialized.text;
+ onCommentUpdate?.(id, text, undefined, serialized.hasMention, permissions);
return;
}
dispatchReplyEdit({
diff --git a/src/elements/content-sidebar/activity-feed-v2/__tests__/FeedItemRow.test.tsx b/src/elements/content-sidebar/activity-feed-v2/__tests__/FeedItemRow.test.tsx
index 5df04af2ef..20a98f948d 100644
--- a/src/elements/content-sidebar/activity-feed-v2/__tests__/FeedItemRow.test.tsx
+++ b/src/elements/content-sidebar/activity-feed-v2/__tests__/FeedItemRow.test.tsx
@@ -417,6 +417,44 @@ describe('elements/content-sidebar/activity-feed-v2/FeedItemRow', () => {
expect(mockedSeekVideoToMs).toHaveBeenCalledWith(8055);
});
+
+ test('should re-prepend timestamp markup when editing a video comment so the badge survives the update', () => {
+ mockedSerializeEditorContent.mockReturnValue({ hasMention: false, text: 'edited-text' });
+ const onCommentUpdate = jest.fn();
+ const timestampedComment: TransformedCommentItem = {
+ ...mockComment,
+ annotationTarget: { timestamp: '0:08', type: AnnotationBadgeType.Frame },
+ annotationTimestampMarkup: '#[timestamp:8055,versionId:2390295731268]',
+ annotationTimestampMs: 8055,
+ };
+ render();
+
+ lastThreadedAnnotationProps.onEdit?.('comment-1', { type: 'doc', content: [] });
+
+ expect(onCommentUpdate).toHaveBeenCalledWith(
+ 'comment-1',
+ '#[timestamp:8055,versionId:2390295731268] edited-text',
+ undefined,
+ false,
+ commentPermissions,
+ );
+ });
+
+ test('should not modify edit text for a regular comment without timestamp markup', () => {
+ mockedSerializeEditorContent.mockReturnValue({ hasMention: false, text: 'edited-text' });
+ const onCommentUpdate = jest.fn();
+ render();
+
+ lastThreadedAnnotationProps.onEdit?.('comment-1', { type: 'doc', content: [] });
+
+ expect(onCommentUpdate).toHaveBeenCalledWith(
+ 'comment-1',
+ 'edited-text',
+ undefined,
+ false,
+ commentPermissions,
+ );
+ });
});
describe('annotation rendering', () => {
diff --git a/src/elements/content-sidebar/activity-feed-v2/__tests__/transformers.test.ts b/src/elements/content-sidebar/activity-feed-v2/__tests__/transformers.test.ts
index c319f685c6..593764b840 100644
--- a/src/elements/content-sidebar/activity-feed-v2/__tests__/transformers.test.ts
+++ b/src/elements/content-sidebar/activity-feed-v2/__tests__/transformers.test.ts
@@ -689,6 +689,7 @@ describe('elements/content-sidebar/activity-feed-v2/transformers', () => {
const result = extractTimestampMarkup('#[timestamp:8055,versionId:2390295731268] wowo');
expect(result.cleanText).toBe('wowo');
expect(result.target).toEqual({ timestamp: '0:08', type: 'frame' });
+ expect(result.timestampMarkup).toBe('#[timestamp:8055,versionId:2390295731268]');
expect(result.timestampMs).toBe(8055);
});
@@ -696,6 +697,7 @@ describe('elements/content-sidebar/activity-feed-v2/transformers', () => {
const result = extractTimestampMarkup('#[timestamp:65000] hello');
expect(result.cleanText).toBe('hello');
expect(result.target).toEqual({ timestamp: '1:05', type: 'frame' });
+ expect(result.timestampMarkup).toBe('#[timestamp:65000]');
expect(result.timestampMs).toBe(65000);
});
@@ -759,6 +761,7 @@ describe('elements/content-sidebar/activity-feed-v2/transformers', () => {
expect(result!.type).toBe('comment');
if (result!.type === 'comment') {
expect(result.annotationTarget).toEqual({ timestamp: '0:08', type: 'frame' });
+ expect(result.annotationTimestampMarkup).toBe('#[timestamp:8055,versionId:2390295731268]');
expect(result.annotationTimestampMs).toBe(8055);
}
});
@@ -778,6 +781,7 @@ describe('elements/content-sidebar/activity-feed-v2/transformers', () => {
const result = transformFeedItem(comment as unknown as FeedItem);
if (result!.type === 'comment') {
expect(result.annotationTarget).toBeUndefined();
+ expect(result.annotationTimestampMarkup).toBeUndefined();
expect(result.annotationTimestampMs).toBeUndefined();
}
});
diff --git a/src/elements/content-sidebar/activity-feed-v2/transformers.ts b/src/elements/content-sidebar/activity-feed-v2/transformers.ts
index 93497bd133..7518ebbd93 100644
--- a/src/elements/content-sidebar/activity-feed-v2/transformers.ts
+++ b/src/elements/content-sidebar/activity-feed-v2/transformers.ts
@@ -46,7 +46,12 @@ const TIMESTAMP_MARKUP_REGEX = /^#\[timestamp:(\d+)(?:,versionId:\d+)?\]\s*/;
export const extractTimestampMarkup = (
text: string,
-): { cleanText: string; target?: AnnotationBadgeTargetType; timestampMs?: number } => {
+): {
+ cleanText: string;
+ target?: AnnotationBadgeTargetType;
+ timestampMarkup?: string;
+ timestampMs?: number;
+} => {
if (!text) return { cleanText: '' };
const match = text.match(TIMESTAMP_MARKUP_REGEX);
if (!match) return { cleanText: text };
@@ -60,7 +65,7 @@ export const extractTimestampMarkup = (
timestamp: convertMillisecondsToTimestamp(ms),
type: AnnotationBadgeType.Frame,
};
- return { cleanText, target, timestampMs: ms };
+ return { cleanText, target, timestampMarkup: fullMatch.trimEnd(), timestampMs: ms };
};
const parseLine = (line: string, authorId: string): (MentionNode | TextNode)[] => {
@@ -292,11 +297,17 @@ export const transformFeedItem = (item: FeedItem, currentUserId?: string): Trans
const comment = item as unknown as Comment;
const commentIsResolved = comment.status === 'resolved';
const rawText = comment.tagged_message || comment.message || '';
- const { cleanText, target: annotationTarget, timestampMs } = extractTimestampMarkup(rawText);
+ const {
+ cleanText,
+ target: annotationTarget,
+ timestampMarkup,
+ timestampMs,
+ } = extractTimestampMarkup(rawText);
const root = commentToTextMessage(comment, cleanText);
const replies = (comment.replies ?? []).map(reply => commentToTextMessage(reply));
return {
annotationTarget,
+ annotationTimestampMarkup: timestampMarkup,
annotationTimestampMs: timestampMs,
id: comment.id,
isResolved: commentIsResolved,
diff --git a/src/elements/content-sidebar/activity-feed-v2/types.ts b/src/elements/content-sidebar/activity-feed-v2/types.ts
index 9710a5ad2b..75aa4757af 100644
--- a/src/elements/content-sidebar/activity-feed-v2/types.ts
+++ b/src/elements/content-sidebar/activity-feed-v2/types.ts
@@ -47,6 +47,7 @@ type ResolvedInfo = {
export type TransformedCommentItem = {
annotationTarget?: AnnotationBadgeTargetType;
+ annotationTimestampMarkup?: string;
annotationTimestampMs?: number;
id: string;
messages: TextMessageType[];