Promote to main: Feed browser luxury redesign #7
+1
-1
@@ -86,7 +86,7 @@ export const FIXTURE_FEED_ITEMS: unknown[] = [
|
|||||||
id: "p3",
|
id: "p3",
|
||||||
content: "A short walkthrough of how we run weekly reviews with a remote team.",
|
content: "A short walkthrough of how we run weekly reviews with a remote team.",
|
||||||
author: author("a3", "Marcus Bell"),
|
author: author("a3", "Marcus Bell"),
|
||||||
mediaItems: [{ mediaType: "video", thumbnailUrl: placeholderImage(210, "Weekly reviews"), mp4Url: FIXTURE_VIDEO_URL, durationSeconds: 154, order: 0 }],
|
mediaItems: [{ mediaType: "video", thumbnailUrl: placeholderImage(210, "Weekly reviews"), mp4Url: FIXTURE_VIDEO_URL, videoDuration: 154, order: 0 }],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ function toMedia(raw: unknown): CardMedia[] {
|
|||||||
const hls = muxHlsUrl(m.hlsUrl);
|
const hls = muxHlsUrl(m.hlsUrl);
|
||||||
const mp4 = feedMp4Url(m.mp4Url);
|
const mp4 = feedMp4Url(m.mp4Url);
|
||||||
// Keep a video even with no thumbnail or playable URL yet (just published).
|
// Keep a video even with no thumbnail or playable URL yet (just published).
|
||||||
const dur = typeof m.durationSeconds === "number" ? m.durationSeconds : typeof m.duration === "number" ? m.duration : null;
|
const dur = [m.videoDuration, m.durationSeconds, m.duration].find((v): v is number => typeof v === "number" && v > 0) ?? null;
|
||||||
out.push({ kind: "video", src: thumb, hlsUrl: hls, mp4Url: mp4, ...(dur ? { durationSeconds: dur } : {}) });
|
out.push({ kind: "video", src: thumb, hlsUrl: hls, mp4Url: mp4, ...(dur ? { durationSeconds: dur } : {}) });
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
+19
-1
@@ -3,7 +3,7 @@ import { createElement } from "react";
|
|||||||
import { renderToStaticMarkup } from "react-dom/server";
|
import { renderToStaticMarkup } from "react-dom/server";
|
||||||
import { FeedView, type FeedViewProps } from "@/components/FeedView";
|
import { FeedView, type FeedViewProps } from "@/components/FeedView";
|
||||||
import { fetchFeedPage, FeedLoadError } from "@/lib/feed-client";
|
import { fetchFeedPage, FeedLoadError } from "@/lib/feed-client";
|
||||||
import { mergePosts, relativeTime, toCardPosts, upgradeVideos } from "@/lib/feed-model";
|
import { formatDuration, mergePosts, relativeTime, toCardPost, toCardPosts, upgradeVideos } from "@/lib/feed-model";
|
||||||
|
|
||||||
const NOW = Date.parse("2026-10-19T12:00:00Z");
|
const NOW = Date.parse("2026-10-19T12:00:00Z");
|
||||||
const post = (id: string, publishedAt: string, extra: Record<string, unknown> = {}) => ({
|
const post = (id: string, publishedAt: string, extra: Record<string, unknown> = {}) => ({
|
||||||
@@ -185,3 +185,21 @@ describe("video transcoding", () => {
|
|||||||
expect(live).toContain("<video");
|
expect(live).toContain("<video");
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe("video duration", () => {
|
||||||
|
it("reads videoDuration (seconds) first and formats it as m:ss", () => {
|
||||||
|
const [m] = toCardPost({ type: "post", post: { id: "v", author: {}, mediaItems: [{ mediaType: "video", videoDuration: 154, duration: 9 }] } })!.media;
|
||||||
|
expect(m.durationSeconds).toBe(154);
|
||||||
|
expect(formatDuration(154)).toBe("2:34");
|
||||||
|
expect(formatDuration(5)).toBe("0:05");
|
||||||
|
expect(formatDuration(3725)).toBe("1:02:05");
|
||||||
|
expect(formatDuration(0)).toBeNull();
|
||||||
|
expect(formatDuration(undefined)).toBeNull();
|
||||||
|
});
|
||||||
|
it("falls back to durationSeconds, then duration, else none", () => {
|
||||||
|
const dur = (item: object) => toCardPost({ type: "post", post: { id: "v", author: {}, mediaItems: [{ mediaType: "video", ...item }] } })!.media[0].durationSeconds;
|
||||||
|
expect(dur({ durationSeconds: 30 })).toBe(30);
|
||||||
|
expect(dur({ duration: 12 })).toBe(12);
|
||||||
|
expect(dur({})).toBeUndefined();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user