9uv0f8oll: Fit mobile videos to the screen without cropping

This commit is contained in:
2026-10-20 09:59:38 -06:00
parent c4434198ad
commit d653e8e75d
7 changed files with 114 additions and 10 deletions
+46 -5
View File
@@ -2,21 +2,22 @@
// real: no Minnect person, post, token or URL. Images are inline SVG data URIs
// so the preview never calls the network. Later tasks extend this file.
import type { Person } from "@/lib/auth-client";
import { toCardPosts, type CardPost } from "@/lib/feed-model";
import type { DiscoverCategory, DiscoverData, DiscoverExpert, DiscoverMinnect } from "@/lib/discover";
import type { ExpertResult } from "@/lib/experts-search";
/** Fixed "now" so relative times in the preview are stable. */
export const FIXTURE_NOW = Date.parse("2026-10-19T12:00:00Z");
/** A 16:9 gradient placeholder image, drawn with hsl() so no fetch happens. */
export function placeholderImage(hue: number, label = ""): string {
/** A gradient placeholder image (16:9 unless a size is given), drawn with hsl() so no fetch happens. */
export function placeholderImage(hue: number, label = "", width = 1280, height = 720): string {
const svg =
`<svg xmlns="http://www.w3.org/2000/svg" width="1280" height="720" viewBox="0 0 1280 720">` +
`<svg xmlns="http://www.w3.org/2000/svg" width="${width}" height="${height}" viewBox="0 0 ${width} ${height}">` +
`<defs><linearGradient id="g" x1="0" y1="0" x2="1" y2="1">` +
`<stop offset="0" stop-color="hsl(${hue} 45% 32%)"/><stop offset="1" stop-color="hsl(${(hue + 40) % 360} 40% 14%)"/>` +
`</linearGradient></defs><rect width="1280" height="720" fill="url(#g)"/>` +
`</linearGradient></defs><rect width="${width}" height="${height}" fill="url(#g)"/>` +
(label
? `<text x="64" y="660" font-family="system-ui,sans-serif" font-size="56" font-weight="700" fill="hsl(0 0% 100% / .85)">${label}</text>`
? `<text x="64" y="${height - 60}" font-family="system-ui,sans-serif" font-size="56" font-weight="700" fill="hsl(0 0% 100% / .85)">${label}</text>`
: "") +
`</svg>`;
return `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
@@ -25,6 +26,22 @@ export function placeholderImage(hue: number, label = ""): string {
/** Not a real file: preload is off, so the preview video shows only its poster, play button and length. */
const FIXTURE_VIDEO_URL = "https://cdn.feed.sharedservices.vt.com/dev-fixture.mp4";
/** Fixture videos with real (tiny) files in public/dev: the feed only accepts CDN-host mp4 URLs, so these are swapped to local paths after parsing. */
const FIXTURE_LANDSCAPE_URL = "https://cdn.feed.sharedservices.vt.com/dev-landscape.mp4";
const FIXTURE_PORTRAIT_URL = "https://cdn.feed.sharedservices.vt.com/dev-portrait.mp4";
const LOCAL_FIXTURE_VIDEOS: Record<string, string> = {
[FIXTURE_LANDSCAPE_URL]: "/dev/landscape.mp4",
[FIXTURE_PORTRAIT_URL]: "/dev/portrait.mp4",
};
/** toCardPosts for the preview, with the fixture video URLs pointed at the local files. */
export function fixtureCards(items: unknown[]): CardPost[] {
return toCardPosts(items).map((p) => ({
...p,
media: p.media.map((m) => (m.mp4Url && LOCAL_FIXTURE_VIDEOS[m.mp4Url] ? { ...m, mp4Url: LOCAL_FIXTURE_VIDEOS[m.mp4Url] } : m)),
}));
}
/** Comments for the preview, in the shape /api/posts/[id]/comments returns. p2 holds a removed one; p4 has none. */
export const FIXTURE_COMMENTS: Record<string, unknown[]> = {
p1: [{ id: "c1", content: "Congratulations, the flow reads well.", createdAt: "2026-10-19T11:50:00Z", author: { displayName: "Priya Natarajan" } }],
@@ -89,6 +106,30 @@ export const FIXTURE_FEED_ITEMS: unknown[] = [
mediaItems: [{ mediaType: "video", thumbnailUrl: placeholderImage(210, "Weekly reviews"), mp4Url: FIXTURE_VIDEO_URL, videoDuration: 154, order: 0 }],
},
},
{
type: "post",
publishedAt: "2026-10-19T07:50:00Z",
likesCount: 31,
commentsCount: 4,
post: {
id: "p8",
content: "Landscape recording of the quarterly walkthrough. It should show whole, centered, over a blurred copy of its own thumbnail.",
author: author("a8", "Tomas Reyes"),
mediaItems: [{ mediaType: "video", thumbnailUrl: placeholderImage(180, "Landscape 16:9"), mp4Url: FIXTURE_LANDSCAPE_URL, videoDuration: 2, order: 0 }],
},
},
{
type: "post",
publishedAt: "2026-10-19T07:20:00Z",
likesCount: 44,
commentsCount: 5,
post: {
id: "p9",
content: "Portrait clip shot on a phone. It should fill the whole slide.",
author: author("a9", "Ines Moreau"),
mediaItems: [{ mediaType: "video", thumbnailUrl: placeholderImage(300, "Portrait 9:16", 720, 1280), mp4Url: FIXTURE_PORTRAIT_URL, videoDuration: 2, order: 0 }],
},
},
{
type: "post",
publishedAt: "2026-10-18T21:20:00Z",