From 440bdafa7495ed3dfeac6b2f54a0a7fcf8d3ae3d Mon Sep 17 00:00:00 2001 From: npeter83 Date: Thu, 13 Aug 2026 23:28:47 +0200 Subject: [PATCH] refactor(feed): drive the feed sort toolbar through the shared SortControl (R13 S1) The feed's bespoke select + asc/desc button is now the generic SortControl, fed a SortOption[] built from the feed vocabulary (relevance offered only with a query; shuffle/relevance directionless). The feedSort wire codec (parseSort/buildSort) stays as the server-boundary translation, and the reshuffle button rides SortControl's `trailing` slot so shuffle keeps its re-seed affordance. Behaviour-equivalent; ArrowUp/ArrowDown imports dropped. --- frontend/src/components/Feed.tsx | 87 ++++++++++++++------------------ 1 file changed, 37 insertions(+), 50 deletions(-) diff --git a/frontend/src/components/Feed.tsx b/frontend/src/components/Feed.tsx index f932e961..e7d47c94 100644 --- a/frontend/src/components/Feed.tsx +++ b/frontend/src/components/Feed.tsx @@ -8,8 +8,6 @@ import { } from "@tanstack/react-query"; import { qk } from "../lib/queryKeys"; import { - ArrowDown, - ArrowUp, ArrowLeft, Columns2, Grid3x3, @@ -34,12 +32,14 @@ import { SORT_KEYS, type SortKey, } from "../lib/feedSort"; +import { type SortOption } from "../lib/columnSort"; import { FEED_VIEWS, type FeedView } from "../lib/feedView"; import { clearedFilters, useActiveFilters } from "../lib/useActiveFilters"; import ActiveFilterChips from "./ActiveFilterChips"; import { LoadingState, StateMessage } from "./QueryState"; import { PageToolbar } from "./PageShell"; import { useWizard } from "./WizardProvider"; +import SortControl from "./SortControl"; import ViewSwitcher from "./ViewSwitcher"; import VirtualFeed from "./VirtualFeed"; // Lazy: the in-app YouTube player (IFrame API) loads only when a video is first opened. @@ -547,6 +547,18 @@ export default function Feed({ ); const { key: sortKey, dir: sortDir } = parseSort(filters.sort); + // The feed's sort vocabulary as source-agnostic options for the shared SortControl. Relevance only + // ranks meaningfully with a search term (offered then); shuffle/relevance are directionless. + const sortOptions: SortOption[] = ( + filters.q.trim() ? (["relevance", ...sortKeys] as SortKey[]) : sortKeys + ).map((k) => ({ + key: k, + label: t("feed.sortKey." + k), + directionless: DIRECTIONLESS.includes(k), + defaultDir: DIRECTIONLESS.includes(k) + ? undefined + : SORT_DEFAULT_DIR[k as Exclude], + })); const toolbar = (
@@ -615,58 +627,33 @@ export default function Feed({
{t("feed.sortLabel")} - - {!DIRECTIONLESS.includes(sortKey) && ( - - )} - {sortKey === "shuffle" && ( - - )} + label={t("feed.sortLabel")} + ascLabel={t("feed.dirAsc")} + descLabel={t("feed.dirDesc")} + trailing={ + sortKey === "shuffle" ? ( + + ) : undefined + } + />