feat(player): the YouTube player and the file viewer are named in the address
The player names what it plays in its own history entry (watch, wrel, wtab): a reload or a
bookmark reopens it on the page that hosts it, with that page's list as the queue, or alone
from the catalog (GET /api/videos/{id}/row); a page that hosts none gets the shell's. Back
lands on the page's address without it (useBackToClose's url; ui/Modal passes it on). The
Files viewer and player do the same (fview, fplay). The Plex and channel-manager views are
always written in full, as their fallback is remembered state.
This commit is contained in:
@@ -68,6 +68,7 @@ import {
|
||||
import { canFullscreenElements, fullscreenElement } from "../../lib/fullscreen";
|
||||
import { dispatch, isActivationTarget, setScopeCoveredProbe, useKeyScope } from "../../lib/keymap";
|
||||
import { useBackToClose } from "../../lib/history";
|
||||
import { linkedPlayer, playerParamUpdates, type PlayerPanelTab } from "../../lib/playerUrlState";
|
||||
import ChannelNameLink from "../video/ChannelNameLink";
|
||||
import Toaster from "../../components/ui/Toaster";
|
||||
import { useOverlayOrder } from "../../components/ui/overlayOrder";
|
||||
@@ -222,8 +223,6 @@ export default function PlayerModal({
|
||||
const { openChannel } = useNavigationActions();
|
||||
const qc = useQueryClient();
|
||||
const { ref: descFadeRef, style: descFadeStyle } = useScrollFade();
|
||||
// Browser/mouse Back closes the player instead of leaving the page.
|
||||
useBackToClose(onClose);
|
||||
// Freeze the launch queue: the player steps through the SAME filtered + sorted list it was
|
||||
// opened with, for its whole session. The live feed refetches and reorders/drops items under
|
||||
// us — e.g. auto-marking each video watched near its end removes it from a filtered feed — which
|
||||
@@ -277,6 +276,25 @@ export default function PlayerModal({
|
||||
setRelatedOf(active.id);
|
||||
setRelated(null);
|
||||
}
|
||||
// An address that names the item this player opens on (a reload, a bookmark) names its view too: the
|
||||
// related video playing in place over it and the panel's tab come back with it. Read once.
|
||||
const [linked] = useState(() => {
|
||||
const here = linkedPlayer();
|
||||
return here && here.watch === video.id ? here : null;
|
||||
});
|
||||
const linkedRelated = useQuery({
|
||||
queryKey: qk.videoRow(linked?.related ?? ""),
|
||||
queryFn: () => api.videoRow(linked!.related!),
|
||||
enabled: !!linked?.related,
|
||||
retry: false,
|
||||
});
|
||||
const [relatedAdopted, setRelatedAdopted] = useState(false);
|
||||
if (!relatedAdopted && linkedRelated.data) {
|
||||
setRelatedAdopted(true);
|
||||
// Only over the item it was named with: a step of the queue before it arrived left that one.
|
||||
if (active.id === linked?.watch && !related)
|
||||
setRelated({ video: linkedRelated.data, startAt: null });
|
||||
}
|
||||
const subject = related?.video ?? active;
|
||||
const subjectResume = related
|
||||
? (related.startAt ?? related.video.position_seconds ?? 0)
|
||||
@@ -753,7 +771,7 @@ export default function PlayerModal({
|
||||
// take the side panel's place of the description, a tab beside it; stacked, a drawer over the video
|
||||
// opened from a button beside the ⓘ. The list is the OPENED video's: one played from it leaves the
|
||||
// list in place, to try the next one (the owner's pick).
|
||||
const [panelTab, setPanelTab] = useState<"description" | "related">("description");
|
||||
const [panelTab, setPanelTab] = useState<PlayerPanelTab>(linked?.panel ?? "description");
|
||||
const [relatedOpen, setRelatedOpen] = useState(false);
|
||||
// The drawer is the stacked layout's: going wide while it is open, the list moves into the panel's
|
||||
// tab instead. Left open, it reopened unasked when the window narrowed again.
|
||||
@@ -762,6 +780,17 @@ export default function PlayerModal({
|
||||
setPanelTab("related");
|
||||
}
|
||||
const panelTabs = useTabPanel();
|
||||
// Browser/mouse Back closes the player instead of leaving the page. Its entry's address names what
|
||||
// it plays (the queue's item, a related one over it) and the wide panel's tab, so a reload or a
|
||||
// bookmark reopens it as it is; the page's own entry under it never carries them.
|
||||
useBackToClose(
|
||||
onClose,
|
||||
playerParamUpdates({
|
||||
watch: active.id,
|
||||
related: related?.video.id ?? null,
|
||||
panel: wide ? panelTab : "description",
|
||||
}),
|
||||
);
|
||||
// Turned off on this instance (a daily limit of 0): no tab, no button — nothing that could only
|
||||
// be refused.
|
||||
const relatedOn = useMe().related_enabled;
|
||||
|
||||
Reference in New Issue
Block a user