feat: Add toggle for view mode in Home page
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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: "确定要删除吗?",
|
||||||
|
|||||||
Reference in New Issue
Block a user