import { StaticScreenProps } from '@react-navigation/native' import React, { memo } from 'react' import { Platform, StyleSheet, View } from 'react-native' import { FlatList } from 'react-native-gesture-handler' import { useSafeAreaInsets } from 'react-native-safe-area-context' import { Avatar, Text } from '../components' import { Tabs } from '../components/display/tabs' import FormSheet, { getFormSheetScreenOptions } from '../components/primitive/form_sheet' import { useSuspenseGet, useTheme } from '../hooks' import { useConversationMessages } from '../hooks/use_conversation_messages' import { useFontScale } from '../hooks/use_font_scale' import { MemberResource } from '../types' import { ReactionCountResource } from '../types/resources/reaction' import { platformFontWeightMedium, REACTION_EMOJIS } from '../utils' export const ReactionsScreenOptions = getFormSheetScreenOptions({ sheetAllowedDetents: Platform.select({ android: [0.5, 0.94], // Going straight to full 0.94 preserves height of screen on Android default: [0.5, 1], }), headerTitle: 'Reactions', }) export type ReactionScreenProps = StaticScreenProps<{ message_id: string conversation_id: number reaction_value?: string }> export function ReactionsScreen({ route }: ReactionScreenProps) { const { conversation_id, message_id, reaction_value } = route.params const styles = useStyles() const { messages } = useConversationMessages({ conversation_id }, { refetchOnMount: false }) const message = messages.find(m => m.id === message_id) const reactionCounts = message?.reactionCounts || [] const initialReactionCount = reactionCounts.find(r => r.value === reaction_value) || reactionCounts[0] const [reactionCountValue, setReactionCountValue] = React.useState< ReactionCountResource['value'] >(initialReactionCount?.value) const reactionCount = reactionCounts.find(r => r.value === reactionCountValue) // Get author IDs for the API call, ensuring we have a valid array const authorIds = reactionCount?.authorIds || [] const { data: members } = useSuspenseGet({ url: `/me/conversations/${conversation_id}/members`, data: { fields: { Member: ['id', 'name', 'avatar', 'badges', 'child', 'role'], }, where: { id: authorIds, }, limit: authorIds.length, }, }) if (!reactionCount) { return ( No reactions found for this message. ) } const authors = members.filter(member => authorIds.includes(member.id)) return ( { setReactionCountValue(item.value) }} renderItem={({ item }) => ( setReactionCountValue(item.value)} /> )} style={styles.actions} /> item.id.toString()} renderItem={({ item: author }) => } /> ) } const Reaction = ({ reaction, }: { active: boolean reaction: ReactionCountResource onPress: () => void }) => { const styles = useStyles() return ( {REACTION_EMOJIS[reaction.value]} {reaction.count} ) } type AuthorProps = Pick const Author = memo(({ author }: { author: AuthorProps }) => { const styles = useStyles() return ( {author.name} ) }) const TABS_HEIGHT = 67 const useStyles = () => { const { colors } = useTheme() const { bottom } = useSafeAreaInsets() const fontScale = useFontScale({ maxFontSizeMultiplier: 1.3 }) return StyleSheet.create({ formSheetContent: { paddingTop: 16, }, contentContainer: { paddingTop: 8, paddingBottom: bottom + Platform.select({ android: 24, default: TABS_HEIGHT + 24 }), }, authorRow: { flexDirection: 'row', alignItems: 'center', gap: 8, paddingVertical: 8, paddingHorizontal: 16, flex: 1, }, authorName: { flex: 1, }, reaction: { paddingHorizontal: 8, paddingVertical: 16, flexDirection: 'row', gap: 4, }, reactionContent: { fontSize: 18, fontWeight: platformFontWeightMedium, }, actions: { minHeight: 68 * fontScale, borderBottomColor: colors.borderColorDefaultBase, borderBottomWidth: 1, }, errorContainer: { flex: 1, justifyContent: 'center', alignItems: 'center', padding: 16, marginTop: 30, }, }) }