From b725a912b0e9f9f8b831077dcba69f559852d1af Mon Sep 17 00:00:00 2001 From: Peifan Li Date: Tue, 25 Nov 2025 19:07:59 -0500 Subject: [PATCH] feat: Add toggle for view mode in Home page --- frontend/src/pages/Home.tsx | 108 ++++++++++++++++++++++------- frontend/src/utils/translations.ts | 2 + 2 files changed, 84 insertions(+), 26 deletions(-) diff --git a/frontend/src/pages/Home.tsx b/frontend/src/pages/Home.tsx index 1919ad0..c8d4c14 100644 --- a/frontend/src/pages/Home.tsx +++ b/frontend/src/pages/Home.tsx @@ -1,4 +1,4 @@ -import { ArrowBack, Download, OndemandVideo, YouTube } from '@mui/icons-material'; +import { ArrowBack, Collections as CollectionsIcon, Download, GridView, OndemandVideo, YouTube } from '@mui/icons-material'; import { Alert, Box, @@ -12,6 +12,8 @@ import { Container, Grid, Pagination, + ToggleButton, + ToggleButtonGroup, Typography } from '@mui/material'; import axios from 'axios'; @@ -70,6 +72,10 @@ const Home: React.FC = ({ const { t } = useLanguage(); const [availableTags, setAvailableTags] = useState([]); const [selectedTags, setSelectedTags] = useState([]); + const [viewMode, setViewMode] = useState<'collections' | 'all-videos'>(() => { + const saved = localStorage.getItem('homeViewMode'); + return (saved as 'collections' | 'all-videos') || 'collections'; + }); // Fetch tags useEffect(() => { @@ -112,32 +118,42 @@ const Home: React.FC = ({ ); } - // Filter videos to only show the first video from each collection - const filteredVideos = videoArray.filter(video => { - // Tag filtering - if (selectedTags.length > 0) { - const videoTags = video.tags || []; - const hasMatchingTag = selectedTags.every(tag => videoTags.includes(tag)); - if (!hasMatchingTag) return false; - } - - // If the video is not in any collection, show it - const videoCollections = collections.filter(collection => - collection.videos.includes(video.id) - ); - - if (videoCollections.length === 0) { + // Filter videos based on view mode + const filteredVideos = viewMode === 'all-videos' + ? videoArray.filter(video => { + // In all-videos mode, only apply tag filtering + if (selectedTags.length > 0) { + const videoTags = video.tags || []; + return selectedTags.every(tag => videoTags.includes(tag)); + } return true; - } + }) + : videoArray.filter(video => { + // In collections mode, show only first video from each collection + // Tag filtering + if (selectedTags.length > 0) { + const videoTags = video.tags || []; + const hasMatchingTag = selectedTags.every(tag => videoTags.includes(tag)); + if (!hasMatchingTag) return false; + } - // For each collection this video is in, check if it's the first video - return videoCollections.some(collection => { - // Get the first video ID in this collection - const firstVideoId = collection.videos[0]; - // Show this video if it's the first in at least one collection - return video.id === firstVideoId; + // If the video is not in any collection, show it + const videoCollections = collections.filter(collection => + collection.videos.includes(video.id) + ); + + if (videoCollections.length === 0) { + return true; + } + + // For each collection this video is in, check if it's the first video + return videoCollections.some(collection => { + // Get the first video ID in this collection + const firstVideoId = collection.videos[0]; + // Show this video if it's the first in at least one collection + return video.id === firstVideoId; + }); }); - }); const handleTagToggle = (tag: string) => { setSelectedTags(prev => @@ -148,6 +164,12 @@ const Home: React.FC = ({ setPage(1); // Reset to first page when filter changes }; + const handleViewModeChange = (mode: 'collections' | 'all-videos') => { + setViewMode(mode); + localStorage.setItem('homeViewMode', mode); + setPage(1); // Reset pagination + }; + // Pagination logic const totalPages = Math.ceil(filteredVideos.length / ITEMS_PER_PAGE); const displayedVideos = filteredVideos.slice( @@ -327,9 +349,43 @@ const Home: React.FC = ({ {/* Videos grid */} + {/* View mode toggle */} + + + {t('videos')} + + newMode && handleViewModeChange(newMode)} + size="small" + > + + + {t('collections')} + + + + {t('allVideos')} + + + {displayedVideos.map(video => { - // Check if this video is the first in a collection + // In all-videos mode, ALWAYS render as VideoCard + if (viewMode === 'all-videos') { + return ( + + + + ); + } + + // In collections mode, check if this video is the first in a collection const collection = collections.find(c => c.videos[0] === video.id); // If it is, render CollectionCard @@ -344,7 +400,7 @@ const Home: React.FC = ({ ); } - // Otherwise render VideoCard + // Otherwise render VideoCard for non-collection videos return (