import { useMemo } from 'react'; import { Pressable, Text, View } from 'react-native'; import { Plus } from 'lucide-react-native'; import * as Haptics from 'expo-haptics'; import { REACTION_EMOJIS, type Reactions, type Human } from '@/api/types'; import { Avatar } from '@/components/Avatar'; import { cn } from '@/lib/utils'; const EMOJI_SET = new Set(REACTION_EMOJIS); interface ReactionStackProps { reactions: Reactions; currentHumanId: string; humans: Human[] | undefined; /** Toggle a reaction (emoji or text) — same contract as ReactionSheet's onToggle. */ onToggle: (key: string) => void; /** Open the full reaction sheet for emoji + custom-text picking. */ onOpenSheet: () => void; } /** * Right-edge reaction stack — mobile counterpart of desktop's ReactionBar. * Sits vertically centered on the right side of the canvas so the user can * see existing reactions at a glance and tap to toggle their own. The "+" * affordance opens the ReactionSheet for the full picker (emoji or text). */ export function ReactionStack({ reactions, currentHumanId, humans, onToggle, onOpenSheet, }: ReactionStackProps) { const activeEmojis = REACTION_EMOJIS.filter( (emoji) => reactions?.[emoji] && (reactions[emoji]?.length ?? 0) > 0, ); const activeTextKeys = useMemo( () => Object.keys(reactions ?? {}).filter( (k) => !EMOJI_SET.has(k) && (reactions?.[k]?.length ?? 0) > 0, ), [reactions], ); const handleToggle = (key: string) => { void Haptics.selectionAsync(); onToggle(key); }; return ( {activeEmojis.map((emoji) => { const reactors = reactions?.[emoji] ?? []; const isMine = reactors.includes(currentHumanId); return ( handleToggle(emoji)} className={cn( 'flex-row items-center gap-1 rounded-full px-2 py-1', isMine ? 'bg-white/25' : 'bg-black/45', )} style={ isMine ? { borderWidth: 1, borderColor: 'rgba(255,255,255,0.4)' } : undefined } > {emoji} {reactors.length} ); })} {activeTextKeys.map((text) => { const reactors = reactions?.[text] ?? []; const isMine = reactors.includes(currentHumanId); return ( handleToggle(text)} className={cn( 'flex-row items-center gap-1.5 rounded-full py-1 pl-1 pr-2.5', isMine ? 'bg-white/25' : 'bg-black/45', )} style={[ { maxWidth: 200 }, isMine ? { borderWidth: 1, borderColor: 'rgba(255,255,255,0.4)' } : null, ]} > {text} {reactors.length > 1 ? ( {reactors.length} ) : null} ); })} ); }