From a66c1074a49b99b8ef8a9106c0025dc4c8ddfc3c Mon Sep 17 00:00:00 2001 From: Silberengel Date: Sat, 25 Oct 2025 09:06:44 +0200 Subject: [PATCH] toggle off emojis --- src/components/NoteStats/LikeButton.tsx | 49 ++++++++++++++++++++++--- src/services/note-stats.service.ts | 14 +++++++ 2 files changed, 57 insertions(+), 6 deletions(-) diff --git a/src/components/NoteStats/LikeButton.tsx b/src/components/NoteStats/LikeButton.tsx index faf5d98..70edb85 100644 --- a/src/components/NoteStats/LikeButton.tsx +++ b/src/components/NoteStats/LikeButton.tsx @@ -6,7 +6,7 @@ import { } from '@/components/ui/dropdown-menu' import { ExtendedKind } from '@/constants' import { useNoteStatsById } from '@/hooks/useNoteStatsById' -import { createReactionDraftEvent } from '@/lib/draft-event' +import { createDeletionRequestDraftEvent, createReactionDraftEvent } from '@/lib/draft-event' import { getRootEventHexId } from '@/lib/event' import { useNostr } from '@/providers/NostrProvider' import { useScreenSize } from '@/providers/ScreenSizeProvider' @@ -79,9 +79,41 @@ export default function LikeButton({ event }: { event: Event }) { await noteStatsService.fetchNoteStats(event, pubkey) } - const reaction = createReactionDraftEvent(event, emoji) - const evt = await publish(reaction) - noteStatsService.updateNoteStatsByEvents([evt]) + // Check if user is clicking the same emoji they already reacted with + const emojiString = typeof emoji === 'string' ? emoji : emoji.shortcode + + // Normalize myLastEmoji for comparison + const myLastEmojiString = typeof myLastEmoji === 'string' ? myLastEmoji : typeof myLastEmoji === 'object' ? myLastEmoji.shortcode : undefined + const isTogglingOff = myLastEmojiString === emojiString + + console.log('Toggle check:', { myLastEmoji, myLastEmojiString, emojiString, isTogglingOff, myLikes: noteStats?.likes?.filter(l => l.pubkey === pubkey) }) + + if (isTogglingOff) { + // User wants to toggle off - find their previous reaction and delete it + const myReaction = noteStats?.likes?.find((like) => { + if (like.pubkey !== pubkey) return false + const likeEmojiString = typeof like.emoji === 'string' ? like.emoji : like.emoji.shortcode + return likeEmojiString === emojiString + }) + + if (myReaction) { + // Optimistically update the UI immediately + noteStatsService.removeLike(event.id, myReaction.id) + + // Fetch the actual reaction event + const reactionEvent = await client.fetchEvent(myReaction.id) + if (reactionEvent) { + // Create and publish a deletion request (kind 5) + const deletionRequest = createDeletionRequestDraftEvent(reactionEvent) + await publish(deletionRequest) + } + } + } else { + // User is adding a new reaction + const reaction = createReactionDraftEvent(event, emoji) + const evt = await publish(reaction) + noteStatsService.updateNoteStatsByEvents([evt]) + } } catch (error) { console.error('like failed', error) } finally { @@ -97,9 +129,14 @@ export default function LikeButton({ event }: { event: Event }) { title={t('Like')} disabled={liking || ((isDiscussion || isReplyToDiscussion) && hasVoted)} onClick={() => { - if (isSmallScreen && !((isDiscussion || isReplyToDiscussion) && hasVoted)) { - setIsEmojiReactionsOpen(true) + // If user has already reacted, clicking the button again should toggle it off + if (myLastEmoji && !isEmojiReactionsOpen) { + like(myLastEmoji) + return } + + // Otherwise, open the emoji picker + setIsEmojiReactionsOpen(true) }} > {liking ? ( diff --git a/src/services/note-stats.service.ts b/src/services/note-stats.service.ts index 307f2ea..4a6198b 100644 --- a/src/services/note-stats.service.ts +++ b/src/services/note-stats.service.ts @@ -250,6 +250,20 @@ class NoteStatsService { return targetEventId } + removeLike(eventId: string, reactionEventId: string) { + const old = this.noteStatsMap.get(eventId) || {} + const likeIdSet = old.likeIdSet || new Set() + const likes = old.likes || [] + + if (!likeIdSet.has(reactionEventId)) return eventId + + likeIdSet.delete(reactionEventId) + const newLikes = likes.filter(like => like.id !== reactionEventId) + this.noteStatsMap.set(eventId, { ...old, likeIdSet, likes: newLikes }) + this.notifyNoteStats(eventId) + return eventId + } + private addRepostByEvent(evt: Event) { const eventId = evt.tags.find(tagNameEquals('e'))?.[1] if (!eventId) return