diff --git a/JoyboyCommunity/src/hooks/index.ts b/JoyboyCommunity/src/hooks/index.ts new file mode 100644 index 00000000..0db9b3f7 --- /dev/null +++ b/JoyboyCommunity/src/hooks/index.ts @@ -0,0 +1,5 @@ +export {useProfile} from './useProfile'; +export {useReactions} from './useReactions'; +export {useReplyNotes} from './useReplyNotes'; +export {useReposts} from './useReposts'; +export {useRootNotes} from './useRootNotes'; diff --git a/JoyboyCommunity/src/hooks/useProfile.ts b/JoyboyCommunity/src/hooks/useProfile.ts new file mode 100644 index 00000000..6b38752a --- /dev/null +++ b/JoyboyCommunity/src/hooks/useProfile.ts @@ -0,0 +1,34 @@ +import {useQuery} from '@tanstack/react-query'; + +import {useNostrContext} from '../context/NostrContext'; +import {EventKind} from '../types'; + +export type UseProfileOptions = { + publicKey: string; +}; + +export const useProfile = (options: UseProfileOptions) => { + const {pool, othersRelays} = useNostrContext(); + + return useQuery({ + queryKey: ['profile', options.publicKey], + queryFn: async () => { + const profileEvent = await pool.get(othersRelays, { + kinds: [EventKind.Metadata], + authors: [options?.publicKey], + }); + + const profile = JSON.parse(profileEvent.content) as Record; + + return { + username: profile.name, + displayName: profile.display_name ?? profile.displayName, + website: profile.website, + banner: profile.banner, + about: profile.about, + picture: profile.picture, + }; + }, + placeholderData: {} as any, + }); +}; diff --git a/JoyboyCommunity/src/hooks/useReactions.ts b/JoyboyCommunity/src/hooks/useReactions.ts new file mode 100644 index 00000000..687623d1 --- /dev/null +++ b/JoyboyCommunity/src/hooks/useReactions.ts @@ -0,0 +1,38 @@ +import {useInfiniteQuery} from '@tanstack/react-query'; + +import {useNostrContext} from '../context/NostrContext'; +import {EventKind} from '../types'; + +export type UseReactionsOptions = { + authors?: string[]; + search?: string; +}; + +export const useReactions = (options?: UseReactionsOptions) => { + const {pool, othersRelays} = useNostrContext(); + + return useInfiniteQuery({ + initialPageParam: Math.round(Date.now() / 1000), + queryKey: ['reactions', options?.authors, options?.search], + getNextPageParam: (lastPage: any, allPages, lastPageParam) => { + if (!lastPage?.length) return undefined; + + const pageParam = lastPage[lastPage.length - 1].created_at; + + if (!pageParam || pageParam === lastPageParam) return undefined; + return pageParam; + }, + queryFn: async ({pageParam}) => { + const notes = await pool.querySync(othersRelays, { + kinds: [EventKind.Reaction], + authors: options?.authors, + search: options?.search, + until: pageParam, + limit: 20, + }); + + return notes; + }, + placeholderData: {pages: [], pageParams: []}, + }); +}; diff --git a/JoyboyCommunity/src/hooks/useReplyNotes.ts b/JoyboyCommunity/src/hooks/useReplyNotes.ts new file mode 100644 index 00000000..4056c6ab --- /dev/null +++ b/JoyboyCommunity/src/hooks/useReplyNotes.ts @@ -0,0 +1,41 @@ +import {useInfiniteQuery} from '@tanstack/react-query'; + +import {useNostrContext} from '../context/NostrContext'; +import {EventKind} from '../types'; + +export type UseReplyNotesOptions = { + noteId?: string; + authors?: string[]; + search?: string; +}; + +export const useReplyNotes = (options?: UseReplyNotesOptions) => { + const {pool, othersRelays} = useNostrContext(); + + return useInfiniteQuery({ + initialPageParam: Math.round(Date.now() / 1000), + queryKey: ['replyNotes', options?.noteId, options?.authors, options?.search], + getNextPageParam: (lastPage: any, allPages, lastPageParam) => { + if (!lastPage?.length) return undefined; + + const pageParam = lastPage[lastPage.length - 1].created_at; + + if (!pageParam || pageParam === lastPageParam) return undefined; + return pageParam; + }, + queryFn: async ({pageParam}) => { + const notes = await pool.querySync(othersRelays, { + kinds: [EventKind.Note], + authors: options?.authors, + search: options?.search, + until: pageParam, + limit: 20, + + '#e': options?.noteId ? [options.noteId] : undefined, + }); + + return notes.filter((note) => note.tags.every((tag) => tag[0] === 'e')); + }, + placeholderData: {pages: [], pageParams: []}, + }); +}; diff --git a/JoyboyCommunity/src/hooks/useReposts.ts b/JoyboyCommunity/src/hooks/useReposts.ts new file mode 100644 index 00000000..67ba375a --- /dev/null +++ b/JoyboyCommunity/src/hooks/useReposts.ts @@ -0,0 +1,38 @@ +import {useInfiniteQuery} from '@tanstack/react-query'; + +import {useNostrContext} from '../context/NostrContext'; +import {EventKind} from '../types'; + +export type UseRepostsOptions = { + authors?: string[]; + search?: string; +}; + +export const useReposts = (options?: UseRepostsOptions) => { + const {pool, othersRelays} = useNostrContext(); + + return useInfiniteQuery({ + initialPageParam: Math.round(Date.now() / 1000), + queryKey: ['reposts', options?.authors, options?.search], + getNextPageParam: (lastPage: any, allPages, lastPageParam) => { + if (!lastPage?.length) return undefined; + + const pageParam = lastPage[lastPage.length - 1].created_at; + + if (!pageParam || pageParam === lastPageParam) return undefined; + return pageParam; + }, + queryFn: async ({pageParam}) => { + const reposts = await pool.querySync(othersRelays, { + kinds: [EventKind.Repost], + authors: options?.authors, + search: options?.search, + until: pageParam, + limit: 20, + }); + + return reposts; + }, + placeholderData: {pages: [], pageParams: []}, + }); +}; diff --git a/JoyboyCommunity/src/hooks/useRootNotes.ts b/JoyboyCommunity/src/hooks/useRootNotes.ts new file mode 100644 index 00000000..d50da4bc --- /dev/null +++ b/JoyboyCommunity/src/hooks/useRootNotes.ts @@ -0,0 +1,38 @@ +import {useInfiniteQuery} from '@tanstack/react-query'; + +import {useNostrContext} from '../context/NostrContext'; +import {EventKind} from '../types'; + +export type UseRootNotesOptions = { + authors?: string[]; + search?: string; +}; + +export const useRootNotes = (options?: UseRootNotesOptions) => { + const {pool, othersRelays} = useNostrContext(); + + return useInfiniteQuery({ + initialPageParam: Math.round(Date.now() / 1000), + queryKey: ['rootNotes', options?.authors, options?.search], + getNextPageParam: (lastPage: any, allPages, lastPageParam) => { + if (!lastPage?.length) return undefined; + + const pageParam = lastPage[lastPage.length - 1].created_at; + + if (!pageParam || pageParam === lastPageParam) return undefined; + return pageParam; + }, + queryFn: async ({pageParam}) => { + const notes = await pool.querySync(othersRelays, { + kinds: [EventKind.Note], + authors: options?.authors, + search: options?.search, + until: pageParam, + limit: 20, + }); + + return notes.filter((note) => note.tags.every((tag) => tag[0] !== 'e')); + }, + placeholderData: {pages: [], pageParams: []}, + }); +}; diff --git a/JoyboyCommunity/src/modules/feed/index.tsx b/JoyboyCommunity/src/modules/feed/index.tsx index 028adfc0..22f97e45 100644 --- a/JoyboyCommunity/src/modules/feed/index.tsx +++ b/JoyboyCommunity/src/modules/feed/index.tsx @@ -5,7 +5,7 @@ import {FlatList, RefreshControl} from 'react-native-gesture-handler'; import {SafeAreaView} from 'react-native-safe-area-context'; import styled from 'styled-components/native'; -import {useGetPoolEventsNotes} from '../../hooks/useNostr'; +import {useRootNotes} from '../../hooks'; import {Post} from '../../shared/components/Post'; import FloatingPostButton from './FloatingPostButton'; import {styles} from './style'; @@ -19,11 +19,7 @@ const FixedPostButton = styled(View)` export default function Feed() { const bottomBarHeight = useBottomTabBarHeight(); - const { - data: poolEventNotesData, - isLoading: poolEventNotesDataLoading, - refetch, - } = useGetPoolEventsNotes(); + const notes = useRootNotes(); const stories = [ {name: 'Luffy', img: require('../../assets/feed/images/story-1.png')}, @@ -73,14 +69,14 @@ export default function Feed() { /> - {poolEventNotesDataLoading && } + {notes.isLoading && } item?.id} renderItem={({item}) => { return ( @@ -91,10 +87,11 @@ export default function Feed() { ); }} refreshControl={ - refetch()} /> + notes.refetch()} /> } /> + diff --git a/JoyboyCommunity/src/types/index.ts b/JoyboyCommunity/src/types/index.ts index 4533f385..c08cf2c4 100644 --- a/JoyboyCommunity/src/types/index.ts +++ b/JoyboyCommunity/src/types/index.ts @@ -1,5 +1,6 @@ import {Event as EventNostr, Filter, SimplePool} from 'nostr-tools'; +export * from './nostr'; export * from './post'; export * from './routes'; diff --git a/JoyboyCommunity/src/types/nostr.ts b/JoyboyCommunity/src/types/nostr.ts new file mode 100644 index 00000000..d9202b91 --- /dev/null +++ b/JoyboyCommunity/src/types/nostr.ts @@ -0,0 +1,17 @@ +export enum EventKind { + Metadata = 0, + Note = 1, + Follow = 3, + EncryptedDM = 4, + EventDeletion = 5, + Repost = 6, + Reaction = 7, + BadgeAward = 8, + GroupChatMessage = 9, + GroupChatThreadedReply = 10, + GroupThread = 11, + GroupThreadReply = 12, + Seal = 13, + DirectMessage = 14, + GenericRepost = 16, +}