Browse Source

Add video title and channel tracking

drawing-pad
Stephanie Gredell 1 month ago
parent
commit
2febb869fe
  1. 15
      frontend/src/components/VideoPlayer/VideoPlayer.tsx
  2. 16
      frontend/src/pages/VideoApp.tsx
  3. 11
      frontend/src/services/apiClient.ts

15
frontend/src/components/VideoPlayer/VideoPlayer.tsx

@ -1,17 +1,23 @@
import { useEffect, useRef } from 'react'; import { useEffect, useRef } from 'react';
import { useTimeLimit } from '../../hooks/useTimeLimit'; import { useTimeLimit } from '../../hooks/useTimeLimit';
import { setCurrentVideo } from '../../services/connectionTracker';
interface VideoPlayerProps { interface VideoPlayerProps {
videoId: string; videoId: string;
videoTitle?: string;
channelName?: string;
onClose: () => void; onClose: () => void;
} }
export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) { export function VideoPlayer({ videoId, videoTitle, channelName, onClose }: VideoPlayerProps) {
const { limitReached, startTracking, stopTracking, remainingTime } = useTimeLimit(); const { limitReached, startTracking, stopTracking, remainingTime } = useTimeLimit();
const iframeRef = useRef<HTMLIFrameElement>(null); const iframeRef = useRef<HTMLIFrameElement>(null);
const checkLimitIntervalRef = useRef<NodeJS.Timeout | null>(null); const checkLimitIntervalRef = useRef<NodeJS.Timeout | null>(null);
useEffect(() => { useEffect(() => {
// Set video info for connection tracking
setCurrentVideo(videoTitle && channelName ? { title: videoTitle, channelName } : null);
// Prevent body scroll // Prevent body scroll
document.body.style.overflow = 'hidden'; document.body.style.overflow = 'hidden';
@ -19,6 +25,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) {
const handleEscape = (e: KeyboardEvent) => { const handleEscape = (e: KeyboardEvent) => {
if (e.key === 'Escape') { if (e.key === 'Escape') {
stopTracking(); stopTracking();
setCurrentVideo(null);
onClose(); onClose();
} }
}; };
@ -37,10 +44,13 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) {
iframeRef.current.src = iframeRef.current.src.replace('autoplay=1', 'autoplay=0'); iframeRef.current.src = iframeRef.current.src.replace('autoplay=1', 'autoplay=0');
} }
stopTracking(); stopTracking();
setCurrentVideo(null);
} }
}, 1000); }, 1000);
return () => { return () => {
// Clear video info when player closes
setCurrentVideo(null);
document.body.style.overflow = 'unset'; document.body.style.overflow = 'unset';
window.removeEventListener('keydown', handleEscape); window.removeEventListener('keydown', handleEscape);
stopTracking(); stopTracking();
@ -48,7 +58,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) {
clearInterval(checkLimitIntervalRef.current); clearInterval(checkLimitIntervalRef.current);
} }
}; };
}, [onClose, limitReached, startTracking, stopTracking]); }, [videoId, videoTitle, channelName, onClose, limitReached, startTracking, stopTracking]);
// Stop video immediately if limit reached // Stop video immediately if limit reached
useEffect(() => { useEffect(() => {
@ -64,6 +74,7 @@ export function VideoPlayer({ videoId, onClose }: VideoPlayerProps) {
const handleClose = () => { const handleClose = () => {
stopTracking(); stopTracking();
setCurrentVideo(null);
onClose(); onClose();
}; };

16
frontend/src/pages/VideoApp.tsx

@ -7,7 +7,7 @@ import { VideoPlayer } from '../components/VideoPlayer/VideoPlayer';
export function VideoApp() { export function VideoApp() {
const [searchParams] = useSearchParams(); const [searchParams] = useSearchParams();
const [selectedVideo, setSelectedVideo] = useState<string | null>(null); const [selectedVideo, setSelectedVideo] = useState<{ id: string; title: string; channelName: string } | null>(null);
const { limitReached } = useTimeLimit(); const { limitReached } = useTimeLimit();
// Read from URL query params // Read from URL query params
@ -37,7 +37,15 @@ export function VideoApp() {
if (limitReached) { if (limitReached) {
return; // Don't allow video to open if limit reached 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 ( return (
@ -61,7 +69,9 @@ export function VideoApp() {
{selectedVideo && ( {selectedVideo && (
<VideoPlayer <VideoPlayer
videoId={selectedVideo} videoId={selectedVideo.id}
videoTitle={selectedVideo.title}
channelName={selectedVideo.channelName}
onClose={() => setSelectedVideo(null)} onClose={() => setSelectedVideo(null)}
/> />
)} )}

11
frontend/src/services/apiClient.ts

@ -71,7 +71,10 @@ api.interceptors.response.use(
} catch (refreshError) { } catch (refreshError) {
processQueue(refreshError, null); processQueue(refreshError, null);
localStorage.removeItem('access_token'); localStorage.removeItem('access_token');
// Only redirect if we're not already on login page
if (window.location.pathname !== '/login') {
window.location.href = '/login'; window.location.href = '/login';
}
return Promise.reject(refreshError); return Promise.reject(refreshError);
} finally { } finally {
isRefreshing = false; isRefreshing = false;
@ -124,7 +127,11 @@ export const settingsApi = {
getTimeLimit: () => api.get('/settings/time-limit'), getTimeLimit: () => api.get('/settings/time-limit'),
setTimeLimit: (dailyLimit: number) => 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 // Word Groups API
@ -147,5 +154,3 @@ export const wordGroupsApi = {
api.delete(`/word-groups/words/${wordId}`) api.delete(`/word-groups/words/${wordId}`)
}; };

Loading…
Cancel
Save