From 2955ba10397482f59d62b4b428404aa256bc384e Mon Sep 17 00:00:00 2001 From: TerryM Date: Tue, 2 Jun 2026 23:51:14 +0800 Subject: [PATCH 1/2] fix: balance latest updates desktop columns --- src/pages/Home/index.tsx | 108 ++++++++++++++++++++++++++++++++++----- 1 file changed, 96 insertions(+), 12 deletions(-) diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index ce437e7..7a91842 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -1,6 +1,6 @@ import { ChevronLeft, ChevronRight } from "lucide-react"; import { Link, useLocation } from "react-router-dom"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useMemo, useRef, useState } from "react"; import { getJSON, itemsOrEmpty, readJSONCache, type Category } from "../../api"; import { CategoryIcon } from "../../components/CategoryIcon"; import { FigmaBanner } from "../../components/FigmaBanner"; @@ -41,6 +41,60 @@ function figmaCategoryRank(category: Category): number { return index === -1 ? FIGMA_CATEGORY_ORDER.length : index; } +type LatestPostColumnItem = { + post: Post; + originalIndex: number; +}; + +function estimateLatestPostHeight(post: Post): number { + const textLength = (post.text ?? post.title ?? "").length; + const textRows = Math.ceil(textLength / 72); + const textHeight = Math.min(180, Math.max(0, textRows * 22)); + const previewHeight = post.linkPreview ? 132 : 0; + const [firstAttachment] = post.attachments; + + if (!firstAttachment) return 72 + textHeight + previewHeight; + + if (post.attachments.length >= 2) { + const mediaHeight = firstAttachment.kind === "video" ? 340 : 300; + return mediaHeight + textHeight + previewHeight + 42; + } + + if (firstAttachment.kind === "video") { + return 300 + textHeight + previewHeight + 42; + } + + if (firstAttachment.kind === "image") { + return (post.text ? 300 : 260) + textHeight + previewHeight + 42; + } + + return 96 + post.attachments.length * 72 + textHeight + previewHeight; +} + +function splitLatestPostsIntoColumns( + posts: Post[], + columnCount: number, +): LatestPostColumnItem[][] { + const safeColumnCount = Math.max(1, columnCount); + const columns = Array.from( + { length: safeColumnCount }, + () => [] as LatestPostColumnItem[], + ); + const columnHeights = Array.from({ length: safeColumnCount }, () => 0); + + posts.forEach((post, originalIndex) => { + const targetColumn = + originalIndex < safeColumnCount + ? originalIndex + : columnHeights.indexOf(Math.min(...columnHeights)); + + columns[targetColumn].push({ post, originalIndex }); + columnHeights[targetColumn] += estimateLatestPostHeight(post) + 16; + }); + + return columns; +} + export function Home() { const { t, lang } = useI18n(); const lp = useLocalizedPath(); @@ -64,6 +118,27 @@ export function Home() { const [activeCategoryPage, setActiveCategoryPage] = useState(0); const [canScrollRec, setCanScrollRec] = useState(false); const [recScroll, setRecScroll] = useState({ ratio: 1, progress: 0 }); + const [latestDesktopColumnCount, setLatestDesktopColumnCount] = useState( + () => + typeof window !== "undefined" && + typeof window.matchMedia === "function" && + window.matchMedia("(min-width: 1024px)").matches + ? 3 + : 2, + ); + + useEffect(() => { + if (typeof window.matchMedia !== "function") return; + + const media = window.matchMedia("(min-width: 1024px)"); + const updateColumnCount = () => { + setLatestDesktopColumnCount(media.matches ? 3 : 2); + }; + + updateColumnCount(); + media.addEventListener("change", updateColumnCount); + return () => media.removeEventListener("change", updateColumnCount); + }, []); useEffect(() => { let cancelled = false; @@ -264,6 +339,10 @@ export function Home() { Math.round(recScroll.progress * (recommendedDotCount - 1)), ) : 0; + const latestDesktopColumns = useMemo( + () => splitLatestPostsIntoColumns(latestPosts, latestDesktopColumnCount), + [latestPosts, latestDesktopColumnCount], + ); // Hide the arrow that points to an edge we're already at. const recAtStart = recScroll.progress <= 0.01; const recAtEnd = recScroll.progress >= 0.99; @@ -481,18 +560,23 @@ export function Home() { ))} - {/* Desktop: masonry that matches the Figma layout. 2 columns at - md (with horizontal padding so cards don't kiss the screen - edge), 3 columns at lg+. */} -
- {latestPosts.map((post, index) => ( - + {latestDesktopColumns.map((column, columnIndex) => ( +
- - + {column.map(({ post, originalIndex }) => ( + + + + ))} +
))}
From 0326cb2998b9f2e3939c4442cfdcffed8a293166 Mon Sep 17 00:00:00 2001 From: TerryM Date: Tue, 2 Jun 2026 23:52:49 +0800 Subject: [PATCH 2/2] fix: limit latest updates preview to nine --- src/pages/Home/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/Home/index.tsx b/src/pages/Home/index.tsx index 7a91842..3cfe9ac 100644 --- a/src/pages/Home/index.tsx +++ b/src/pages/Home/index.tsx @@ -148,7 +148,7 @@ export function Home() { const postQ = `?lang=${langParam}&language=${languageParam}`; const categoriesUrl = `/api/categories${catQ}`; const recommendedUrl = `/api/posts/recommended${postQ}&limit=12`; - const latestUrl = `/api/posts${postQ}&sort=latest&limit=12`; + const latestUrl = `/api/posts${postQ}&sort=latest&limit=9`; const popularUrl = `/api/posts${postQ}&sort=popular&limit=5`; const applyHomeData = (