feat: Add toggle for view mode in Home page

This commit is contained in:
Peifan Li
2025-11-25 19:07:59 -05:00
parent cc522fe7e6
commit b725a912b0
2 changed files with 84 additions and 26 deletions

View File

@@ -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 { import {
Alert, Alert,
Box, Box,
@@ -12,6 +12,8 @@ import {
Container, Container,
Grid, Grid,
Pagination, Pagination,
ToggleButton,
ToggleButtonGroup,
Typography Typography
} from '@mui/material'; } from '@mui/material';
import axios from 'axios'; import axios from 'axios';
@@ -70,6 +72,10 @@ const Home: React.FC<HomeProps> = ({
const { t } = useLanguage(); const { t } = useLanguage();
const [availableTags, setAvailableTags] = useState<string[]>([]); const [availableTags, setAvailableTags] = useState<string[]>([]);
const [selectedTags, setSelectedTags] = useState<string[]>([]); const [selectedTags, setSelectedTags] = useState<string[]>([]);
const [viewMode, setViewMode] = useState<'collections' | 'all-videos'>(() => {
const saved = localStorage.getItem('homeViewMode');
return (saved as 'collections' | 'all-videos') || 'collections';
});
// Fetch tags // Fetch tags
useEffect(() => { useEffect(() => {
@@ -112,32 +118,42 @@ const Home: React.FC<HomeProps> = ({
); );
} }
// Filter videos to only show the first video from each collection // Filter videos based on view mode
const filteredVideos = videoArray.filter(video => { const filteredVideos = viewMode === 'all-videos'
// Tag filtering ? videoArray.filter(video => {
if (selectedTags.length > 0) { // In all-videos mode, only apply tag filtering
const videoTags = video.tags || []; if (selectedTags.length > 0) {
const hasMatchingTag = selectedTags.every(tag => videoTags.includes(tag)); const videoTags = video.tags || [];
if (!hasMatchingTag) return false; return selectedTags.every(tag => videoTags.includes(tag));
} }
// 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; 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 // If the video is not in any collection, show it
return videoCollections.some(collection => { const videoCollections = collections.filter(collection =>
// Get the first video ID in this collection collection.videos.includes(video.id)
const firstVideoId = collection.videos[0]; );
// Show this video if it's the first in at least one collection
return video.id === firstVideoId; 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) => { const handleTagToggle = (tag: string) => {
setSelectedTags(prev => setSelectedTags(prev =>
@@ -148,6 +164,12 @@ const Home: React.FC<HomeProps> = ({
setPage(1); // Reset to first page when filter changes 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 // Pagination logic
const totalPages = Math.ceil(filteredVideos.length / ITEMS_PER_PAGE); const totalPages = Math.ceil(filteredVideos.length / ITEMS_PER_PAGE);
const displayedVideos = filteredVideos.slice( const displayedVideos = filteredVideos.slice(
@@ -327,9 +349,43 @@ const Home: React.FC<HomeProps> = ({
{/* Videos grid */} {/* Videos grid */}
<Grid size={{ xs: 12, md: 9 }}> <Grid size={{ xs: 12, md: 9 }}>
{/* View mode toggle */}
<Box sx={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', mb: 3 }}>
<Typography variant="h5" fontWeight="bold">
{t('videos')}
</Typography>
<ToggleButtonGroup
value={viewMode}
exclusive
onChange={(_, newMode) => newMode && handleViewModeChange(newMode)}
size="small"
>
<ToggleButton value="collections">
<CollectionsIcon fontSize="small" sx={{ mr: 1 }} />
{t('collections')}
</ToggleButton>
<ToggleButton value="all-videos">
<GridView fontSize="small" sx={{ mr: 1 }} />
{t('allVideos')}
</ToggleButton>
</ToggleButtonGroup>
</Box>
<Grid container spacing={3}> <Grid container spacing={3}>
{displayedVideos.map(video => { {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 (
<Grid size={{ xs: 12, sm: 6, lg: 4, xl: 3 }} key={video.id}>
<VideoCard
video={video}
collections={collections}
disableCollectionGrouping={true}
/>
</Grid>
);
}
// In collections mode, check if this video is the first in a collection
const collection = collections.find(c => c.videos[0] === video.id); const collection = collections.find(c => c.videos[0] === video.id);
// If it is, render CollectionCard // If it is, render CollectionCard
@@ -344,7 +400,7 @@ const Home: React.FC<HomeProps> = ({
); );
} }
// Otherwise render VideoCard // Otherwise render VideoCard for non-collection videos
return ( return (
<Grid size={{ xs: 12, sm: 6, lg: 4, xl: 3 }} key={video.id}> <Grid size={{ xs: 12, sm: 6, lg: 4, xl: 3 }} key={video.id}>
<VideoCard <VideoCard

View File

@@ -83,6 +83,7 @@ export const translations = {
manageContent: "Manage Content", manageContent: "Manage Content",
videos: "Videos", videos: "Videos",
collections: "Collections", collections: "Collections",
allVideos: "All Videos",
delete: "Delete", delete: "Delete",
backToHome: "Back to Home", backToHome: "Back to Home",
confirmDelete: "Are you sure you want to delete this?", confirmDelete: "Are you sure you want to delete this?",
@@ -292,6 +293,7 @@ export const translations = {
manageContent: "内容管理", manageContent: "内容管理",
videos: "视频", videos: "视频",
collections: "合集", collections: "合集",
allVideos: "所有视频",
delete: "删除", delete: "删除",
backToHome: "返回首页", backToHome: "返回首页",
confirmDelete: "确定要删除吗?", confirmDelete: "确定要删除吗?",