feat(filters): make the feed's channel filter reachable
`filters.channelId` was settable by exactly one thing: a "?channel=" URL from a shared view. The panel still rendered a CHANNEL card whose only control was "remove" — a remove button for a filter the UI could not apply. Two ways in now. The panel gets a real Channel group (searchable picker, first-class alongside date/tags/language, so the chip row surfaces it for free); normalizeLayout appends it to saved layouts, so nobody loses their arrangement. And a subscribed channel's page gets "Show in my feed", which applies the filter and keeps the rest of your filters — the point is "this channel, the way I normally read", which the channel page itself can't answer (it runs show=all, scope=all over the whole catalog). The picker owns its channel query so a few-hundred-entry list loads when the panel is actually opened, not on every feed visit, and says "showing 50 of N" rather than quietly stopping.
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
import { useCallback, useEffect, useRef, useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query";
|
||||
import { ArrowLeft, Ban, Check, ExternalLink, Loader2, Plus, RefreshCw } from "lucide-react";
|
||||
import { ArrowLeft, Ban, Check, ExternalLink, Loader2, Plus, RefreshCw, SlidersHorizontal } from "lucide-react";
|
||||
import Avatar from "./Avatar";
|
||||
import Feed from "./Feed";
|
||||
import { useConfirm } from "./ConfirmProvider";
|
||||
@@ -56,12 +56,17 @@ export default function ChannelPage({
|
||||
view,
|
||||
onBack,
|
||||
onOpenChannel,
|
||||
onShowInFeed,
|
||||
}: {
|
||||
channelId: string;
|
||||
initialName?: string;
|
||||
me: Me;
|
||||
view: "grid" | "list";
|
||||
onBack: () => void;
|
||||
// Narrow the real feed to this channel and go there. Offered only for a channel you're
|
||||
// subscribed to: the feed defaults to scope "my", so filtering it to a channel you don't
|
||||
// follow would just land you on an empty page.
|
||||
onShowInFeed: () => void;
|
||||
onOpenChannel: (id: string, name?: string) => void;
|
||||
}) {
|
||||
const { t, i18n } = useTranslation();
|
||||
@@ -290,6 +295,16 @@ export default function ChannelPage({
|
||||
>
|
||||
<ExternalLink className="w-4 h-4" />
|
||||
</a>
|
||||
{ch?.subscribed && (
|
||||
<button
|
||||
onClick={onShowInFeed}
|
||||
title={t("channel.showInFeed")}
|
||||
aria-label={t("channel.showInFeed")}
|
||||
className="inline-flex items-center justify-center w-9 h-9 rounded-lg text-muted hover:text-accent border border-border hover:border-accent transition"
|
||||
>
|
||||
<SlidersHorizontal className="w-4 h-4" />
|
||||
</button>
|
||||
)}
|
||||
{!me.is_demo && (
|
||||
<button
|
||||
onClick={() => block.mutate()}
|
||||
|
||||
Reference in New Issue
Block a user