terry-staging #5
@@ -18,10 +18,12 @@ export function MessageStream({ scope }: MessageStreamProps) {
|
||||
|
||||
const type = sp.get("type") || "all";
|
||||
const q = (sp.get("q") || "").trim();
|
||||
const sort = sp.get("sort") || "";
|
||||
const tag = sp.get("tag") || "";
|
||||
|
||||
const params = useMemo(
|
||||
() => ({ scope, type, q, lang }),
|
||||
[scope, type, q, lang],
|
||||
() => ({ scope, type, q, sort, tag, lang }),
|
||||
[scope, type, q, sort, tag, lang],
|
||||
);
|
||||
|
||||
const { items, isLoading, error, hasMore, loadMore, reset } =
|
||||
|
||||
@@ -77,15 +77,23 @@ function VideoAttachmentCard({
|
||||
}}
|
||||
>
|
||||
{playing && !compact ? (
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={attachment.url}
|
||||
poster={attachment.posterUrl}
|
||||
controls
|
||||
playsInline
|
||||
autoPlay
|
||||
className="absolute inset-0 h-full w-full"
|
||||
/>
|
||||
<>
|
||||
<video
|
||||
ref={videoRef}
|
||||
src={attachment.url}
|
||||
poster={attachment.posterUrl}
|
||||
controls
|
||||
playsInline
|
||||
autoPlay
|
||||
className="absolute inset-0 h-full w-full"
|
||||
/>
|
||||
<AttachmentDownloadPill
|
||||
postId={postId}
|
||||
attachment={attachment}
|
||||
leadingLabel={duration}
|
||||
className="absolute left-2 top-2 z-20"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{posterUrl ? (
|
||||
|
||||
@@ -16,6 +16,7 @@ export type PostStreamParams = {
|
||||
language?: string;
|
||||
q?: string;
|
||||
sort?: string;
|
||||
tag?: string;
|
||||
lang: Lang;
|
||||
};
|
||||
|
||||
@@ -60,6 +61,9 @@ function filterMock(params: PostStreamParams): Post[] {
|
||||
return false;
|
||||
const q = params.q?.trim().toLowerCase();
|
||||
if (params.language && p.language !== params.language) return false;
|
||||
const tag = params.tag?.trim().toLowerCase();
|
||||
if (tag && !(p.tags ?? []).some((t) => t.toLowerCase() === tag))
|
||||
return false;
|
||||
if (q) {
|
||||
const haystack = [
|
||||
p.text ?? "",
|
||||
@@ -89,6 +93,7 @@ function buildRealUrl(params: PostStreamParams, cursor?: string): string {
|
||||
if (params.scope.kind === "category") sp.set("category", params.scope.slug);
|
||||
if (params.type && params.type !== "all") sp.set("type", params.type);
|
||||
if (params.sort) sp.set("sort", params.sort);
|
||||
if (params.tag) sp.set("tag", params.tag);
|
||||
if (params.language) sp.set("language", sourceLanguageQuery(params.language));
|
||||
if (cursor) sp.set("cursor", cursor);
|
||||
return `${q ? "/api/posts/search" : "/api/posts"}?${sp.toString()}`;
|
||||
@@ -166,6 +171,7 @@ export function usePostStream(params: PostStreamParams): PostStreamResult {
|
||||
params.language,
|
||||
params.q,
|
||||
params.sort,
|
||||
params.tag,
|
||||
params.lang,
|
||||
]);
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import { Link, Outlet, useLocation, useNavigate } from "react-router-dom";
|
||||
import { ArkLogoMark } from "../components/ArkLogoMark";
|
||||
import { useI18n, type Lang } from "../i18n";
|
||||
import { LANG_OPTIONS } from "../i18nLanguages";
|
||||
import { isPopularTag, popularTagParam } from "../utils/popularTag";
|
||||
|
||||
type PublicNavWhich =
|
||||
| "home"
|
||||
@@ -20,6 +21,7 @@ function navIsActive(
|
||||
search: string,
|
||||
hash: string,
|
||||
which: PublicNavWhich,
|
||||
currentLang: Lang,
|
||||
): boolean {
|
||||
const sp = new URLSearchParams(search);
|
||||
switch (which) {
|
||||
@@ -37,7 +39,9 @@ function navIsActive(
|
||||
case "browseRecommended":
|
||||
return pathname === "/official-recommendations";
|
||||
case "browsePopular":
|
||||
return pathname === "/browse" && sp.get("sort") === "popular";
|
||||
return (
|
||||
pathname === "/browse" && isPopularTag(sp.get("tag") || "", currentLang)
|
||||
);
|
||||
case "favorites":
|
||||
return (
|
||||
pathname === "/favorites" || (pathname === "/" && hash === "#favorites")
|
||||
@@ -273,9 +277,10 @@ export function PublicLayout() {
|
||||
const nav = useNavigate();
|
||||
|
||||
const na = (which: PublicNavWhich) =>
|
||||
navIsActive(pathname, search, hash, which);
|
||||
navIsActive(pathname, search, hash, which, lang);
|
||||
const isHome = pathname === "/";
|
||||
const footerInContentFlow = pathname === "/browse";
|
||||
const popularHref = `/browse?tag=${popularTagParam(lang)}`;
|
||||
|
||||
const goSearch = () => {
|
||||
const s = q.trim();
|
||||
@@ -416,7 +421,7 @@ export function PublicLayout() {
|
||||
{t("latest")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/browse?sort=popular"
|
||||
to={popularHref}
|
||||
className={navClassName(na("browsePopular"))}
|
||||
aria-current={na("browsePopular") ? "page" : undefined}
|
||||
>
|
||||
@@ -518,7 +523,7 @@ export function PublicLayout() {
|
||||
{t("latest")}
|
||||
</Link>
|
||||
<Link
|
||||
to="/browse?sort=popular"
|
||||
to={popularHref}
|
||||
className={navClassName(na("browsePopular"))}
|
||||
aria-current={na("browsePopular") ? "page" : undefined}
|
||||
onClick={() => setOpen(false)}
|
||||
@@ -593,12 +598,12 @@ export function PublicLayout() {
|
||||
active={na("favorites")}
|
||||
/>
|
||||
<BottomNavIcon
|
||||
to="/browse?sort=popular"
|
||||
to={popularHref}
|
||||
label={t("popular")}
|
||||
icon="update"
|
||||
active={
|
||||
pathname === "/browse" &&
|
||||
new URLSearchParams(search).get("sort") === "popular"
|
||||
new URLSearchParams(search).get("tag") === "popular"
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -2,18 +2,20 @@ import { useSearchParams } from "react-router-dom";
|
||||
import { MessageStream } from "../../components/messageStream/MessageStream";
|
||||
import { SectionHeader } from "../../components/SectionHeader";
|
||||
import { useI18n } from "../../i18n";
|
||||
import { isPopularTag } from "../../utils/popularTag";
|
||||
|
||||
export function Browse() {
|
||||
const { t } = useI18n();
|
||||
const { t, lang } = useI18n();
|
||||
const [sp] = useSearchParams();
|
||||
const q = sp.get("q") || "";
|
||||
const sort = sp.get("sort") || "";
|
||||
const tag = sp.get("tag") || "";
|
||||
const title = q
|
||||
? `${t("search")}: ${q}`
|
||||
: sort === "latest"
|
||||
? t("latest")
|
||||
: sort === "popular"
|
||||
? t("popular")
|
||||
: isPopularTag(tag, lang)
|
||||
? t("popular")
|
||||
: sort === "latest"
|
||||
? t("latest")
|
||||
: sort === "recommended"
|
||||
? t("official")
|
||||
: t("all");
|
||||
|
||||
@@ -13,6 +13,7 @@ import { SectionHeader } from "../../components/SectionHeader";
|
||||
import { MessageBubble } from "../../components/messageStream/MessageBubble";
|
||||
import { langQuery, useI18n } from "../../i18n";
|
||||
import { sourceLanguageQuery } from "../../i18nLanguages";
|
||||
import { popularTagParam } from "../../utils/popularTag";
|
||||
import { cleanCategoryDisplayName } from "../../utils/categoryDisplay";
|
||||
import {
|
||||
postToResource,
|
||||
@@ -69,7 +70,7 @@ export function Home() {
|
||||
getJSON<{ items: Post[] }>(`/api/posts/recommended${postQ}&limit=12`),
|
||||
getJSON<{ items: Post[] }>(`/api/posts/latest${postQ}&limit=5`),
|
||||
getJSON<{ items: Post[] }>(
|
||||
`/api/posts${postQ}&sort=popular&limit=5`,
|
||||
`/api/posts${postQ}&tag=${popularTagParam(lang)}&limit=5`,
|
||||
).catch((): { items: Post[] } => ({ items: [] })),
|
||||
])
|
||||
.then(([c, r, l, p]) => {
|
||||
@@ -392,7 +393,7 @@ export function Home() {
|
||||
<div className="px-4 md:px-0">
|
||||
<SectionHeader
|
||||
title={t("popularSection")}
|
||||
viewAllTo="/browse?sort=popular"
|
||||
viewAllTo={`/browse?tag=${popularTagParam(lang)}`}
|
||||
viewAllLabel={t("viewAll")}
|
||||
/>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user