15 changed files with 167 additions and 33 deletions
@ -1,2 +1,5 @@
@@ -1,2 +1,5 @@
|
||||
export * from './useFetchEvent' |
||||
export * from './use-toast' |
||||
export * from './useFetchEventById' |
||||
export * from './useFetchFollowings' |
||||
export * from './useFetchNip05' |
||||
export * from './useFetchProfile' |
||||
|
||||
@ -0,0 +1,33 @@
@@ -0,0 +1,33 @@
|
||||
import { tagNameEquals } from '@renderer/lib/tag' |
||||
import client from '@renderer/services/client.service' |
||||
import { kinds } from 'nostr-tools' |
||||
import { useEffect, useState } from 'react' |
||||
|
||||
export function useFetchFollowings(pubkey?: string) { |
||||
const [followings, setFollowings] = useState<string[]>([]) |
||||
|
||||
useEffect(() => { |
||||
const init = async () => { |
||||
if (!pubkey) return |
||||
|
||||
const followListEvent = await client.fetchEventByFilter({ |
||||
authors: [pubkey], |
||||
kinds: [kinds.Contacts], |
||||
limit: 1 |
||||
}) |
||||
if (!followListEvent) return |
||||
|
||||
setFollowings( |
||||
followListEvent.tags |
||||
.filter(tagNameEquals('p')) |
||||
.map(([, pubkey]) => pubkey) |
||||
.filter(Boolean) |
||||
.reverse() |
||||
) |
||||
} |
||||
|
||||
init() |
||||
}, [pubkey]) |
||||
|
||||
return followings |
||||
} |
||||
@ -0,0 +1,71 @@
@@ -0,0 +1,71 @@
|
||||
import Nip05 from '@renderer/components/Nip05' |
||||
import UserAvatar from '@renderer/components/UserAvatar' |
||||
import Username from '@renderer/components/Username' |
||||
import { useFetchFollowings, useFetchProfile } from '@renderer/hooks' |
||||
import SecondaryPageLayout from '@renderer/layouts/SecondaryPageLayout' |
||||
import { useEffect, useRef, useState } from 'react' |
||||
|
||||
export default function ProfilePage({ pubkey }: { pubkey?: string }) { |
||||
const { username } = useFetchProfile(pubkey) |
||||
const followings = useFetchFollowings(pubkey) |
||||
const [visibleFollowings, setVisibleFollowings] = useState<string[]>([]) |
||||
const observer = useRef<IntersectionObserver | null>(null) |
||||
const bottomRef = useRef<HTMLDivElement>(null) |
||||
|
||||
useEffect(() => { |
||||
setVisibleFollowings(followings.slice(0, 10)) |
||||
}, [followings]) |
||||
|
||||
useEffect(() => { |
||||
const options = { |
||||
root: null, |
||||
rootMargin: '10px', |
||||
threshold: 1 |
||||
} |
||||
|
||||
observer.current = new IntersectionObserver((entries) => { |
||||
if (entries[0].isIntersecting && followings.length > visibleFollowings.length) { |
||||
setVisibleFollowings((prev) => [ |
||||
...prev, |
||||
...followings.slice(prev.length, prev.length + 10) |
||||
]) |
||||
} |
||||
}, options) |
||||
|
||||
if (bottomRef.current) { |
||||
observer.current.observe(bottomRef.current) |
||||
} |
||||
|
||||
return () => { |
||||
if (observer.current && bottomRef.current) { |
||||
observer.current.unobserve(bottomRef.current) |
||||
} |
||||
} |
||||
}, [visibleFollowings]) |
||||
|
||||
return ( |
||||
<SecondaryPageLayout titlebarContent={username ? `${username}'s following` : 'following'}> |
||||
<div className="space-y-2"> |
||||
{visibleFollowings.map((pubkey, index) => ( |
||||
<FollowingItem key={index} pubkey={pubkey} /> |
||||
))} |
||||
{followings.length > visibleFollowings.length && <div ref={bottomRef} />} |
||||
</div> |
||||
</SecondaryPageLayout> |
||||
) |
||||
} |
||||
|
||||
function FollowingItem({ pubkey }: { pubkey: string }) { |
||||
const { about, nip05 } = useFetchProfile(pubkey) |
||||
|
||||
return ( |
||||
<div className="flex gap-2 items-start"> |
||||
<UserAvatar userId={pubkey} /> |
||||
<div className="w-full overflow-hidden"> |
||||
<Username userId={pubkey} className="font-semibold truncate" /> |
||||
<Nip05 nip05={nip05} pubkey={pubkey} /> |
||||
<div className="truncate text-muted-foreground">{about}</div> |
||||
</div> |
||||
</div> |
||||
) |
||||
} |
||||
Loading…
Reference in new issue