mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-04 01:31:13 +02:00
Add useFollowerPictureFeed hook and integrate it into Index page for user-specific picture feed
This commit is contained in:
60
src/hooks/useFollowerPictureFeed.ts
Normal file
60
src/hooks/useFollowerPictureFeed.ts
Normal file
@@ -0,0 +1,60 @@
|
||||
import { useNostr } from '@nostrify/react';
|
||||
import { useInfiniteQuery } from '@tanstack/react-query';
|
||||
import type { NostrEvent } from '@nostrify/nostrify';
|
||||
import { useCurrentUser } from './useCurrentUser';
|
||||
|
||||
/**
|
||||
* Hook for fetching kind 20 (NIP-68) picture events from followed users with infinite scroll
|
||||
* Uses NIP-02 follow list to filter events
|
||||
*/
|
||||
export function useFollowerPictureFeed() {
|
||||
const { nostr } = useNostr();
|
||||
const { user } = useCurrentUser();
|
||||
|
||||
return useInfiniteQuery({
|
||||
queryKey: ['follower-picture-feed', user?.pubkey],
|
||||
queryFn: async ({ pageParam, signal }) => {
|
||||
if (!user?.pubkey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// First, fetch the user's follow list (kind 3, NIP-02)
|
||||
const followListEvents = await nostr.query(
|
||||
[{ kinds: [3], authors: [user.pubkey], limit: 1 }],
|
||||
{ signal: AbortSignal.any([signal, AbortSignal.timeout(1500)]) }
|
||||
);
|
||||
|
||||
if (followListEvents.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract followed pubkeys from p tags
|
||||
const followedPubkeys = followListEvents[0].tags
|
||||
.filter(([tag]) => tag === 'p')
|
||||
.map(([, pubkey]) => pubkey)
|
||||
.filter(Boolean);
|
||||
|
||||
if (followedPubkeys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Fetch pictures from followed users
|
||||
const filter = pageParam
|
||||
? { kinds: [20], authors: followedPubkeys, limit: 20, until: pageParam as number }
|
||||
: { kinds: [20], authors: followedPubkeys, limit: 20 };
|
||||
|
||||
const events = await nostr.query([filter], {
|
||||
signal: AbortSignal.any([signal, AbortSignal.timeout(1500)])
|
||||
});
|
||||
|
||||
return events as NostrEvent[];
|
||||
},
|
||||
getNextPageParam: (lastPage) => {
|
||||
if (lastPage.length === 0) return undefined;
|
||||
// Subtract 1 since 'until' is inclusive
|
||||
return lastPage[lastPage.length - 1].created_at - 1;
|
||||
},
|
||||
initialPageParam: undefined as number | undefined,
|
||||
enabled: !!user?.pubkey,
|
||||
});
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useSeoMeta } from '@unhead/react';
|
||||
import { Layout } from '@/components/Layout';
|
||||
import { usePictureFeed } from '@/hooks/usePictureFeed';
|
||||
import { useFollowerPictureFeed } from '@/hooks/useFollowerPictureFeed';
|
||||
import { PictureCard } from '@/components/feed/PictureCard';
|
||||
import { MinimalPictureCard } from '@/components/feed/MinimalPictureCard';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
@@ -8,6 +9,7 @@ import { useEffect, useMemo } from 'react';
|
||||
import { Skeleton } from '@/components/ui/skeleton';
|
||||
import { Card, CardContent } from '@/components/ui/card';
|
||||
import { useAppContext } from '@/hooks/useAppContext';
|
||||
import { useCurrentUser } from '@/hooks/useCurrentUser';
|
||||
|
||||
const Index = () => {
|
||||
useSeoMeta({
|
||||
@@ -16,7 +18,13 @@ const Index = () => {
|
||||
});
|
||||
|
||||
const { config } = useAppContext();
|
||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = usePictureFeed();
|
||||
const { user } = useCurrentUser();
|
||||
|
||||
// Use follower feed for logged-in users, global feed for logged-out users
|
||||
const globalFeed = usePictureFeed();
|
||||
const followerFeed = useFollowerPictureFeed();
|
||||
const { data, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading } = user ? followerFeed : globalFeed;
|
||||
|
||||
const { ref, inView } = useInView();
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
Reference in New Issue
Block a user