mirror of
https://github.com/lumina-rocks/lumina.git
synced 2026-06-04 09:41:32 +02:00
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:
@@ -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>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user