mono/packages/ui/src/components/GalleryLarge.tsx
2026-02-02 00:12:16 +01:00

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;