From fd1a3f4b3e5773ed9010d5a23bde035f25a069ee Mon Sep 17 00:00:00 2001 From: TerryM Date: Sat, 6 Jun 2026 00:23:46 +0800 Subject: [PATCH] fix: favorites click opens isolated single-post view When `linkToResource` is set, PopularRankRow now navigates to /resource/?single=1 and PostRedirect propagates the `single=1` flag to the /browse?post=... target. MessageStream already supports singlePostMode (no filter bar, no sentinel, only the target post in the list), so favorites cards open the post in isolation instead of dropping the user into the surrounding stream. --- src/components/PopularRankList.tsx | 2 +- src/pages/PostRedirect/index.tsx | 23 ++++++++++++++++------- 2 files changed, 17 insertions(+), 8 deletions(-) diff --git a/src/components/PopularRankList.tsx b/src/components/PopularRankList.tsx index 73a89f0..f47f55c 100644 --- a/src/components/PopularRankList.tsx +++ b/src/components/PopularRankList.tsx @@ -150,7 +150,7 @@ export function PopularRankRow({ type="button" onClick={() => { if (linkToResource) { - navigate(lp(`/resource/${encodeURIComponent(post.id)}`)); + navigate(lp(`/resource/${encodeURIComponent(post.id)}?single=1`)); return; } const params = new URLSearchParams(); diff --git a/src/pages/PostRedirect/index.tsx b/src/pages/PostRedirect/index.tsx index 1f45565..0fe6144 100644 --- a/src/pages/PostRedirect/index.tsx +++ b/src/pages/PostRedirect/index.tsx @@ -1,5 +1,5 @@ import { useEffect, useRef } from "react"; -import { useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams, useSearchParams } from "react-router-dom"; import { getJSON } from "../../api"; import { langQuery, useI18n, type Lang } from "../../i18n"; import { useLocalizedPath } from "../../useLocalizedPath"; @@ -25,6 +25,8 @@ function postLangToUiLang(code: string | undefined): Lang | null { export function PostRedirect() { const { id } = useParams(); + const [sp] = useSearchParams(); + const singleMode = sp.get("single") === "1"; const { lang, t } = useI18n(); const navigate = useNavigate(); const lp = useLocalizedPath(); @@ -41,18 +43,25 @@ export function PostRedirect() { if (POST_STREAM_USES_MOCK) { const post = MOCK_POSTS.find((p) => p.id === id); + const suffix = singleMode ? "&single=1" : ""; navigate( - lp(post ? `/browse?post=${encodeURIComponent(post.id)}` : "/browse"), - { - replace: true, - }, + lp( + post + ? `/browse?post=${encodeURIComponent(post.id)}${suffix}` + : "/browse", + ), + { replace: true }, ); return; } const goToPostInLang = (post: Post, targetLang: Lang) => { + const suffix = singleMode ? "&single=1" : ""; navigate( - localizePath(`/browse?post=${encodeURIComponent(post.id)}`, targetLang), + localizePath( + `/browse?post=${encodeURIComponent(post.id)}${suffix}`, + targetLang, + ), { replace: true }, ); }; @@ -73,7 +82,7 @@ export function PostRedirect() { }) .catch(() => navigate(lp("/browse"), { replace: true })); }); - }, [id, lang, navigate, lp, showToast, t]); + }, [id, lang, navigate, lp, showToast, singleMode, t]); return (