Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
at main 157 lines 4.9 kB view raw
1import { BellIcon } from "@heroicons/react/24/outline"; 2import { NotificationFeedType } from "@hey/data/enums"; 3import { 4 type NotificationRequest, 5 NotificationType, 6 useNotificationsQuery 7} from "@hey/indexer"; 8import { memo, useCallback, useEffect } from "react"; 9import { WindowVirtualizer } from "virtua"; 10import AccountActionExecutedNotification from "@/components/Notification/Type/AccountActionExecutedNotification"; 11import CommentNotification from "@/components/Notification/Type/CommentNotification"; 12import FollowNotification from "@/components/Notification/Type/FollowNotification"; 13import MentionNotification from "@/components/Notification/Type/MentionNotification"; 14import PostActionExecutedNotification from "@/components/Notification/Type/PostActionExecutedNotification"; 15import QuoteNotification from "@/components/Notification/Type/QuoteNotification"; 16import ReactionNotification from "@/components/Notification/Type/ReactionNotification"; 17import RepostNotification from "@/components/Notification/Type/RepostNotification"; 18import { Card, EmptyState, ErrorMessage } from "@/components/Shared/UI"; 19import cn from "@/helpers/cn"; 20import useLoadMoreOnIntersect from "@/hooks/useLoadMoreOnIntersect"; 21import { useNotificationStore } from "@/store/persisted/useNotificationStore"; 22import NotificationShimmer from "./Shimmer"; 23import TokenDistributedNotification from "./Type/TokenDistributedNotification"; 24 25const notificationComponentMap = { 26 AccountActionExecutedNotification, 27 CommentNotification, 28 FollowNotification, 29 MentionNotification, 30 PostActionExecutedNotification, 31 QuoteNotification, 32 ReactionNotification, 33 RepostNotification, 34 TokenDistributedNotification 35}; 36 37interface ListProps { 38 feedType: string; 39} 40 41const List = ({ feedType }: ListProps) => { 42 const { setLastSeenNotificationId } = useNotificationStore(); 43 44 const getNotificationType = useCallback(() => { 45 switch (feedType) { 46 case NotificationFeedType.All: 47 return; 48 case NotificationFeedType.Mentions: 49 return [NotificationType.Mentioned]; 50 case NotificationFeedType.Comments: 51 return [NotificationType.Commented]; 52 case NotificationFeedType.Likes: 53 return [NotificationType.Reacted]; 54 case NotificationFeedType.PostActions: 55 return [NotificationType.ExecutedPostAction]; 56 case NotificationFeedType.Rewards: 57 return [NotificationType.TokenDistributed]; 58 default: 59 return; 60 } 61 }, [feedType]); 62 63 const request: NotificationRequest = { 64 filter: { 65 includeLowScore: false, 66 notificationTypes: getNotificationType() 67 } 68 }; 69 70 const { data, error, fetchMore, loading } = useNotificationsQuery({ 71 variables: { request } 72 }); 73 74 const notifications = data?.notifications?.items; 75 const pageInfo = data?.notifications?.pageInfo; 76 const hasMore = !!pageInfo?.next; 77 78 useEffect(() => { 79 const firstNotification = notifications?.[0]; 80 if ( 81 !firstNotification || 82 typeof firstNotification !== "object" || 83 !("id" in firstNotification) 84 ) { 85 return; 86 } 87 const firstId = firstNotification.id; 88 if (firstId) { 89 setLastSeenNotificationId(firstId); 90 } 91 }, [notifications, setLastSeenNotificationId]); 92 93 const handleEndReached = useCallback(async () => { 94 if (hasMore) { 95 await fetchMore({ 96 variables: { request: { ...request, cursor: pageInfo?.next } } 97 }); 98 } 99 }, [fetchMore, hasMore, pageInfo?.next, request]); 100 101 const loadMoreRef = useLoadMoreOnIntersect(handleEndReached); 102 103 if (loading) { 104 return ( 105 <Card className="divide-y divide-gray-200 dark:divide-gray-700"> 106 <NotificationShimmer /> 107 <NotificationShimmer /> 108 <NotificationShimmer /> 109 <NotificationShimmer /> 110 </Card> 111 ); 112 } 113 114 if (error) { 115 return <ErrorMessage error={error} title="Failed to load notifications" />; 116 } 117 118 if (!notifications?.length) { 119 return ( 120 <EmptyState 121 icon={<BellIcon className="size-8" />} 122 message="Inbox zero!" 123 /> 124 ); 125 } 126 127 return ( 128 <Card className="virtual-divider-list-window"> 129 <WindowVirtualizer> 130 {notifications.map((notification) => { 131 if (!("id" in notification)) { 132 return null; 133 } 134 135 const Component = 136 notificationComponentMap[ 137 notification.__typename as keyof typeof notificationComponentMap 138 ]; 139 140 return ( 141 <div 142 className={cn({ 143 "p-5": notification.__typename !== "FollowNotification" 144 })} 145 key={notification.id} 146 > 147 {Component && <Component notification={notification as never} />} 148 </div> 149 ); 150 })} 151 {hasMore && <span ref={loadMoreRef} />} 152 </WindowVirtualizer> 153 </Card> 154 ); 155}; 156 157export default memo(List);