Hey is a decentralized and permissionless social media app built with Lens Protocol 馃尶
1import type { PostFragment } from "@hey/indexer";
2import { AnimateNumber } from "motion-plus-react";
3import plur from "plur";
4import { memo, useCallback, useState } from "react";
5import { Link } from "react-router";
6import Likes from "@/components/Shared/Modal/Likes";
7import PostExecutors from "@/components/Shared/Modal/PostExecutors";
8import Reposts from "@/components/Shared/Modal/Reposts";
9import { Modal } from "@/components/Shared/UI";
10
11const AnimatedNumber = ({
12 key,
13 name,
14 value
15}: {
16 key: string;
17 name: string;
18 value: number;
19}) => {
20 return (
21 <span className="flex items-center gap-x-1">
22 <AnimateNumber
23 className="font-bold text-black dark:text-white"
24 format={{ notation: "compact" }}
25 key={key}
26 transition={{ type: "tween" }}
27 >
28 {value}
29 </AnimateNumber>
30 {plur(name, value)}
31 </span>
32 );
33};
34
35type PostExecutorsType = "Tippers" | "Collectors";
36
37interface PostStatsProps {
38 post: PostFragment;
39}
40
41const PostStats = ({ post }: PostStatsProps) => {
42 const [showLikesModal, setShowLikesModal] = useState(false);
43 const [showRepostsModal, setShowRepostsModal] = useState(false);
44 const [showPostExecutorsModal, setShowPostExecutorsModal] =
45 useState<PostExecutorsType | null>(null);
46
47 const handleOpenRepostsModal = useCallback(() => {
48 setShowRepostsModal(true);
49 }, []);
50
51 const handleOpenLikesModal = useCallback(() => {
52 setShowLikesModal(true);
53 }, []);
54
55 const handleOpenTippersModal = useCallback(() => {
56 setShowPostExecutorsModal("Tippers");
57 }, []);
58
59 const handleOpenCollectorsModal = useCallback(() => {
60 setShowPostExecutorsModal("Collectors");
61 }, []);
62
63 const handleCloseLikesModal = useCallback(() => {
64 setShowLikesModal(false);
65 }, []);
66
67 const handleCloseRepostsModal = useCallback(() => {
68 setShowRepostsModal(false);
69 }, []);
70
71 const handleClosePostExecutorsModal = useCallback(() => {
72 setShowPostExecutorsModal(null);
73 }, []);
74
75 const { bookmarks, comments, reposts, quotes, reactions, collects, tips } =
76 post.stats;
77
78 const showStats =
79 comments > 0 ||
80 reactions > 0 ||
81 reposts > 0 ||
82 quotes > 0 ||
83 bookmarks > 0 ||
84 collects > 0 ||
85 tips > 0;
86
87 if (!showStats) {
88 return null;
89 }
90
91 return (
92 <>
93 <div className="divider" />
94 <div className="flex flex-wrap items-center gap-x-6 gap-y-3 py-3 text-gray-500 text-sm dark:text-gray-200">
95 {comments > 0 ? (
96 <AnimatedNumber
97 key={`comment-count-${post.id}`}
98 name="Comment"
99 value={comments}
100 />
101 ) : null}
102 {reposts > 0 ? (
103 <button
104 className="outline-offset-2"
105 onClick={handleOpenRepostsModal}
106 type="button"
107 >
108 <AnimatedNumber
109 key={`repost-count-${post.id}`}
110 name="Repost"
111 value={reposts}
112 />
113 </button>
114 ) : null}
115 {quotes > 0 ? (
116 <Link className="outline-offset-2" to={`/posts/${post.slug}/quotes`}>
117 <AnimatedNumber
118 key={`quote-count-${post.id}`}
119 name="Quote"
120 value={quotes}
121 />
122 </Link>
123 ) : null}
124 {reactions > 0 ? (
125 <button
126 className="outline-offset-2"
127 onClick={handleOpenLikesModal}
128 type="button"
129 >
130 <AnimatedNumber
131 key={`like-count-${post.id}`}
132 name="Like"
133 value={reactions}
134 />
135 </button>
136 ) : null}
137 {tips > 0 ? (
138 <button
139 className="outline-offset-2"
140 onClick={handleOpenTippersModal}
141 type="button"
142 >
143 <AnimatedNumber
144 key={`tip-count-${post.id}`}
145 name="Tip"
146 value={tips}
147 />
148 </button>
149 ) : null}
150 {collects > 0 ? (
151 <button
152 className="outline-offset-2"
153 onClick={handleOpenCollectorsModal}
154 type="button"
155 >
156 <AnimatedNumber
157 key={`collect-count-${post.id}`}
158 name="Collect"
159 value={collects}
160 />
161 </button>
162 ) : null}
163 {bookmarks > 0 ? (
164 <AnimatedNumber
165 key={`bookmark-count-${post.id}`}
166 name="Bookmark"
167 value={bookmarks}
168 />
169 ) : null}
170 </div>
171 <Modal
172 onClose={handleCloseLikesModal}
173 show={showLikesModal}
174 title="Likes"
175 >
176 <Likes postId={post.id} />
177 </Modal>
178 <Modal
179 onClose={handleCloseRepostsModal}
180 show={showRepostsModal}
181 title="Reposts"
182 >
183 <Reposts postId={post.id} />
184 </Modal>
185 <Modal
186 onClose={handleClosePostExecutorsModal}
187 show={showPostExecutorsModal !== null}
188 title={showPostExecutorsModal === "Tippers" ? "Tippers" : "Collectors"}
189 >
190 <PostExecutors
191 filter={
192 showPostExecutorsModal === "Tippers"
193 ? { tipping: true }
194 : { simpleCollect: true }
195 }
196 postId={post.id}
197 />
198 </Modal>
199 </>
200 );
201};
202
203export default memo(PostStats);