[DMs] Reactions - link up API (attempt 2) (#8074)

* update package

* wire up APIs

* get reactions to display

* allow removing emoji

* handle limits better

* listen to reactions in log

* update convo list with reactions

* tweaks to reaction display

* Handle empty message fallback case

* update package

* shift reacts up by 2px

---------

Co-authored-by: Eric Bailey <git@esb.lol>
This commit is contained in:
Samuel Newman
2025-03-28 16:34:07 +02:00
committed by GitHub
parent 55a40c2436
commit 152bc3c1ec
17 changed files with 659 additions and 123 deletions
+50 -22
View File
@@ -1,24 +1,29 @@
import {useState} from 'react'
import {View} from 'react-native'
import {ChatBskyConvoDefs} from '@atproto/api'
import {type ChatBskyConvoDefs} from '@atproto/api'
import EmojiPicker from '@emoji-mart/react'
import {msg} from '@lingui/macro'
import {useLingui} from '@lingui/react'
import {Emoji} from '#/view/com/composer/text-input/web/EmojiPicker.web'
import {useSession} from '#/state/session'
import {type Emoji} from '#/view/com/composer/text-input/web/EmojiPicker.web'
import {PressableWithHover} from '#/view/com/util/PressableWithHover'
import {atoms as a} from '#/alf'
import {useTheme} from '#/alf'
import {DotGrid_Stroke2_Corner0_Rounded as DotGridIcon} from '#/components/icons/DotGrid'
import * as Menu from '#/components/Menu'
import {TriggerProps} from '#/components/Menu/types'
import {type TriggerProps} from '#/components/Menu/types'
import {Text} from '#/components/Typography'
import {hasAlreadyReacted, hasReachedReactionLimit} from './util'
export function EmojiReactionPicker({
message,
children,
onEmojiSelect,
}: {
message: ChatBskyConvoDefs.MessageView
children?: TriggerProps['children']
onEmojiSelect: (emoji: string) => void
}) {
if (!children)
throw new Error('EmojiReactionPicker requires the children prop on web')
@@ -29,15 +34,22 @@ export function EmojiReactionPicker({
<Menu.Root>
<Menu.Trigger label={_(msg`Add emoji reaction`)}>{children}</Menu.Trigger>
<Menu.Outer>
<MenuInner />
<MenuInner message={message} onEmojiSelect={onEmojiSelect} />
</Menu.Outer>
</Menu.Root>
)
}
function MenuInner() {
function MenuInner({
message,
onEmojiSelect,
}: {
message: ChatBskyConvoDefs.MessageView
onEmojiSelect: (emoji: string) => void
}) {
const t = useTheme()
const {control} = Menu.useMenuContext()
const {currentAccount} = useSession()
const [expanded, setExpanded] = useState(false)
@@ -47,29 +59,45 @@ function MenuInner() {
const handleEmojiSelect = (emoji: string) => {
control.close()
window.alert(emoji)
onEmojiSelect(emoji)
}
const limitReacted = hasReachedReactionLimit(message, currentAccount?.did)
return expanded ? (
<EmojiPicker onEmojiSelect={handleEmojiPickerResponse} autoFocus={true} />
) : (
<View style={[a.flex_row, a.gap_xs]}>
{['👍', '😆', '❤️', '👀', '😢'].map(emoji => (
<PressableWithHover
key={emoji}
onPress={() => handleEmojiSelect(emoji)}
hoverStyle={{backgroundColor: t.palette.primary_100}}
style={[
a.rounded_xs,
{height: 40, width: 40},
a.justify_center,
a.align_center,
]}>
<Text style={[a.text_center, {fontSize: 30}]} emoji>
{emoji}
</Text>
</PressableWithHover>
))}
{['👍', '😆', '❤️', '👀', '😢'].map(emoji => {
const alreadyReacted = hasAlreadyReacted(
message,
currentAccount?.did,
emoji,
)
return (
<PressableWithHover
key={emoji}
onPress={() => handleEmojiSelect(emoji)}
hoverStyle={{
backgroundColor: alreadyReacted
? t.palette.negative_200
: !limitReacted
? t.palette.primary_300
: undefined,
}}
style={[
a.rounded_xs,
{height: 40, width: 40},
a.justify_center,
a.align_center,
alreadyReacted && {backgroundColor: t.palette.primary_100},
]}>
<Text style={[a.text_center, {fontSize: 30}]} emoji>
{emoji}
</Text>
</PressableWithHover>
)
})}
<PressableWithHover
onPress={() => setExpanded(true)}
hoverStyle={{backgroundColor: t.palette.primary_100}}