Browse Source

change emoji reactions button on discussions to just up/down emoji buttons

imwald
Silberengel 4 months ago
parent
commit
a45709f963
  1. 5
      src/components/CitationCard/index.tsx
  2. 4
      src/components/EmbeddedCitation/index.tsx
  3. 73
      src/components/NoteStats/LikeButton.tsx

5
src/components/CitationCard/index.tsx

@ -2,10 +2,13 @@ import { ExtendedKind } from '@/constants'
import { Event } from 'nostr-tools' import { Event } from 'nostr-tools'
import { useMemo } from 'react' import { useMemo } from 'react'
import { useTranslation } from 'react-i18next' import { useTranslation } from 'react-i18next'
import { getTagValue } from '@/lib/tag'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
import { ExternalLink, Book, FileText, Bot } from 'lucide-react' import { ExternalLink, Book, FileText, Bot } from 'lucide-react'
function getTagValue(event: Event, tagName: string): string {
return event.tags.find(tag => tag[0] === tagName)?.[1] || ''
}
interface CitationCardProps { interface CitationCardProps {
event: Event event: Event
className?: string className?: string

4
src/components/EmbeddedCitation/index.tsx

@ -29,9 +29,9 @@ export default function EmbeddedCitation({ citationId, displayType = 'end', clas
eventId = citationId eventId = citationId
} }
const { event, isLoading } = useFetchEvent(eventId || '') const { event, isFetching } = useFetchEvent(eventId || '')
if (isLoading) { if (isFetching) {
return ( return (
<div className={className}> <div className={className}>
<Skeleton className="h-24 w-full" /> <Skeleton className="h-24 w-full" />

73
src/components/NoteStats/LikeButton.tsx

@ -23,9 +23,10 @@ import { useTranslation } from 'react-i18next'
import Emoji from '../Emoji' import Emoji from '../Emoji'
import EmojiPicker from '../EmojiPicker' import EmojiPicker from '../EmojiPicker'
import SuggestedEmojis from '../SuggestedEmojis' import SuggestedEmojis from '../SuggestedEmojis'
import DiscussionEmojis from '../SuggestedEmojis/DiscussionEmojis'
import { formatCount } from './utils' import { formatCount } from './utils'
const DISCUSSION_EMOJIS = ['⬆', '⬇']
export default function LikeButton({ event, hideCount = false }: { event: Event; hideCount?: boolean }) { export default function LikeButton({ event, hideCount = false }: { event: Event; hideCount?: boolean }) {
const { t } = useTranslation() const { t } = useTranslation()
const { isSmallScreen } = useScreenSize() const { isSmallScreen } = useScreenSize()
@ -57,17 +58,22 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
}) })
} }
}, [event.id, isDiscussion]) }, [event.id, isDiscussion])
const { myLastEmoji, likeCount, hasVoted } = useMemo(() => { const { myLastEmoji, likeCount, upVoteCount, downVoteCount } = useMemo(() => {
const stats = noteStats || {} const stats = noteStats || {}
const myLike = stats.likes?.find((like) => like.pubkey === pubkey) const myLike = stats.likes?.find((like) => like.pubkey === pubkey)
const likes = hideUntrustedInteractions const likes = hideUntrustedInteractions
? stats.likes?.filter((like) => isUserTrusted(like.pubkey)) ? stats.likes?.filter((like) => isUserTrusted(like.pubkey))
: stats.likes : stats.likes
// For discussion events and replies to discussions, check if user has voted (either up or down) // Calculate separate up/down vote counts for discussions
const hasVoted = (isDiscussion || isReplyToDiscussion) && myLike && (myLike.emoji === '⬆' || myLike.emoji === '⬇') let upVoteCount = 0
let downVoteCount = 0
if (isDiscussion || isReplyToDiscussion) {
upVoteCount = likes?.filter(like => like.emoji === '⬆').length || 0
downVoteCount = likes?.filter(like => like.emoji === '⬇').length || 0
}
return { myLastEmoji: myLike?.emoji, likeCount: likes?.length, hasVoted } return { myLastEmoji: myLike?.emoji, likeCount: likes?.length, upVoteCount, downVoteCount }
}, [noteStats, pubkey, hideUntrustedInteractions, isDiscussion, isReplyToDiscussion]) }, [noteStats, pubkey, hideUntrustedInteractions, isDiscussion, isReplyToDiscussion])
const like = async (emoji: string | TEmoji) => { const like = async (emoji: string | TEmoji) => {
@ -136,7 +142,7 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
<button <button
className="flex items-center enabled:hover:text-primary gap-1 px-3 h-full text-muted-foreground" className="flex items-center enabled:hover:text-primary gap-1 px-3 h-full text-muted-foreground"
title={t('Like')} title={t('Like')}
disabled={liking || ((isDiscussion || isReplyToDiscussion) && hasVoted)} disabled={liking}
onClick={() => { onClick={() => {
// If user has already reacted, clicking the button again should toggle it off // If user has already reacted, clicking the button again should toggle it off
if (myLastEmoji && !isEmojiReactionsOpen) { if (myLastEmoji && !isEmojiReactionsOpen) {
@ -164,6 +170,42 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
</button> </button>
) )
// For discussions, show the two arrow emojis directly as buttons
if (isDiscussion || isReplyToDiscussion) {
return (
<div className="flex items-center gap-1">
{DISCUSSION_EMOJIS.map((emoji, index) => {
const isSelected = myLastEmoji === emoji
const count = index === 0 ? upVoteCount : downVoteCount
return (
<button
key={index}
className={`flex items-center enabled:hover:text-primary gap-1 px-2 h-full text-muted-foreground rounded ${
isSelected ? 'text-primary bg-muted' : ''
}`}
title={emoji === '⬆' ? t('Upvote') : t('Downvote')}
disabled={liking}
onClick={() => {
like(emoji)
}}
>
{liking && index === 0 ? (
<Loader className="animate-spin" />
) : (
<>
<span className="text-base">{emoji}</span>
{!hideCount && !!count && (
<div className="text-sm">{formatCount(count)}</div>
)}
</>
)}
</button>
)
})}
</div>
)
}
if (isSmallScreen) { if (isSmallScreen) {
return ( return (
<> <>
@ -174,14 +216,6 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
<DrawerHeader className="sr-only"> <DrawerHeader className="sr-only">
<DrawerTitle>React</DrawerTitle> <DrawerTitle>React</DrawerTitle>
</DrawerHeader> </DrawerHeader>
{(isDiscussion || isReplyToDiscussion) ? (
<DiscussionEmojis
onEmojiClick={(emoji) => {
setIsEmojiReactionsOpen(false)
like(emoji)
}}
/>
) : (
<EmojiPicker <EmojiPicker
onEmojiClick={(emoji) => { onEmojiClick={(emoji) => {
setIsEmojiReactionsOpen(false) setIsEmojiReactionsOpen(false)
@ -190,7 +224,6 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
like(emoji) like(emoji)
}} }}
/> />
)}
</DrawerContent> </DrawerContent>
</Drawer> </Drawer>
</> </>
@ -201,7 +234,6 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
<DropdownMenu <DropdownMenu
open={isEmojiReactionsOpen} open={isEmojiReactionsOpen}
onOpenChange={(open) => { onOpenChange={(open) => {
if ((isDiscussion || isReplyToDiscussion) && hasVoted) return // Don't open if user has already voted
setIsEmojiReactionsOpen(open) setIsEmojiReactionsOpen(open)
if (open) { if (open) {
setIsPickerOpen(false) setIsPickerOpen(false)
@ -209,7 +241,7 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
}} }}
> >
<DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger> <DropdownMenuTrigger asChild>{trigger}</DropdownMenuTrigger>
<DropdownMenuContent side="top" className={(isDiscussion || isReplyToDiscussion) ? "p-0 w-fit min-w-0 max-w-fit" : "p-0 w-fit"} style={(isDiscussion || isReplyToDiscussion) ? { width: '60px', maxWidth: '60px', minWidth: '60px' } : undefined}> <DropdownMenuContent side="top" className="p-0 w-fit">
{isPickerOpen ? ( {isPickerOpen ? (
<EmojiPicker <EmojiPicker
onEmojiClick={(emoji, e) => { onEmojiClick={(emoji, e) => {
@ -217,13 +249,6 @@ export default function LikeButton({ event, hideCount = false }: { event: Event;
setIsEmojiReactionsOpen(false) setIsEmojiReactionsOpen(false)
if (!emoji) return if (!emoji) return
like(emoji)
}}
/>
) : (isDiscussion || isReplyToDiscussion) ? (
<DiscussionEmojis
onEmojiClick={(emoji) => {
setIsEmojiReactionsOpen(false)
like(emoji) like(emoji)
}} }}
/> />

Loading…
Cancel
Save