Feed browser: Redesign finalization #5
@@ -17,11 +17,14 @@ interface Props {
|
|||||||
* The trending card's 16:9 frame. Video answers play inline as a <video> with a
|
* The trending card's 16:9 frame. Video answers play inline as a <video> with a
|
||||||
* poster; audio keeps its <audio> control under the frame; no url shows the
|
* poster; audio keeps its <audio> control under the frame; no url shows the
|
||||||
* thumbnail only. The glass chip, play button, views and kind label sit over
|
* thumbnail only. The glass chip, play button, views and kind label sit over
|
||||||
* the thumbnail and step aside while a video plays.
|
* the thumbnail and step aside once a video has started, and only then does the
|
||||||
|
* video show its native controls.
|
||||||
*/
|
*/
|
||||||
export function MinnectMedia({ media, thumbnailUrl, question, who, pictureUrl, views = null }: Props) {
|
export function MinnectMedia({ media, thumbnailUrl, question, who, pictureUrl, views = null }: Props) {
|
||||||
const [videoFailed, setVideoFailed] = useState(false);
|
const [videoFailed, setVideoFailed] = useState(false);
|
||||||
const [playing, setPlaying] = useState(false);
|
// Native controls only after the first play, so the frame never shows our
|
||||||
|
// play button and the browser's controls at the same time.
|
||||||
|
const [started, setStarted] = useState(false);
|
||||||
const video = useRef<HTMLVideoElement | null>(null);
|
const video = useRef<HTMLVideoElement | null>(null);
|
||||||
const audio = useRef<HTMLAudioElement | null>(null);
|
const audio = useRef<HTMLAudioElement | null>(null);
|
||||||
|
|
||||||
@@ -36,20 +39,19 @@ export function MinnectMedia({ media, thumbnailUrl, question, who, pictureUrl, v
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={`trend-media${playing ? " trend-media-playing" : ""}`}>
|
<div className={`trend-media${started ? " trend-media-playing" : ""}`}>
|
||||||
{isVideo ? (
|
{isVideo ? (
|
||||||
<video
|
<video
|
||||||
ref={video}
|
ref={video}
|
||||||
className="minnect-thumb minnect-video"
|
className="minnect-thumb minnect-video"
|
||||||
controls
|
controls={started}
|
||||||
playsInline
|
playsInline
|
||||||
preload="none"
|
preload="none"
|
||||||
src={media.url}
|
src={media.url}
|
||||||
poster={thumbnailUrl ?? undefined}
|
poster={thumbnailUrl ?? undefined}
|
||||||
aria-label={`Play answer from ${who}`}
|
aria-label={`Play answer from ${who}`}
|
||||||
onPlay={() => setPlaying(true)}
|
onClick={started ? undefined : play}
|
||||||
onPause={() => setPlaying(false)}
|
onPlay={() => setStarted(true)}
|
||||||
onEnded={() => setPlaying(false)}
|
|
||||||
onError={() => setVideoFailed(true)}
|
onError={() => setVideoFailed(true)}
|
||||||
/>
|
/>
|
||||||
) : thumbnailUrl ? (
|
) : thumbnailUrl ? (
|
||||||
|
|||||||
@@ -57,7 +57,9 @@ describe("TrendingMinnectsRail", () => {
|
|||||||
minnects: [minnect({ media: { kind: "video", url, duration: 9, waveform: null } })],
|
minnects: [minnect({ media: { kind: "video", url, duration: 9, waveform: null } })],
|
||||||
}));
|
}));
|
||||||
expect(out).toContain("<video");
|
expect(out).toContain("<video");
|
||||||
expect(out).toContain("controls");
|
// Before the first play only our play button shows, not the native controls too.
|
||||||
|
expect(out).not.toMatch(/<video[^>]*controls/);
|
||||||
|
expect(out).toContain('class="trend-play"');
|
||||||
expect(out).toContain("preload=\"none\"");
|
expect(out).toContain("preload=\"none\"");
|
||||||
expect(out).toContain("X-Amz-Signature=abc");
|
expect(out).toContain("X-Amz-Signature=abc");
|
||||||
expect(out).toContain('poster="https://x/t.jpg"');
|
expect(out).toContain('poster="https://x/t.jpg"');
|
||||||
|
|||||||
Reference in New Issue
Block a user