feat(feed): saved views as a header popover + Personal Views modal + a view chip
Retire the standalone 'Saved views' sidebar panel. The saved-views feature now
lives in three lighter surfaces, coordinated by a new SavedViewsProvider (query +
CRUD + the active/sticky view + modal state):
- the filter panel's header ICON opens a quick-pick popover (apply a view, active
one checked, plus 'Manage views…');
- the header TITLE ('Filters') opens a new Personal Views modal — a master/detail
manager: left = the reorderable list + a save-current-filters input, right = the
selected view's name, its captured filters (shown as the same chips the toolbar
row uses), default toggle, apply, and delete;
- a SelectedViewChip leads the applied-filter row, naming the active view with a
warn-coloured 'modified' dot once the live filters diverge from it; clicking it
reopens the picker.
SidePanel/FilterRail gain optional iconExpanded + onTitleClick slots (feed-only,
hidden for the demo account); the collapsed tab keeps its plain pin behaviour.
SavedViewsWidget is deleted and dropped from the panel layout.
Verified live: icon popover lists HH + Manage; applying HH surfaces the chip and
switches scope; removing a filter lights the dirty dot; the title opens the
master/detail modal with the captured-filters summary. 374 vitest, tsc, eslint
(0 err).
This commit is contained in:
@@ -9,7 +9,9 @@ import { useDebounced } from "../lib/useDebounced";
|
||||
import { clearedFilters, useActiveFilters } from "../lib/useActiveFilters";
|
||||
import { applyDateChip, deriveDateKey } from "../lib/dateFilter";
|
||||
import TagManager from "./TagManager";
|
||||
import SavedViewsWidget from "./SavedViewsWidget";
|
||||
import SavedViewsPopover from "./SavedViewsPopover";
|
||||
import PersonalViewsModal from "./PersonalViewsModal";
|
||||
import { useSavedViews } from "./SavedViewsProvider";
|
||||
import FilterRail from "./FilterRail";
|
||||
import { type PanelItem } from "./PanelGroups";
|
||||
import { type MediaSourceKind } from "../lib/mediaSource";
|
||||
@@ -42,6 +44,7 @@ export default function Sidebar({
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const isDemo = useMe().is_demo;
|
||||
const { modalOpen, openModal } = useSavedViews();
|
||||
const filters = useFeedFilters();
|
||||
const { setFilters } = useFeedFiltersActions();
|
||||
// "Focus this channel in the manager" (from the tag manager here) lives in ChannelsProvider.
|
||||
@@ -111,7 +114,6 @@ export default function Sidebar({
|
||||
}
|
||||
|
||||
const available: Record<string, boolean> = {
|
||||
savedviews: !isDemo,
|
||||
date: true,
|
||||
channel: true,
|
||||
language: languages.length > 0,
|
||||
@@ -121,8 +123,6 @@ export default function Sidebar({
|
||||
|
||||
function widgetBody(id: string): ReactNode {
|
||||
switch (id) {
|
||||
case "savedviews":
|
||||
return <SavedViewsWidget filters={filters} onApply={setFilters} />;
|
||||
case "channel":
|
||||
return <ChannelPicker filters={filters} setFilters={setFilters} />;
|
||||
case "date": {
|
||||
@@ -278,10 +278,19 @@ export default function Sidebar({
|
||||
setLayout={setLayout}
|
||||
items={items}
|
||||
actions={actions}
|
||||
// Saved ("personal") views: the header icon opens a quick-pick popover, the title opens the
|
||||
// full manager modal. Hidden for the demo account (no per-user views). Feed rail only.
|
||||
iconExpanded={
|
||||
!isDemo && source === "feed" ? (
|
||||
<SavedViewsPopover trigger={<SlidersHorizontal className="w-4 h-4" />} />
|
||||
) : undefined
|
||||
}
|
||||
onTitleClick={!isDemo && source === "feed" ? openModal : undefined}
|
||||
>
|
||||
{tagManagerOpen && (
|
||||
<TagManager onClose={() => setTagManagerOpen(false)} onFocusChannel={onFocusChannel} />
|
||||
)}
|
||||
{modalOpen && source === "feed" && <PersonalViewsModal />}
|
||||
</FilterRail>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user