diff --git a/frontend/src/components/FeedScopeToggle.tsx b/frontend/src/components/FeedScopeToggle.tsx new file mode 100644 index 00000000..b943f19c --- /dev/null +++ b/frontend/src/components/FeedScopeToggle.tsx @@ -0,0 +1,39 @@ +import { useTranslation } from "react-i18next"; +import { Library, User } from "lucide-react"; +import SegmentedToggle from "./SegmentedToggle"; +import { useFeedFilters, useFeedFiltersActions } from "./FeedFiltersProvider"; + +// The feed's Mine/Library scope, relocated from the filter sidebar to sit just right of the header +// search box — mirroring PlexScopeToggle's placement, so the primary "what am I browsing" control is +// always in reach instead of buried in the (possibly collapsed) filter rail. Same `filters.scope` +// field, so behaviour is unchanged. The `pointer-events-auto`/`shrink-0` wrapper matches the other +// header pills: the header band is `pointer-events-none`, and the pill must not collapse in the flex row. +export default function FeedScopeToggle() { + const { t } = useTranslation(); + const filters = useFeedFilters(); + const { setFilters } = useFeedFiltersActions(); + return ( +
+ , + title: t("header.scope.myTip"), + }, + { + value: "all", + label: t("header.scope.all"), + icon: , + title: t("header.scope.allTip"), + }, + ]} + value={filters.scope} + onChange={(scope) => setFilters({ ...filters, scope })} + /> +
+ ); +} diff --git a/frontend/src/components/FilterRail.tsx b/frontend/src/components/FilterRail.tsx index 2bc59b07..48bf4068 100644 --- a/frontend/src/components/FilterRail.tsx +++ b/frontend/src/components/FilterRail.tsx @@ -8,10 +8,9 @@ import PanelGroups, { type PanelItem } from "./PanelGroups"; // chrome around a PanelGroups body: the feed Sidebar, the PlexSidebar, and the PlaylistsRail. It owns // the customize (reorder/hide) `editing` state and the fixed title-bar wiring (edit toggle, reset to // the layout default, edit hint); each rail supplies only what actually differs — its identity -// (title/icon), the active-filter count + Clear-all action, its `items`, and any content above the -// groups (`top`, e.g. the feed's scope toggle) or below (`children`, e.g. the feed's tag manager). -// `kind` is the PanelId (also the layout key), so `top`/`children` default to nothing for the rails -// that need neither. +// (title/icon), the active-filter count + Clear-all action, its `items`, and any content below the +// groups (`children`, e.g. the feed's tag manager). `kind` is the PanelId (also the layout key), so +// `children` defaults to nothing for the rails that need none. export default function FilterRail({ kind, title, @@ -23,7 +22,6 @@ export default function FilterRail({ layout, setLayout, items, - top, children, }: { kind: PanelId; @@ -36,7 +34,6 @@ export default function FilterRail({ layout: PanelLayout; setLayout: (l: PanelLayout) => void; items: PanelItem[]; - top?: ReactNode; children?: ReactNode; }) { const { t } = useTranslation(); @@ -54,7 +51,6 @@ export default function FilterRail({ editHint={t("sidebar.editHint")} actions={actions} > - {top} {children} diff --git a/frontend/src/components/Header.tsx b/frontend/src/components/Header.tsx index d373b7ae..7618accc 100644 --- a/frontend/src/components/Header.tsx +++ b/frontend/src/components/Header.tsx @@ -5,6 +5,7 @@ import type { Me } from "../lib/api"; import SearchBar from "./SearchBar"; import PlexSearchBox from "./PlexSearchBox"; import PlexScopeToggle from "./PlexScopeToggle"; +import FeedScopeToggle from "./FeedScopeToggle"; import SyncStatus from "./SyncStatus"; import { useNavigation } from "./NavigationProvider"; import { useFeedFilters, useFeedFiltersActions } from "./FeedFiltersProvider"; @@ -98,6 +99,9 @@ const Header = forwardRef< className="pointer-events-none absolute top-3 left-4 right-[calc(1rem+var(--sbw))] mx-auto max-w-[var(--content-mw,none)] z-chrome flex flex-wrap items-center justify-center gap-x-3 gap-y-2" > {search} + {/* Mine/Library scope sits right of the feed search box — relocated here from the filter sidebar + so the primary "what am I browsing" control is always in reach (mirrors Plex's scope toggle). */} + {page === "feed" && } {/* Library scope (movie/show/both) sits right of the search box on Plex — relocated here from the filter sidebar (S3) so the primary "what am I browsing" control is always in reach. */} {page === "plex" && me.plex_enabled && } diff --git a/frontend/src/components/Sidebar.tsx b/frontend/src/components/Sidebar.tsx index 63a97252..1b549101 100644 --- a/frontend/src/components/Sidebar.tsx +++ b/frontend/src/components/Sidebar.tsx @@ -2,7 +2,7 @@ import { type ReactNode, useState } from "react"; import { useTranslation } from "react-i18next"; import { keepPreviousData, useQuery } from "@tanstack/react-query"; import { qk } from "../lib/queryKeys"; -import { Library, Pencil, SlidersHorizontal, User } from "lucide-react"; +import { Pencil, SlidersHorizontal } from "lucide-react"; import { api, type FeedFilters, type Tag } from "../lib/api"; import { defaultLayout, type PanelLayout } from "../lib/panelLayout"; import { useDebounced } from "../lib/useDebounced"; @@ -283,30 +283,6 @@ export default function Sidebar({ setLayout={setLayout} items={items} actions={actions} - top={ - /* Scope: your own subscriptions (Mine) vs the shared Library. */ - , - title: t("header.scope.myTip"), - }, - { - value: "all", - label: t("header.scope.all"), - icon: , - title: t("header.scope.allTip"), - }, - ]} - value={filters.scope} - onChange={(scope) => setFilters({ ...filters, scope })} - /> - } > {tagManagerOpen && ( setTagManagerOpen(false)} onFocusChannel={onFocusChannel} />