Files
buzz/desktop/src/shared/ui/VideoReviewCommentMarkdown.tsx
klopez4212 17d2147eca Fix video comment effect wrapping (#5748)
## What changed

- render video-review timecode chips inside the first Markdown paragraph
so comment text wraps naturally around them
- reuse the canonical video-review chip treatment across the timeline,
Inbox previews, and Inbox detail
- preserve video-review context in Inbox so timestamp chips remain
interactive

## Why

Video comments now support Markdown-like effects, but non-player
surfaces rendered the timestamp beside a separate text layout. That kept
the chip and comment from sharing the same inline flow and made Inbox
behavior inconsistent with the player.

## Validation

- `pnpm --dir desktop check`
- 100 focused Markdown, timecode, video-review, and Inbox unit tests
- `pnpm --dir desktop build:e2e`
- focused `video-attachment.spec.ts` Playwright scenario
- pre-push desktop typecheck and 4,761-test desktop suite
- native Builderlab staging with the configured profile

Focused timeline and Inbox snapshots will be attached in a PR comment.

---------

Signed-off-by: kenny lopez <klopez4212@gmail.com>
Signed-off-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
Co-authored-by: Fast Fizz <2df81cb51f05a9d5387ef24d7b9ecb8fcdfcd1c70ffabc67061c9596e1b5b1c4@buzz.block.builderlab.xyz>
2026-08-14 17:16:28 +01:00

78 lines
2.2 KiB
TypeScript

import * as React from "react";
import { Markdown } from "@/shared/ui/markdown";
import type { MarkdownProps } from "@/shared/ui/markdown/types";
import { useOpenVideoReviewAt } from "@/shared/ui/VideoReviewNavigation";
import { parseVideoReviewTimecode } from "@/shared/ui/videoReviewTimecode";
import {
VideoReviewTimecodeButton,
VideoReviewTimecodeChip,
} from "@/shared/ui/VideoReviewTimecodeButton";
type VideoReviewCommentMarkdownProps = Omit<
MarkdownProps,
"leadingInlineContent"
> & {
videoReviewCommentRootId?: string;
};
/** Renders a video-review timecode inside the comment's first Markdown line. */
export function VideoReviewCommentMarkdown({
content,
interactive = true,
videoReviewCommentRootId,
...markdownProps
}: VideoReviewCommentMarkdownProps) {
const openVideoReviewAt = useOpenVideoReviewAt();
const reviewTimecode = React.useMemo(
() => (videoReviewCommentRootId ? parseVideoReviewTimecode(content) : null),
[content, videoReviewCommentRootId],
);
const handleTimecodeClick = React.useCallback(
(event: React.MouseEvent<HTMLButtonElement>) => {
event.stopPropagation();
if (reviewTimecode && videoReviewCommentRootId) {
openVideoReviewAt?.(videoReviewCommentRootId, reviewTimecode.seconds);
}
},
[openVideoReviewAt, reviewTimecode, videoReviewCommentRootId],
);
const leadingInlineContent = React.useMemo(() => {
if (!reviewTimecode) return undefined;
const timecode =
interactive && openVideoReviewAt ? (
<VideoReviewTimecodeButton
surface="message"
timecode={reviewTimecode.timecode}
onClick={handleTimecodeClick}
/>
) : (
<VideoReviewTimecodeChip
surface="message"
timecode={reviewTimecode.timecode}
/>
);
return <>{timecode} </>;
}, [handleTimecodeClick, interactive, openVideoReviewAt, reviewTimecode]);
if (!reviewTimecode) {
return (
<Markdown
{...markdownProps}
content={content}
interactive={interactive}
/>
);
}
return (
<Markdown
{...markdownProps}
content={reviewTimecode.text || "\u200B"}
interactive={interactive}
leadingInlineContent={leadingInlineContent}
/>
);
}