diff --git a/frontend/src/components/VideoPlayer/VideoPlayer.tsx b/frontend/src/components/VideoPlayer/VideoPlayer.tsx index 234e06d..86e2c95 100644 --- a/frontend/src/components/VideoPlayer/VideoPlayer.tsx +++ b/frontend/src/components/VideoPlayer/VideoPlayer.tsx @@ -1,17 +1,23 @@ import { useEffect, useRef } from 'react'; import { useTimeLimit } from '../../hooks/useTimeLimit'; +import { setCurrentVideo } from '../../services/connectionTracker'; interface VideoPlayerProps { videoId: string; + videoTitle?: string; + channelName?: string; onClose: () => void; } -export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { +export function VideoPlayer({ videoId, videoTitle, channelName, onClose }: VideoPlayerProps) { const { limitReached, startTracking, stopTracking, remainingTime } = useTimeLimit(); const iframeRef = useRef(null); const checkLimitIntervalRef = useRef(null); useEffect(() => { + // Set video info for connection tracking + setCurrentVideo(videoTitle && channelName ? { title: videoTitle, channelName } : null); + // Prevent body scroll document.body.style.overflow = 'hidden'; @@ -19,6 +25,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { const handleEscape = (e: KeyboardEvent) => { if (e.key === 'Escape') { stopTracking(); + setCurrentVideo(null); onClose(); } }; @@ -37,10 +44,13 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { iframeRef.current.src = iframeRef.current.src.replace('autoplay=1', 'autoplay=0'); } stopTracking(); + setCurrentVideo(null); } }, 1000); return () => { + // Clear video info when player closes + setCurrentVideo(null); document.body.style.overflow = 'unset'; window.removeEventListener('keydown', handleEscape); stopTracking(); @@ -48,7 +58,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { clearInterval(checkLimitIntervalRef.current); } }; - }, [onClose, limitReached, startTracking, stopTracking]); + }, [videoId, videoTitle, channelName, onClose, limitReached, startTracking, stopTracking]); // Stop video immediately if limit reached useEffect(() => { @@ -64,6 +74,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { const handleClose = () => { stopTracking(); + setCurrentVideo(null); onClose(); }; diff --git a/frontend/src/pages/VideoApp.tsx b/frontend/src/pages/VideoApp.tsx index d243d7e..411fe69 100644 --- a/frontend/src/pages/VideoApp.tsx +++ b/frontend/src/pages/VideoApp.tsx @@ -7,7 +7,7 @@ import { VideoPlayer } from '../components/VideoPlayer/VideoPlayer'; export function VideoApp() { const [searchParams] = useSearchParams(); - const [selectedVideo, setSelectedVideo] = useState(null); + const [selectedVideo, setSelectedVideo] = useState<{ id: string; title: string; channelName: string } | null>(null); const { limitReached } = useTimeLimit(); // Read from URL query params @@ -37,7 +37,15 @@ export function VideoApp() { if (limitReached) { return; // Don't allow video to open if limit reached } - setSelectedVideo(videoId); + // Find the video to get title and channel name + const video = videos.find(v => v.id === videoId); + if (video) { + setSelectedVideo({ + id: videoId, + title: video.title, + channelName: video.channelName + }); + } }; return ( @@ -61,7 +69,9 @@ export function VideoApp() { {selectedVideo && ( setSelectedVideo(null)} /> )} diff --git a/frontend/src/services/apiClient.ts b/frontend/src/services/apiClient.ts index a1af4c2..99684ee 100644 --- a/frontend/src/services/apiClient.ts +++ b/frontend/src/services/apiClient.ts @@ -71,7 +71,10 @@ api.interceptors.response.use( } catch (refreshError) { processQueue(refreshError, null); localStorage.removeItem('access_token'); - window.location.href = '/login'; + // Only redirect if we're not already on login page + if (window.location.pathname !== '/login') { + window.location.href = '/login'; + } return Promise.reject(refreshError); } finally { isRefreshing = false; @@ -124,7 +127,11 @@ export const settingsApi = { getTimeLimit: () => api.get('/settings/time-limit'), setTimeLimit: (dailyLimit: number) => - api.put('/settings/time-limit', { dailyLimit }) + api.put('/settings/time-limit', { dailyLimit }), + + heartbeat: (sessionId: string, route: string, video?: { title: string; channelName: string }, timeLimit?: { timeUsed: number; dailyLimit: number }) => api.post('/settings/heartbeat', { sessionId, route, videoTitle: video?.title, videoChannel: video?.channelName, timeUsed: timeLimit?.timeUsed, dailyLimit: timeLimit?.dailyLimit }), + + getConnectionStats: () => api.get('/settings/connection-stats') }; // Word Groups API @@ -147,5 +154,3 @@ export const wordGroupsApi = { api.delete(`/word-groups/words/${wordId}`) }; - -