185 lines
6.6 KiB
TypeScript
185 lines
6.6 KiB
TypeScript
import { MediaGrid, PhotoGrid } from "./PhotoGrid";
|
|
import MediaCard from "./MediaCard";
|
|
import React, { useEffect, useState, useRef } from "react";
|
|
import { useAuth } from "@/hooks/useAuth";
|
|
import { useNavigate } from "react-router-dom";
|
|
import { useProfiles } from "@/contexts/ProfilesContext";
|
|
import { usePostNavigation } from "@/hooks/usePostNavigation";
|
|
import { useOrganization } from "@/contexts/OrganizationContext";
|
|
import { useFeedData } from "@/hooks/useFeedData";
|
|
import { normalizeMediaType, isVideoType } from "@/lib/mediaRegistry";
|
|
import { UserProfile } from '../pages/Post/types';
|
|
import * as db from '../pages/Post/db';
|
|
import type { MediaItem, MediaType } from "@/types";
|
|
import { supabase } from "@/integrations/supabase/client";
|
|
|
|
// Duplicate types for now or we could reuse specific generic props
|
|
// To minimalize refactoring PhotoGrid, I'll copy the logic but use the Feed variant
|
|
|
|
import type { FeedSortOption } from '@/hooks/useFeedData';
|
|
|
|
interface GalleryLargeProps {
|
|
customPictures?: MediaItem[];
|
|
customLoading?: boolean;
|
|
navigationSource?: 'home' | 'collection' | 'tag' | 'user';
|
|
navigationSourceId?: string;
|
|
sortBy?: FeedSortOption;
|
|
}
|
|
|
|
const GalleryLarge = ({
|
|
customPictures,
|
|
customLoading,
|
|
navigationSource = 'home',
|
|
navigationSourceId,
|
|
sortBy = 'latest'
|
|
}: GalleryLargeProps) => {
|
|
const { user } = useAuth();
|
|
const navigate = useNavigate();
|
|
const { setNavigationData, navigationData } = usePostNavigation();
|
|
const { orgSlug, isOrgContext } = useOrganization();
|
|
const [mediaItems, setMediaItems] = useState<MediaItem[]>([]);
|
|
const [userLikes, setUserLikes] = useState<Set<string>>(new Set());
|
|
const [loading, setLoading] = useState(true);
|
|
|
|
// 1. Data Fetching
|
|
const { posts: feedPosts, loading: feedLoading } = useFeedData({
|
|
source: navigationSource,
|
|
sourceId: navigationSourceId,
|
|
isOrgContext,
|
|
orgSlug,
|
|
sortBy,
|
|
// Disable hook if we have custom pictures
|
|
enabled: !customPictures
|
|
});
|
|
|
|
// 2. State & Effects
|
|
useEffect(() => {
|
|
let finalMedia: MediaItem[] = [];
|
|
|
|
if (customPictures) {
|
|
finalMedia = customPictures;
|
|
setLoading(customLoading || false);
|
|
} else {
|
|
// Map FeedPost[] -> MediaItemType[]
|
|
finalMedia = db.mapFeedPostsToMediaItems(feedPosts as any, sortBy);
|
|
setLoading(feedLoading);
|
|
}
|
|
|
|
setMediaItems(finalMedia || []);
|
|
|
|
// Update Navigation Data
|
|
if (finalMedia && finalMedia.length > 0) {
|
|
const navData = {
|
|
posts: finalMedia.map(item => ({
|
|
id: item.id,
|
|
title: item.title,
|
|
image_url: item.image_url,
|
|
user_id: item.user_id,
|
|
type: normalizeMediaType(item.type)
|
|
})),
|
|
currentIndex: 0,
|
|
source: navigationSource,
|
|
sourceId: navigationSourceId
|
|
};
|
|
setNavigationData(navData);
|
|
}
|
|
}, [feedPosts, feedLoading, customPictures, customLoading, navigationSource, navigationSourceId, setNavigationData, sortBy]);
|
|
|
|
const fetchUserLikes = async () => {
|
|
if (!user || mediaItems.length === 0) return;
|
|
|
|
try {
|
|
// Collect IDs to check (picture_id for feed, id for collection/direct pictures)
|
|
const targetIds = mediaItems
|
|
.map(item => item.picture_id || item.id)
|
|
.filter(Boolean) as string[];
|
|
|
|
if (targetIds.length === 0) return;
|
|
|
|
// Fetch likes only for the displayed items
|
|
const { data: likesData, error } = await supabase
|
|
.from('likes')
|
|
.select('picture_id')
|
|
.eq('user_id', user.id)
|
|
.in('picture_id', targetIds);
|
|
|
|
if (error) throw error;
|
|
|
|
// Merge new likes with existing set
|
|
setUserLikes(prev => {
|
|
const newSet = new Set(prev);
|
|
likesData?.forEach(l => newSet.add(l.picture_id));
|
|
return newSet;
|
|
});
|
|
} catch (error) {
|
|
console.error('Error fetching user likes:', error);
|
|
}
|
|
};
|
|
|
|
const handleError = () => {
|
|
window.location.reload();
|
|
}
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="py-8">
|
|
<div className="text-center text-muted-foreground">
|
|
Loading gallery...
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|
|
|
|
if (!mediaItems || mediaItems.length === 0) {
|
|
return (
|
|
<div className="py-8">
|
|
<div className="text-center text-muted-foreground">
|
|
<p className="text-lg">No media yet!</p>
|
|
<p>Be the first to share content with the community.</p>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="w-full relative max-w-4xl mx-auto px-4 pb-20 pt-4">
|
|
<div className="flex flex-col gap-12">
|
|
{mediaItems.map((item, index) => {
|
|
const itemType = normalizeMediaType(item.type);
|
|
const isVideo = isVideoType(itemType);
|
|
const displayUrl = item.image_url;
|
|
|
|
return (
|
|
<MediaCard
|
|
key={item.id}
|
|
id={item.id}
|
|
pictureId={item.picture_id}
|
|
url={displayUrl}
|
|
thumbnailUrl={item.thumbnail_url}
|
|
title={item.title}
|
|
author={undefined as any}
|
|
authorAvatarUrl={undefined}
|
|
authorId={item.user_id}
|
|
likes={item.likes_count || 0}
|
|
comments={item.comments[0]?.count || 0}
|
|
isLiked={userLikes.has(item.picture_id || item.id)}
|
|
description={item.description}
|
|
type={itemType}
|
|
meta={item.meta}
|
|
onClick={() => navigate(`/post/${item.id}`)}
|
|
onLike={fetchUserLikes}
|
|
onDelete={handleError}
|
|
created_at={item.created_at}
|
|
job={item.job}
|
|
responsive={item.responsive}
|
|
variant="feed"
|
|
/>
|
|
);
|
|
})}
|
|
</div>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default GalleryLarge;
|