From 9332420ddf983bbc3124105773850aa47e292457 Mon Sep 17 00:00:00 2001 From: npeter83 Date: Fri, 12 Jun 2026 17:38:45 +0200 Subject: [PATCH] feat(player): in-app modal YouTube player with resume MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Left-clicking a feed card (Ctrl/Cmd/middle still open youtube.com in a new tab) opens a modal that plays the video in-app via the YouTube IFrame Player API instead of leaving the app. Using the JS API (not a bare embed) lets us read the playback position: it's checkpointed per-video in localStorage and on close, and restored via the 'start' param when the video is reopened. The modal closes via a header button, the backdrop, or ESC (ESC only while focus is on our page — a cross-origin iframe owns its own key events). --- frontend/src/components/Feed.tsx | 7 + frontend/src/components/PlayerModal.tsx | 202 ++++++++++++++++++++++++ frontend/src/components/VideoCard.tsx | 31 +++- 3 files changed, 236 insertions(+), 4 deletions(-) create mode 100644 frontend/src/components/PlayerModal.tsx diff --git a/frontend/src/components/Feed.tsx b/frontend/src/components/Feed.tsx index 747149b..b5109a4 100644 --- a/frontend/src/components/Feed.tsx +++ b/frontend/src/components/Feed.tsx @@ -3,6 +3,7 @@ import { useInfiniteQuery, useQuery, useQueryClient } from "@tanstack/react-quer import { api, type FeedFilters, type Video } from "../lib/api"; import { notify } from "../lib/notifications"; import VideoCard from "./VideoCard"; +import PlayerModal from "./PlayerModal"; const PAGE = 60; @@ -31,6 +32,7 @@ export default function Feed({ view: "grid" | "list"; }) { const [overrides, setOverrides] = useState>({}); + const [activeVideo, setActiveVideo] = useState