Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import { UsersIcon } from "@heroicons/react/24/outline";
2import type { FollowersRequest } from "@hey/indexer";
3import { PageSize, useFollowersQuery } from "@hey/indexer";
4import { motion } from "motion/react";
5import { useCallback } from "react";
6import { Virtualizer } from "virtua";
7import SingleAccount from "@/components/Shared/Account/SingleAccount";
8import AccountListShimmer from "@/components/Shared/Shimmer/AccountListShimmer";
9import { EmptyState, ErrorMessage } from "@/components/Shared/UI";
10import cn from "@/helpers/cn";
11import useLoadMoreOnIntersect from "@/hooks/useLoadMoreOnIntersect";
12import { useAccountStore } from "@/store/persisted/useAccountStore";
13import { accountsList } from "@/variants";
14
15interface FollowersProps {
16 username: string;
17 address: string;
18}
19
20const Followers = ({ username, address }: FollowersProps) => {
21 const { currentAccount } = useAccountStore();
22
23 const request: FollowersRequest = {
24 account: address,
25 pageSize: PageSize.Fifty
26 };
27
28 const { data, error, fetchMore, loading } = useFollowersQuery({
29 skip: !address,
30 variables: { request }
31 });
32
33 const followers = data?.followers?.items;
34 const pageInfo = data?.followers?.pageInfo;
35 const hasMore = pageInfo?.next;
36
37 const handleEndReached = useCallback(async () => {
38 if (hasMore) {
39 await fetchMore({
40 variables: { request: { ...request, cursor: pageInfo?.next } }
41 });
42 }
43 }, [fetchMore, hasMore, pageInfo?.next, request]);
44
45 const loadMoreRef = useLoadMoreOnIntersect(handleEndReached);
46
47 if (loading) {
48 return <AccountListShimmer />;
49 }
50
51 if (!followers?.length) {
52 return (
53 <EmptyState
54 hideCard
55 icon={<UsersIcon className="size-8" />}
56 message={
57 <div>
58 <span className="mr-1 font-bold">@{username}</span>
59 <span>doesn't have any followers yet.</span>
60 </div>
61 }
62 />
63 );
64 }
65
66 if (error) {
67 return (
68 <ErrorMessage
69 className="m-5"
70 error={error}
71 title="Failed to load followers"
72 />
73 );
74 }
75
76 return (
77 <div className="!h-[80vh] overflow-y-auto">
78 <Virtualizer>
79 {followers.map((follower, index) => (
80 <motion.div
81 animate="visible"
82 className={cn(
83 "divider p-5",
84 index === followers.length - 1 && "border-b-0"
85 )}
86 initial="hidden"
87 key={follower.follower.address}
88 variants={accountsList}
89 >
90 <SingleAccount
91 account={follower.follower}
92 hideFollowButton={
93 currentAccount?.address === follower.follower.address
94 }
95 hideUnfollowButton={
96 currentAccount?.address === follower.follower.address
97 }
98 showBio
99 showUserPreview={false}
100 />
101 </motion.div>
102 ))}
103 {hasMore && <span ref={loadMoreRef} />}
104 </Virtualizer>
105 </div>
106 );
107};
108
109export default Followers;