refactor(nav): read navigation from context, drop the prop-drilling

VideoCard/PlayerModal read openChannel from useNavigationActions (the memo'd
VideoCard subscribes to the STABLE actions context, so a page/channel change
no longer re-renders it); NavSidebar/Header read page/setPage; ChannelPage
reads closeChannel; NotificationsPanel reads setPage. App sheds the matching
props, and Feed/VirtualFeed no longer thread onOpenChannel at all.
This commit is contained in:
2026-07-18 02:19:52 +02:00
parent 41bb1b2ab3
commit a1bdfece6d
9 changed files with 20 additions and 53 deletions
+4 -5
View File
@@ -1,6 +1,7 @@
import { useEffect, useMemo, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { createPortal } from "react-dom";
import { useNavigationActions } from "./NavigationProvider";
import { useQuery, useQueryClient } from "@tanstack/react-query";
import { AlertTriangle, ArrowLeft, Check, CheckCheck, ChevronLeft, ChevronRight, ExternalLink, Repeat, Repeat1, Settings, Shuffle, SkipBack, SkipForward, Volume2, VolumeX, X } from "lucide-react";
import Avatar from "./Avatar";
@@ -55,7 +56,6 @@ export default function PlayerModal({
startIndex,
onClose,
onState,
onOpenChannel,
}: {
video: Video;
// Where to start the opened video: a number of seconds (0 = Restart), or null/undefined
@@ -70,9 +70,9 @@ export default function PlayerModal({
onState: (id: string, status: string) => void;
// Open the active video's channel page in-app (closes the player first). The small external
// icon next to the name keeps the open-on-YouTube behaviour.
onOpenChannel?: (channelId: string, channelName?: string) => void;
}) {
const { t, i18n } = useTranslation();
const { openChannel } = useNavigationActions();
const qc = useQueryClient();
const descFade = useScrollFade();
// Browser/mouse Back closes the player instead of leaving the page.
@@ -840,16 +840,15 @@ export default function PlayerModal({
<div className="flex items-center gap-1.5 shrink-0 min-w-0">
<button
onClick={() => {
if (!onOpenChannel) return onClose();
const cid = active.channel_id;
const cname = active.channel_title ?? undefined;
// Closing the player pops its own history entry (useBackToClose → history.back()).
// Open the channel only AFTER that pop's popstate — pushing the channel entry
// before it would let the back remove it again (the bug: clicking the name only
// closed the player). One-shot listener fires after App's popstate handler.
// closed the player). One-shot listener fires after the popstate handler.
const open = () => {
window.removeEventListener("popstate", open);
onOpenChannel(cid, cname);
openChannel(cid, cname);
};
window.addEventListener("popstate", open);
onClose();