feat(tags): tag UX overhaul + per-card video reset
- Channel-row tags show only what's attached; a '+' opens a per-channel picker. Clicking a tag filters the feed by it; a 'Your tags' sidebar widget makes that filter visible/clearable. - Tag manager dialog (add/rename/delete; delete only confirms when the tag is in use), reachable from the Channel manager and the feed sidebar; hovering a tag's count lists its channels, each a link that focuses it in the Channel manager (DataTable gains an external filter input). - Video cards get a reset action that clears all watch state (incl. an in-progress position). - api.req() now raises the error dialog on 5xx and 400/409/422 with the server's reason.
This commit is contained in:
parent
b55a944e7c
commit
1e530d23a6
22 changed files with 445 additions and 49 deletions
|
|
@ -32,6 +32,7 @@ import SettingsPanel from "./components/SettingsPanel";
|
|||
import OnboardingWizard from "./components/OnboardingWizard";
|
||||
import { shouldAutoOpenOnboarding } from "./lib/onboarding";
|
||||
import Toaster from "./components/Toaster";
|
||||
import ErrorDialog from "./components/ErrorDialog";
|
||||
import About from "./components/About";
|
||||
import ReleaseNotes from "./components/ReleaseNotes";
|
||||
import VersionBanner from "./components/VersionBanner";
|
||||
|
|
@ -108,6 +109,16 @@ export default function App() {
|
|||
const [aboutOpen, setAboutOpen] = useState(false);
|
||||
const [notesOpen, setNotesOpen] = useState(false);
|
||||
const [notesHighlight, setNotesHighlight] = useState<string | undefined>(undefined);
|
||||
// "Focus this channel in the manager": jump to the Channels page and seed its name filter
|
||||
// so the channel is isolated. Cleared when leaving the page so it doesn't re-apply later.
|
||||
const [focusChannelName, setFocusChannelName] = useState<string | null>(null);
|
||||
const focusChannel = (name: string) => {
|
||||
setFocusChannelName(name);
|
||||
setPage("channels");
|
||||
};
|
||||
useEffect(() => {
|
||||
if (page !== "channels") setFocusChannelName(null);
|
||||
}, [page]);
|
||||
|
||||
function openReleaseNotes(highlight?: string) {
|
||||
setNotesHighlight(highlight);
|
||||
|
|
@ -271,6 +282,7 @@ export default function App() {
|
|||
setFilters={setFilters}
|
||||
layout={sidebarLayout}
|
||||
setLayout={setSidebarLayout}
|
||||
onFocusChannel={focusChannel}
|
||||
/>
|
||||
)}
|
||||
<main className="flex-1 min-w-0 overflow-y-auto">
|
||||
|
|
@ -278,6 +290,8 @@ export default function App() {
|
|||
<Channels
|
||||
canWrite={meQuery.data!.can_write}
|
||||
isAdmin={meQuery.data!.role === "admin"}
|
||||
focusChannelName={focusChannelName}
|
||||
onFocusChannel={focusChannel}
|
||||
statusFilter={channelFilter}
|
||||
setStatusFilter={setChannelFilter}
|
||||
onOpenWizard={() => setWizardOpen(true)}
|
||||
|
|
@ -285,6 +299,11 @@ export default function App() {
|
|||
setFilters({ ...filters, channelId: id, channelName: name, show: "all" });
|
||||
setPage("feed");
|
||||
}}
|
||||
onFilterByTag={(tagId, name) => {
|
||||
setFilters({ ...filters, tags: [tagId], channelId: undefined, channelName: undefined });
|
||||
setPage("feed");
|
||||
notify({ level: "info", message: t("channels.notify.filteredByTag", { name }) });
|
||||
}}
|
||||
/>
|
||||
) : page === "stats" && meQuery.data!.role === "admin" ? (
|
||||
<Stats />
|
||||
|
|
@ -333,6 +352,7 @@ export default function App() {
|
|||
{notesOpen && (
|
||||
<ReleaseNotes onClose={() => setNotesOpen(false)} highlight={notesHighlight} />
|
||||
)}
|
||||
<ErrorDialog />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue