diff options
Diffstat (limited to 'front/src/components/post/Footer.tsx')
-rw-r--r-- | front/src/components/post/Footer.tsx | 238 |
1 files changed, 238 insertions, 0 deletions
diff --git a/front/src/components/post/Footer.tsx b/front/src/components/post/Footer.tsx new file mode 100644 index 0000000..3b48241 --- /dev/null +++ b/front/src/components/post/Footer.tsx @@ -0,0 +1,238 @@ +import type { PostProps } from "./Post"; +import reply from "@/assets/icons/reply.svg"; +import quote from "@/assets/icons/quote.svg"; +import repost from "@/assets/icons/rt.svg"; +import { useState } from "react"; +import useLocalState from "@/state/state"; +import { useLocation } from "wouter"; +import { displayCount } from "@/logic/utils"; +import { TrillReactModal, stringToReact } from "./Reactions"; +import toast from "react-hot-toast"; +import NostrIcon from "./wrappers/NostrIcon"; +// TODO abstract this somehow + +function Footer({ poast, refetch }: PostProps) { + const [_showMenu, setShowMenu] = useState(false); + const [location, navigate] = useLocation(); + const [reposting, _setReposting] = useState(false); + const { api, setComposerData, setModal } = useLocalState(); + const our = api!.airlock.our!; + function doReply(e: React.MouseEvent) { + e.stopPropagation(); + setComposerData({ type: "reply", post: { trill: poast } }); + } + function doQuote(e: React.MouseEvent) { + e.stopPropagation(); + setComposerData({ + type: "quote", + post: { trill: poast }, + }); + navigate("/composer"); + } + const childrenCount = poast.children + ? poast.children.length + ? poast.children.length + : Object.keys(poast.children).length + : 0; + const myRP = poast.engagement.shared.find((r) => r.pid.ship === our); + async function cancelRP(e: React.MouseEvent) { + e.stopPropagation(); + const r = await api!.deletePost(our); + if (r) toast.success("Repost deleted"); + refetch(); + if (location.includes(poast.id)) navigate("/"); + } + async function sendRP(e: React.MouseEvent) { + // TODO update backend because contents are only markdown now + e.stopPropagation(); + // const c = [ + // { + // ref: { + // type: "trill", + // ship: poast.host, + // path: `/${poast.id}`, + // }, + // }, + // ]; + // const post: SentPoast = { + // host: our, + // author: our, + // thread: null, + // parent: null, + // contents: input, + // read: openLock, + // write: openLock, + // tags: [], // TODO + // }; + // const r = await api!.addPost(post, false); + // setReposting(true); + // if (r) { + // setReposting(false); + // toast.success("Your post was published"); + // } + } + function doReact(e: React.MouseEvent) { + e.stopPropagation(); + const modal = <TrillReactModal poast={poast} />; + setModal(modal); + } + function showReplyCount() { + if (poast.children[0]) fetchAndShow(); // Flatpoast + // else { + // const authors = Object.keys(poast.children).map( + // (i) => poast.children[i].post.author + // ); + // setEngagement({ type: "replies", ships: authors }, poast); + // } + } + async function fetchAndShow() { + // let authors = []; + // for (let i of poast.children as string[]) { + // const res = await scrypoastFull(poast.host, i); + // if (res) + // authors.push(res.post.author || "deleter"); + // } + // setEngagement({ type: "replies", ships: authors }, poast); + } + function showRepostCount() { + // const ships = poast.engagement.shared.map((entry) => entry.host); + // setEngagement({ type: "reposts", ships: ships }, poast); + } + function showQuoteCount() { + // setEngagement({ type: "quotes", quotes: poast.engagement.quoted }, poast); + } + function showReactCount() { + // setEngagement({ type: "reacts", reacts: poast.engagement.reacts }, poast); + } + + const mostCommonReact = Object.values(poast.engagement.reacts).reduce( + (acc: any, item) => { + if (!acc.counts[item]) acc.counts[item] = 0; + acc.counts[item] += 1; + if (!acc.winner || acc.counts[item] > acc.counts[acc.winner]) + acc.winner = item; + return acc; + }, + { counts: {}, winner: "" }, + ).winner; + const reactIcon = stringToReact(mostCommonReact); + + // TODO round up all helpers + + return ( + <div className="footer-wrapper post-footer"> + <footer> + <div className="icon"> + <span role="link" onMouseUp={showReplyCount} className="reply-count"> + {displayCount(childrenCount)} + </span> + <img role="link" onMouseUp={doReply} src={reply} alt="" /> + </div> + <div className="icon"> + <span role="link" onMouseUp={showQuoteCount} className="quote-count"> + {displayCount(poast.engagement.quoted.length)} + </span> + <img role="link" onMouseUp={doQuote} src={quote} alt="" /> + </div> + <div className="icon"> + <span + role="link" + onMouseUp={showRepostCount} + className="repost-count" + > + {displayCount(poast.engagement.shared.length)} + </span> + {reposting ? ( + <p>...</p> + ) : myRP ? ( + <img + role="link" + className="my-rp" + onMouseUp={cancelRP} + src={repost} + title="cancel repost" + /> + ) : ( + <img role="link" onMouseUp={sendRP} src={repost} title="repost" /> + )} + </div> + <div className="icon" role="link" onMouseUp={doReact}> + <span + role="link" + onMouseUp={showReactCount} + className="reaction-count" + > + {displayCount(Object.keys(poast.engagement.reacts).length)} + </span> + {reactIcon} + </div> + <NostrIcon poast={poast} /> + </footer> + </div> + ); +} +export default Footer; + +// function Menu({ +// poast, +// setShowMenu, +// refetch, +// }: { +// poast: Poast; +// setShowMenu: Function; +// refetch: Function; +// }) { +// const ref = useRef<HTMLDivElement>(null); +// const [location, navigate] = useLocation(); +// // TODO this is a mess and the event still propagates +// useEffect(() => { +// const checkIfClickedOutside = (e: any) => { +// e.stopPropagation(); +// if (ref && ref.current && !ref.current.contains(e.target)) +// setShowMenu(false); +// }; +// document.addEventListener("mousedown", checkIfClickedOutside); +// return () => { +// document.removeEventListener("mousedown", checkIfClickedOutside); +// }; +// }, []); +// const { our, setModal, setAlert } = useLocalState(); +// const mine = our === poast.host || our === poast.author; +// async function doDelete(e: React.MouseEvent) { +// e.stopPropagation(); +// deletePost(poast.host, poast.id); +// setAlert("Post deleted"); +// setShowMenu(false); +// refetch(); +// if (location.includes(poast.id)) navigate("/"); +// } +// async function copyLink(e: React.MouseEvent) { +// e.stopPropagation(); +// const link = trillPermalink(poast); +// await navigator.clipboard.writeText(link); +// // some alert +// setShowMenu(false); +// } +// function openStats(e: React.MouseEvent) { +// e.stopPropagation(); +// e.preventDefault(); +// const m = <StatsModal poast={poast} close={() => setModal(null)} />; +// setModal(m); +// } +// return ( +// <div ref={ref} id="post-menu"> +// {/* <p onClick={openShare}>Share to Groups</p> */} +// <p role="link" onMouseUp={openStats}> +// See Stats +// </p> +// <p role="link" onMouseUp={copyLink}> +// Permalink +// </p> +// {mine && ( +// <p role="link" onMouseUp={doDelete}> +// Delete Post +// </p> +// )} +// </div> +// ); +// } |