mirror of
https://github.com/mroxso/zelo-news.git
synced 2026-06-06 18:41:14 +02:00
Add FollowingPage component and related hooks; update routing and BlogHeader
This commit is contained in:
44
src/hooks/useFollowing.ts
Normal file
44
src/hooks/useFollowing.ts
Normal file
@@ -0,0 +1,44 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNostr } from '@nostrify/react';
|
||||
import { useCurrentUser } from './useCurrentUser';
|
||||
|
||||
/**
|
||||
* Hook to fetch the list of pubkeys that the current user follows.
|
||||
* Returns the pubkeys from the user's kind 3 contact list event.
|
||||
*/
|
||||
export function useFollowing() {
|
||||
const { nostr } = useNostr();
|
||||
const { user } = useCurrentUser();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['following', user?.pubkey],
|
||||
queryFn: async (c) => {
|
||||
if (!user?.pubkey) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const signal = AbortSignal.any([c.signal, AbortSignal.timeout(1500)]);
|
||||
|
||||
// Query kind 3 (contact list) for the current user
|
||||
const events = await nostr.query(
|
||||
[{ kinds: [3], authors: [user.pubkey], limit: 1 }],
|
||||
{ signal }
|
||||
);
|
||||
|
||||
if (events.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
// Extract pubkeys from p tags
|
||||
const contactEvent = events[0];
|
||||
const followedPubkeys = contactEvent.tags
|
||||
.filter(([tagName]) => tagName === 'p')
|
||||
.map(([, pubkey]) => pubkey)
|
||||
.filter((pubkey): pubkey is string => !!pubkey);
|
||||
|
||||
return followedPubkeys;
|
||||
},
|
||||
enabled: !!user?.pubkey,
|
||||
staleTime: 1000 * 60 * 5, // Cache for 5 minutes
|
||||
});
|
||||
}
|
||||
47
src/hooks/useFollowingBlogPosts.ts
Normal file
47
src/hooks/useFollowingBlogPosts.ts
Normal file
@@ -0,0 +1,47 @@
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import { useNostr } from '@nostrify/react';
|
||||
import type { NostrEvent } from '@nostrify/nostrify';
|
||||
import { useFollowing } from './useFollowing';
|
||||
|
||||
/**
|
||||
* Hook to fetch long-form blog posts (kind 30023) from authors the user follows.
|
||||
*/
|
||||
export function useFollowingBlogPosts() {
|
||||
const { nostr } = useNostr();
|
||||
const { data: followedPubkeys = [], isLoading: isLoadingFollowing } = useFollowing();
|
||||
|
||||
return useQuery({
|
||||
queryKey: ['following-blog-posts', followedPubkeys],
|
||||
queryFn: async (c) => {
|
||||
if (followedPubkeys.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const signal = AbortSignal.any([c.signal, AbortSignal.timeout(3000)]);
|
||||
|
||||
// Query kind 30023 (long-form content) from followed authors
|
||||
const events = await nostr.query(
|
||||
[
|
||||
{
|
||||
kinds: [30023],
|
||||
authors: followedPubkeys,
|
||||
limit: 50,
|
||||
},
|
||||
],
|
||||
{ signal }
|
||||
);
|
||||
|
||||
// Filter out events without required tags
|
||||
const validEvents = events.filter((event: NostrEvent) => {
|
||||
const hasTitle = event.tags.some(([name]) => name === 'title');
|
||||
const hasDTag = event.tags.some(([name]) => name === 'd');
|
||||
return hasTitle && hasDTag;
|
||||
});
|
||||
|
||||
// Sort by created_at descending (newest first)
|
||||
return validEvents.sort((a, b) => b.created_at - a.created_at);
|
||||
},
|
||||
enabled: followedPubkeys.length > 0 && !isLoadingFollowing,
|
||||
staleTime: 1000 * 60 * 2, // Cache for 2 minutes
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user