import React, { ReactNode, useCallback, useEffect, useRef, useState } from 'react' import { AccessibilityActionEvent, Platform, Pressable, StyleProp, StyleSheet, View, ViewStyle, } from 'react-native' import ReanimatedSwipeable, { SwipeableMethods, } from 'react-native-gesture-handler/ReanimatedSwipeable' import { useConversationsContext } from '../../contexts/conversations_context' import { useTheme, useCreateAndroidRippleColor } from '../../hooks' import { useConversationsMarkRead, useConversationsMute, } from '../../hooks/use_conversations_actions' import { ConversationResource } from '../../types' import { isArchivedConversation, platformPressedOpacityStyle } from '../../utils' import { tokens } from '../../vendor/tapestry/tokens' import { SwipeableToggleButton } from './swipeable_toggle_button' export function ConversationActions({ children, conversation, style, onPress, accessibilityLabel, }: { children: ReactNode conversation: ConversationResource onPress: () => void style?: StyleProp accessibilityLabel?: string }) { const swipeableRef = useRef(null) const styles = useStyles() const { activeConversationId, setActiveConversationId } = useConversationsContext() const [disabled, setDisabled] = useState(false) const overshootLeft = Platform.OS === 'ios' const androidRippleColor = useCreateAndroidRippleColor({ color: styles.swipeableChildContainer.backgroundColor, }) const { muted, setMuted, isPending: isMutedPending } = useConversationsMute({ conversation }) const { read, markRead, isPending: isMarkReadPending, } = useConversationsMarkRead({ conversation, }) const isConversationEmpty = conversation.lastMessageCreatedAt === null const canMute = !isArchivedConversation(conversation) const handleSwipeableClose = useCallback(() => { setDisabled(false) swipeableRef.current?.close() }, [setDisabled]) const handleSwipeableOpen = () => { setDisabled(true) setActiveConversationId(conversation.id) } const handlePress = useCallback(() => { if (disabled || !onPress) return onPress() }, [disabled, onPress]) const handleMute = useCallback(() => { setMuted(!muted) handleSwipeableClose() }, [muted, handleSwipeableClose, setMuted]) const handleLatestMessageUnread = useCallback(() => { markRead(!read) handleSwipeableClose() }, [read, handleSwipeableClose, markRead]) const readAccessibilityAction = !isConversationEmpty ? [ { name: read ? 'markUnread' : 'markRead', label: read ? 'Mark unread' : 'Mark read', }, ] : [] const muteAccessibilityAction = canMute ? [ { name: muted ? 'unmute' : 'mute', label: muted ? 'Enable' : 'Silence', }, ] : [] const accessibilityActions = [...muteAccessibilityAction, ...readAccessibilityAction] const handleAccessibilityAction = useCallback( (event: AccessibilityActionEvent) => { switch (event.nativeEvent.actionName) { case 'markRead': handleLatestMessageUnread() break case 'markUnread': handleLatestMessageUnread() break case 'mute': handleMute() break case 'unmute': handleMute() break } }, [handleLatestMessageUnread, handleMute] ) useEffect(() => { if (activeConversationId === conversation.id) return handleSwipeableClose() }, [activeConversationId, conversation.id, handleSwipeableClose]) return ( setDisabled(false)} renderRightActions={() => <>} renderLeftActions={() => ( )} > [style, pressed && platformPressedOpacityStyle]} android_ripple={{ color: androidRippleColor, borderless: false, foreground: true }} accessibilityLabel={accessibilityLabel} accessibilityRole="button" accessibilityActions={accessibilityActions} onAccessibilityAction={handleAccessibilityAction} > {children} ) } interface LeftActionsProps { isConversationEmpty: boolean canMute: boolean handleMute: () => void handleLatestMessageUnread: () => void muted: boolean read: boolean isMutedPending: boolean isMarkReadPending: boolean } function LeftActions({ isConversationEmpty, canMute, handleMute, handleLatestMessageUnread, muted, read, isMutedPending, isMarkReadPending, }: LeftActionsProps) { const styles = useStyles({ actionButtonCount: canMute ? 2 : 1 }) const muteToggleContent = { true: { iconName: 'general.bell', label: 'Enable' }, false: { iconName: 'general.bellMuted', label: 'Silence' }, } as const const latestMessageUnreadToggleContent = { true: { iconName: 'general.outlinedTextMessage', label: 'Mark read' }, false: { iconName: 'general.textMessageNotifications', label: 'Mark unread' }, } as const return ( {canMute && ( )} ) } const ACTION_BUTTON_WIDTH = 120 const useStyles = ({ actionButtonCount = 2 }: { actionButtonCount?: number } = {}) => { const { colors } = useTheme() return StyleSheet.create({ swipeableChildContainer: { backgroundColor: colors.conversationActionsBackground, }, actionButtonContainer: { flexDirection: 'row', alignItems: 'center', width: ACTION_BUTTON_WIDTH * actionButtonCount, alignContent: 'stretch', }, swipeableContainer: { backgroundColor: colors.surfaceColor090, }, }) }