Feature: Reaction (Like) (#33)

* add sign and push event on reaction button click (not tested yet and WIP)

* Refactor ReactionButton component to improve state management and user feedback on reactions
This commit is contained in:
mroxso
2025-02-02 00:07:31 +01:00
committed by GitHub
parent b422e1534e
commit 9f4e0cb780

View File

@@ -1,103 +1,135 @@
import { useNostr, dateToUnix, useNostrEvents } from "nostr-react";
import { useNostr, useNostrEvents } from "nostr-react"
import type { Event as NostrEvent } from "nostr-tools"
import { Button } from "@/components/ui/button"
import {
type Event as NostrEvent,
getEventHash,
getPublicKey,
finalizeEvent,
} from "nostr-tools";
import { Button } from "@/components/ui/button";
import {
Drawer,
DrawerClose,
DrawerContent,
DrawerDescription,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
Drawer,
DrawerClose,
DrawerContent,
DrawerFooter,
DrawerHeader,
DrawerTitle,
DrawerTrigger,
} from "@/components/ui/drawer"
import { ReloadIcon } from "@radix-ui/react-icons";
import ReactionButtonReactionList from "./ReactionButtonReactionList";
import { ReloadIcon } from "@radix-ui/react-icons"
import ReactionButtonReactionList from "./ReactionButtonReactionList"
import { signEvent } from "@/utils/utils"
import { useState, useEffect, useMemo } from "react"
export default function ReactionButton({ event }: { event: any }) {
const { events, isLoading } = useNostrEvents({
filter: {
// since: dateToUnix(now.current), // all new events from now
// since: 0,
// limit: 100,
'#e': [event.id],
kinds: [7],
},
});
const { publish } = useNostr()
// filter out all events that also have another e tag with another id
// this will filter out likes that are made on comments and not on the note itself
const filteredEvents = events.filter((event) => { return event.tags.filter((tag) => { return tag[0] === '#e' && tag[1] !== event.id }).length === 0 });
const loginType = typeof window !== "undefined" ? window.localStorage.getItem("loginType") : null
const loggedInUserPublicKey = typeof window !== "undefined" ? window.localStorage.getItem("pubkey") : null
// const { publish } = useNostr();
const [liked, setLiked] = useState(false)
const [likeIcon, setLikeIcon] = useState("")
// const onPost = async () => {
// const privKey = prompt("Paste your private key:");
const { events, isLoading } = useNostrEvents({
filter: {
"#e": [event.id],
kinds: [7],
},
})
// if (!privKey) {
// alert("no private key provided");
// return;
// }
const filteredEvents = useMemo(() => {
return events.filter((e) => {
return e.tags.filter((tag) => tag[0] === "e" && tag[1] !== event.id).length === 0
})
}, [events, event.id])
// const message = prompt("Enter the message you want to send:");
const reactionCount = filteredEvents.length
// if (!message) {
// alert("no message provided");
// return;
// }
useEffect(() => {
const userReaction = filteredEvents.find((e) => e.pubkey === loggedInUserPublicKey)
if (userReaction) {
setLiked(true)
setLikeIcon(userReaction.content)
} else {
setLiked(false)
setLikeIcon("")
}
}, [filteredEvents, loggedInUserPublicKey])
// const event: NostrEvent = {
// content: message,
// kind: 1,
// tags: [],
// created_at: dateToUnix(),
// pubkey: getPublicKey(privKey),
// id: "",
// sig: ""
// };
const onPost = async (icon: string) => {
const message = icon || "+"
// event.id = getEventHash(event);
// event.sig = getSignature(event, privKey);
const likeEvent: NostrEvent = {
content: message,
kind: 7,
tags: [],
created_at: Math.floor(Date.now() / 1000),
pubkey: "",
id: "",
sig: "",
}
// publish(event);
// };
likeEvent.tags.push(["e", event.id])
likeEvent.tags.push(["p", event.pubkey])
likeEvent.tags.push(["k", event.kind.toString()])
return (
<Drawer>
<DrawerTrigger>
{/* <Button variant="default" onClick={onPost}>{events.length} Reactions</Button> */}
{isLoading ? (
<Button variant="default"><ReloadIcon className="mr-2 h-4 w-4 animate-spin" /> 💜</Button>
) : (
<Button variant="default">{filteredEvents.length} 💜</Button>
)}
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Reactions</DrawerTitle>
</DrawerHeader>
{/* TODO: Create Reaction Event on Click */}
<div className="px-4 grid grid-cols-3">
<Button variant={"outline"} className="mx-1" disabled>💜</Button>
<Button variant={"outline"} className="mx-1" disabled>👍</Button>
<Button variant={"outline"} className="mx-1" disabled>👎</Button>
</div>
<hr className="my-4" />
<ReactionButtonReactionList filteredEvents={filteredEvents} />
<DrawerFooter>
<DrawerClose>
<Button variant={"secondary"}>Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
const signedEvent = await signEvent(loginType, likeEvent)
// <Button variant="default" onClick={onPost}>{events.length} Reactions</Button>
);
if (signedEvent) {
publish(signedEvent)
setLiked(true)
setLikeIcon(message)
filteredEvents.push(signedEvent)
} else {
console.error("Failed to sign event")
alert("Failed to sign event")
}
}
return (
<Drawer>
<DrawerTrigger>
<Button variant={liked ? "default" : "outline"}>
{isLoading ? (
<>
<ReloadIcon className="mr-2 h-4 w-4 animate-spin" /> 💜
</>
) : (
<>
{reactionCount} {liked ? likeIcon : "💜"}
</>
)}
</Button>
</DrawerTrigger>
<DrawerContent>
<DrawerHeader>
<DrawerTitle>Reactions</DrawerTitle>
</DrawerHeader>
<div className="px-4 grid grid-cols-3">
<Button
variant={liked && likeIcon === "💜" ? "secondary" : "outline"}
className={`mx-1`}
onClick={() => onPost("💜")}
>
💜
</Button>
<Button
variant={liked && likeIcon === "👍" ? "secondary" : "outline"}
className={`mx-1`}
onClick={() => onPost("👍")}
>
👍
</Button>
<Button
variant={liked && likeIcon === "👎" ? "secondary" : "outline"}
className={`mx-1`}
onClick={() => onPost("👎")}
>
👎
</Button>
</div>
<hr className="my-4" />
<ReactionButtonReactionList filteredEvents={filteredEvents} />
<DrawerFooter>
<DrawerClose>
<Button variant={"secondary"}>Close</Button>
</DrawerClose>
</DrawerFooter>
</DrawerContent>
</Drawer>
)
}