Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import { ChatBubbleBottomCenterIcon } from "@heroicons/react/24/outline";
2import { AccountFeedType } from "@hey/data/enums";
3import {
4 type AnyPostFragment,
5 MainContentFocus,
6 PageSize,
7 type PostsRequest,
8 PostType,
9 usePostsQuery
10} from "@hey/indexer";
11import { useCallback, useMemo } from "react";
12import SinglePost from "@/components/Post/SinglePost";
13import PostFeed from "@/components/Shared/Post/PostFeed";
14
15interface AccountFeedProps {
16 username: string;
17 address: string;
18 type:
19 | AccountFeedType.Collects
20 | AccountFeedType.Feed
21 | AccountFeedType.Media
22 | AccountFeedType.Replies;
23}
24
25const EMPTY_MESSAGES: Record<AccountFeedType, string> = {
26 [AccountFeedType.Feed]: "has nothing in their feed yet!",
27 [AccountFeedType.Media]: "has no media yet!",
28 [AccountFeedType.Replies]: "hasn't replied yet!",
29 [AccountFeedType.Collects]: "hasn't collected anything yet!"
30};
31
32const AccountFeed = ({ username, address, type }: AccountFeedProps) => {
33 const postTypes = useMemo(() => {
34 switch (type) {
35 case AccountFeedType.Feed:
36 return [PostType.Root, PostType.Repost, PostType.Quote];
37 case AccountFeedType.Replies:
38 return [PostType.Comment];
39 case AccountFeedType.Media:
40 return [PostType.Root, PostType.Quote];
41 default:
42 return [
43 PostType.Root,
44 PostType.Comment,
45 PostType.Repost,
46 PostType.Quote
47 ];
48 }
49 }, [type]);
50
51 const getEmptyMessage = () => {
52 return EMPTY_MESSAGES[type] || "";
53 };
54
55 const request = useMemo<PostsRequest>(
56 () => ({
57 filter: {
58 postTypes,
59 ...(type === AccountFeedType.Media && {
60 metadata: {
61 mainContentFocus: [
62 MainContentFocus.Image,
63 MainContentFocus.Audio,
64 MainContentFocus.Video,
65 MainContentFocus.ShortVideo
66 ]
67 }
68 }),
69 ...(type === AccountFeedType.Collects
70 ? { collectedBy: { account: address } }
71 : { authors: [address] })
72 },
73 pageSize: PageSize.Fifty
74 }),
75 [address, postTypes, type]
76 );
77
78 const { data, error, fetchMore, loading } = usePostsQuery({
79 skip: !address,
80 variables: { request }
81 });
82
83 const posts = data?.posts?.items;
84 const pageInfo = data?.posts?.pageInfo;
85 const hasMore = pageInfo?.next;
86
87 const safePosts = (posts ?? []) as AnyPostFragment[];
88
89 const handleEndReached = useCallback(async () => {
90 if (hasMore) {
91 await fetchMore({
92 variables: { request: { ...request, cursor: pageInfo?.next } }
93 });
94 }
95 }, [fetchMore, hasMore, pageInfo?.next, request]);
96
97 return (
98 <PostFeed
99 emptyIcon={<ChatBubbleBottomCenterIcon className="size-8" />}
100 emptyMessage={
101 <div>
102 <b className="mr-1">{username}</b>
103 <span>{getEmptyMessage()}</span>
104 </div>
105 }
106 error={error}
107 errorTitle="Failed to load account feed"
108 handleEndReached={handleEndReached}
109 hasMore={hasMore}
110 items={safePosts}
111 loading={loading}
112 renderItem={(post) => <SinglePost key={post.id} post={post} />}
113 />
114 );
115};
116
117export default AccountFeed;