diff --git a/native/chat/multimedia-message-tooltip-button.react.js b/native/chat/multimedia-message-tooltip-button.react.js --- a/native/chat/multimedia-message-tooltip-button.react.js +++ b/native/chat/multimedia-message-tooltip-button.react.js @@ -1,15 +1,15 @@ // @flow import * as React from 'react'; -import Animated, { type SharedValue } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; import EmojiPicker from 'rn-emoji-keyboard'; import { localIDPrefix } from 'lib/shared/message-utils'; import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils'; -import type { SetState } from 'lib/types/hook-types'; import type { AppNavigationProp } from '../navigation/app-navigator.react'; import { useSelector } from '../redux/redux-utils'; +import { useTooltipActions } from '../tooltip/tooltip-hooks'; import type { TooltipRoute } from '../tooltip/tooltip.react'; import { TooltipInlineEngagement } from './inline-engagement.react'; import { InnerMultimediaMessage } from './inner-multimedia-message.react'; @@ -33,17 +33,9 @@ +route: TooltipRoute<'MultimediaMessageTooltipModal'>, +progress: Node, +isOpeningSidebar: boolean, - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, }; function MultimediaMessageTooltipButton(props: Props): React.Node { - const { - navigation, - progress, - isOpeningSidebar, - setHideTooltip, - showEmojiKeyboard, - } = props; + const { navigation, route, progress, isOpeningSidebar } = props; const windowWidth = useSelector(state => state.dimensions.width); @@ -55,12 +47,7 @@ setSidebarInputBarHeight(height); }, []); - const { - item, - verticalBounds, - initialCoordinates, - margin, - } = props.route.params; + const { item, verticalBounds, initialCoordinates, margin } = route.params; const { style: messageContainerStyle } = useAnimatedMessageTooltipButton({ sourceMessage: item, @@ -138,6 +125,11 @@ margin, }); + const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false); + const openEmojiPicker = React.useCallback(() => { + setEmojiPickerOpen(true); + }, []); + const reactionSelectionPopover = React.useMemo(() => { if (!canCreateReactionFromMessage) { return null; @@ -145,8 +137,9 @@ return ( ); }, [ + navigation, + route, + openEmojiPicker, canCreateReactionFromMessage, reactionSelectionPopoverPosition, sendReaction, - setHideTooltip, - showEmojiKeyboard, ]); + const tooltipRouteKey = route.key; + const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); + const onEmojiSelected = React.useCallback( emoji => { sendReaction(emoji.emoji); - setHideTooltip(true); + dismissTooltip(); }, - [sendReaction, setHideTooltip], + [sendReaction, dismissTooltip], ); - const onCloseEmojiPicker = React.useCallback(() => { - showEmojiKeyboard.value = false; - navigation.goBackOnce(); - }, [navigation, showEmojiKeyboard]); - return ( <> @@ -190,8 +182,8 @@ ); diff --git a/native/chat/reaction-selection-popover.react.js b/native/chat/reaction-selection-popover.react.js --- a/native/chat/reaction-selection-popover.react.js +++ b/native/chat/reaction-selection-popover.react.js @@ -2,27 +2,30 @@ import * as React from 'react'; import { View, TouchableOpacity, Text } from 'react-native'; -import type { SharedValue } from 'react-native-reanimated'; - -import type { SetState } from 'lib/types/hook-types'; import SWMansionIcon from '../components/swmansion-icon.react'; +import type { AppNavigationProp } from '../navigation/app-navigator.react'; +import type { TooltipModalParamList } from '../navigation/route-names'; import { useStyles } from '../themes/colors'; +import { useTooltipActions } from '../tooltip/tooltip-hooks'; +import type { TooltipRoute } from '../tooltip/tooltip.react'; import type { ViewStyle } from '../types/styles'; -type ReactionSelectionPopoverProps = { - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, +type Props> = { + +navigation: AppNavigationProp, + +route: TooltipRoute, + +openEmojiPicker: () => mixed, +reactionSelectionPopoverContainerStyle: ViewStyle, +sendReaction: (reaction: string) => mixed, }; -function ReactionSelectionPopover( - props: ReactionSelectionPopoverProps, +function ReactionSelectionPopover>( + props: Props, ): React.Node { const { - setHideTooltip, - showEmojiKeyboard, + navigation, + route, + openEmojiPicker, reactionSelectionPopoverContainerStyle, sendReaction, } = props; @@ -40,18 +43,24 @@ ], ); + const tooltipRouteKey = route.key; + const { hideTooltip, dismissTooltip } = useTooltipActions( + navigation, + tooltipRouteKey, + ); + const onPressDefaultEmoji = React.useCallback( (emoji: string) => { sendReaction(emoji); - setHideTooltip(true); + dismissTooltip(); }, - [sendReaction, setHideTooltip], + [sendReaction, dismissTooltip], ); const onPressEmojiKeyboardButton = React.useCallback(() => { - showEmojiKeyboard.value = true; - setHideTooltip(true); - }, [setHideTooltip, showEmojiKeyboard]); + openEmojiPicker(); + hideTooltip(); + }, [openEmojiPicker, hideTooltip]); const defaultEmojis = React.useMemo(() => { const defaultEmojisData = ['❤️', '😆', '😮', '😠', '👍']; diff --git a/native/chat/robotext-message-tooltip-button.react.js b/native/chat/robotext-message-tooltip-button.react.js --- a/native/chat/robotext-message-tooltip-button.react.js +++ b/native/chat/robotext-message-tooltip-button.react.js @@ -1,15 +1,15 @@ // @flow import * as React from 'react'; -import Animated, { type SharedValue } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; import EmojiPicker from 'rn-emoji-keyboard'; import { localIDPrefix } from 'lib/shared/message-utils'; import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils'; -import type { SetState } from 'lib/types/hook-types'; import type { AppNavigationProp } from '../navigation/app-navigator.react'; import { useSelector } from '../redux/redux-utils'; +import { useTooltipActions } from '../tooltip/tooltip-hooks'; import type { TooltipRoute } from '../tooltip/tooltip.react'; import { TooltipInlineEngagement } from './inline-engagement.react'; import { InnerRobotextMessage } from './inner-robotext-message.react'; @@ -31,17 +31,9 @@ +route: TooltipRoute<'RobotextMessageTooltipModal'>, +progress: Node, +isOpeningSidebar: boolean, - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, }; function RobotextMessageTooltipButton(props: Props): React.Node { - const { - navigation, - progress, - isOpeningSidebar, - setHideTooltip, - showEmojiKeyboard, - } = props; + const { navigation, route, progress, isOpeningSidebar } = props; const windowWidth = useSelector(state => state.dimensions.width); @@ -53,12 +45,7 @@ setSidebarInputBarHeight(height); }, []); - const { - item, - verticalBounds, - initialCoordinates, - margin, - } = props.route.params; + const { item, verticalBounds, initialCoordinates, margin } = route.params; const { style: messageContainerStyle } = useAnimatedMessageTooltipButton({ sourceMessage: item, @@ -122,6 +109,11 @@ margin, }); + const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false); + const openEmojiPicker = React.useCallback(() => { + setEmojiPickerOpen(true); + }, []); + const reactionSelectionPopover = React.useMemo(() => { if (!canCreateReactionFromMessage) { return null; @@ -129,8 +121,9 @@ return ( ); }, [ + navigation, + route, + openEmojiPicker, canCreateReactionFromMessage, reactionSelectionPopoverPosition, sendReaction, - setHideTooltip, - showEmojiKeyboard, ]); + const tooltipRouteKey = route.key; + const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); + const onEmojiSelected = React.useCallback( emoji => { sendReaction(emoji.emoji); - setHideTooltip(true); + dismissTooltip(); }, - [sendReaction, setHideTooltip], + [sendReaction, dismissTooltip], ); - const onCloseEmojiPicker = React.useCallback(() => { - showEmojiKeyboard.value = false; - navigation.goBackOnce(); - }, [navigation, showEmojiKeyboard]); - return ( <> @@ -174,8 +166,8 @@ ); diff --git a/native/chat/text-message-tooltip-button.react.js b/native/chat/text-message-tooltip-button.react.js --- a/native/chat/text-message-tooltip-button.react.js +++ b/native/chat/text-message-tooltip-button.react.js @@ -1,15 +1,15 @@ // @flow import * as React from 'react'; -import Animated, { type SharedValue } from 'react-native-reanimated'; +import Animated from 'react-native-reanimated'; import EmojiPicker from 'rn-emoji-keyboard'; import { localIDPrefix } from 'lib/shared/message-utils'; import { useCanCreateReactionFromMessage } from 'lib/shared/reaction-utils'; -import type { SetState } from 'lib/types/hook-types'; import type { AppNavigationProp } from '../navigation/app-navigator.react'; import { useSelector } from '../redux/redux-utils'; +import { useTooltipActions } from '../tooltip/tooltip-hooks'; import type { TooltipRoute } from '../tooltip/tooltip.react'; import { TooltipInlineEngagement } from './inline-engagement.react'; import { InnerTextMessage } from './inner-text-message.react'; @@ -33,17 +33,9 @@ +route: TooltipRoute<'TextMessageTooltipModal'>, +progress: Node, +isOpeningSidebar: boolean, - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, }; function TextMessageTooltipButton(props: Props): React.Node { - const { - navigation, - progress, - isOpeningSidebar, - setHideTooltip, - showEmojiKeyboard, - } = props; + const { navigation, route, progress, isOpeningSidebar } = props; const windowWidth = useSelector(state => state.dimensions.width); @@ -55,12 +47,7 @@ setSidebarInputBarHeight(height); }, []); - const { - item, - verticalBounds, - initialCoordinates, - margin, - } = props.route.params; + const { item, verticalBounds, initialCoordinates, margin } = route.params; const { style: messageContainerStyle, @@ -137,6 +124,11 @@ margin, }); + const [emojiPickerOpen, setEmojiPickerOpen] = React.useState(false); + const openEmojiPicker = React.useCallback(() => { + setEmojiPickerOpen(true); + }, []); + const reactionSelectionPopover = React.useMemo(() => { if (!canCreateReactionFromMessage) { return null; @@ -144,8 +136,9 @@ return ( ); }, [ + navigation, + route, + openEmojiPicker, canCreateReactionFromMessage, reactionSelectionPopoverPosition, sendReaction, - setHideTooltip, - showEmojiKeyboard, ]); + const tooltipRouteKey = route.key; + const { dismissTooltip } = useTooltipActions(navigation, tooltipRouteKey); + const onEmojiSelected = React.useCallback( emoji => { sendReaction(emoji.emoji); - setHideTooltip(true); + dismissTooltip(); }, - [sendReaction, setHideTooltip], + [sendReaction, dismissTooltip], ); - const onCloseEmojiPicker = React.useCallback(() => { - showEmojiKeyboard.value = false; - navigation.goBackOnce(); - }, [navigation, showEmojiKeyboard]); - return ( ); diff --git a/native/tooltip/tooltip-context.react.js b/native/tooltip/tooltip-context.react.js --- a/native/tooltip/tooltip-context.react.js +++ b/native/tooltip/tooltip-context.react.js @@ -24,6 +24,7 @@ +maxOptionsToDisplay: number, +visibleEntryIDs: ?$ReadOnlyArray, +cancel: () => mixed, + +hideTooltip: () => mixed, +children: React.Node, }; function TooltipContextProvider(props: ProviderProps): React.Node { @@ -72,9 +73,11 @@ optionsRef.current = optionsRef.current.filter(option => option.id !== id); }, []); - const { cancel } = props; + const { cancel, hideTooltip } = props; const { showActionSheetWithOptions } = useActionSheet(); const showActionSheet = React.useCallback(() => { + hideTooltip(); + const options = optionsRef.current; const optionsToDisplay = options.filter(option => @@ -131,6 +134,7 @@ onPressAction, ); }, [ + hideTooltip, maxOptionsToDisplay, visibleEntryIDsSet, cancel, diff --git a/native/tooltip/tooltip-hooks.js b/native/tooltip/tooltip-hooks.js new file mode 100644 --- /dev/null +++ b/native/tooltip/tooltip-hooks.js @@ -0,0 +1,39 @@ +// @flow + +import * as React from 'react'; + +import type { AppNavigationProp } from '../navigation/app-navigator.react'; +import type { TooltipModalParamList } from '../navigation/route-names'; + +type TooltipActions = { + // Hiding will keep the Tooltip ReactNav screen open, which means that the + // background will still be dimmed. But it will hide the actual tooltip menu. + +hideTooltip: () => void, + // Dismiss the tooltip will dismiss the ReactNav screen. This will start the + // OverlayNavigator animation to dismiss the screen. + +dismissTooltip: () => void, +}; +function useTooltipActions>( + navigation: AppNavigationProp, + tooltipRouteKey: string, +): TooltipActions { + const { clearOverlayModals, setRouteParams } = navigation; + + const hideTooltip = React.useCallback(() => { + setRouteParams(tooltipRouteKey, { hideTooltip: true }); + }, [setRouteParams, tooltipRouteKey]); + + const dismissTooltip = React.useCallback(() => { + clearOverlayModals([tooltipRouteKey]); + }, [clearOverlayModals, tooltipRouteKey]); + + return React.useMemo( + () => ({ + hideTooltip, + dismissTooltip, + }), + [hideTooltip, dismissTooltip], + ); +} + +export { useTooltipActions }; diff --git a/native/tooltip/tooltip-item.react.js b/native/tooltip/tooltip-item.react.js --- a/native/tooltip/tooltip-item.react.js +++ b/native/tooltip/tooltip-item.react.js @@ -20,7 +20,7 @@ type Props = { ...TooltipItemBaseProps, +containerStyle?: ViewStyle, - +closeTooltip: () => mixed, + +closeTooltip?: () => mixed, }; function TooltipItem(props: Props): React.Node { const tooltipContext = React.useContext(TooltipContext); @@ -45,7 +45,7 @@ const onPress = React.useCallback(() => { onPressItem(); - closeTooltip(); + closeTooltip?.(); }, [onPressItem, closeTooltip]); if (!shouldRender) { diff --git a/native/tooltip/tooltip.react.js b/native/tooltip/tooltip.react.js --- a/native/tooltip/tooltip.react.js +++ b/native/tooltip/tooltip.react.js @@ -10,15 +10,7 @@ Platform, Keyboard, } from 'react-native'; -import Animated, { - SlideInDown, - SlideOutDown, - runOnJS, - useSharedValue, - type SharedValue, -} from 'react-native-reanimated'; - -import type { SetState } from 'lib/types/hook-types'; +import Animated, { SlideInDown, SlideOutDown } from 'react-native-reanimated'; import { ChatContext, type ChatContextType } from '../chat/chat-context'; import SWMansionIcon from '../components/swmansion-icon.react'; @@ -59,6 +51,7 @@ +margin?: number, +visibleEntryIDs?: $ReadOnlyArray, +chatInputBarHeight?: number, + +hideTooltip?: boolean, }; export type TooltipRoute> = RouteProp< TooltipModalParamList, @@ -73,8 +66,6 @@ ...Base, +progress: Node, +isOpeningSidebar: boolean, - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, }; type TooltipProps = { ...Base, @@ -82,11 +73,6 @@ +dimensions: DimensionsInfo, +overlayContext: ?OverlayContextType, +chatContext: ?ChatContextType, - +actionSheetShown: SharedValue, - +hideTooltip: boolean, - +setHideTooltip: SetState, - +showEmojiKeyboard: SharedValue, - +exitAnimationWorklet: (finished: boolean) => void, +styles: typeof unboundStyles, +tooltipContext: TooltipContextType, +closeTooltip: () => mixed, @@ -301,11 +287,6 @@ dimensions, overlayContext, chatContext, - actionSheetShown, - hideTooltip, - setHideTooltip, - showEmojiKeyboard, - exitAnimationWorklet, styles, tooltipContext, closeTooltip, @@ -335,7 +316,6 @@ onPress={this.onPressMore} renderIcon={this.renderMoreIcon} containerStyle={tooltipContainerStyle} - closeTooltip={this.props.closeTooltip} key="more" />, ); @@ -376,16 +356,13 @@ ...navAndRouteForFlow, progress: position, isOpeningSidebar, - setHideTooltip, - showEmojiKeyboard, }; const itemsStyles = [styles.items, styles.itemsFixed]; const animationDelay = Platform.OS === 'ios' ? 200 : 500; const enterAnimation = SlideInDown.delay(animationDelay); - - const exitAnimation = SlideOutDown.withCallback(exitAnimationWorklet); + const exitAnimation = SlideOutDown; let tooltip = null; @@ -402,8 +379,7 @@ ); } else if ( this.tooltipLocation === 'fixed' && - !hideTooltip && - !showEmojiKeyboard.value + !this.props.route.params.hideTooltip ) { tooltip = ( { Keyboard.dismiss(); - this.props.actionSheetShown.value = true; - this.props.setHideTooltip(true); this.props.tooltipContext.showActionSheet(); }; @@ -467,45 +441,24 @@ } }; } - function ConnectedTooltip(props: BaseTooltipPropsType) { + function ConnectedTooltip(props) { const dimensions = useSelector(state => state.dimensions); const overlayContext = React.useContext(OverlayContext); const chatContext = React.useContext(ChatContext); - const actionSheetShown = useSharedValue(false); - const [hideTooltip, setHideTooltip] = React.useState(false); - - const showEmojiKeyboard = useSharedValue(false); - - const { goBackOnce } = props.navigation; - const goBackCallback = React.useCallback(() => { - if (!actionSheetShown.value) { - goBackOnce(); - } - }, [actionSheetShown.value, goBackOnce]); - - const exitAnimationWorklet = React.useCallback( - finished => { - 'worklet'; - if (finished) { - runOnJS(goBackCallback)(); - } - }, - [goBackCallback], - ); - const { params } = props.route; const { tooltipLocation } = params; const isFixed = tooltipLocation === 'fixed'; + const { hideTooltip, ...rest } = props; + + const { goBackOnce } = props.navigation; const closeTooltip = React.useCallback(() => { - if (isFixed && !actionSheetShown.value) { - setHideTooltip(true); - } else { - goBackOnce(); + goBackOnce(); + if (isFixed) { + hideTooltip(); } - showEmojiKeyboard.value = false; - }, [isFixed, actionSheetShown.value, goBackOnce, showEmojiKeyboard]); + }, [isFixed, hideTooltip, goBackOnce]); const styles = useStyles(unboundStyles); const boundTooltipItem = React.useCallback( @@ -528,15 +481,10 @@ invariant(tooltipContext, 'TooltipContext should be set in Tooltip'); return ( { + const paramsUpdate: any = { hideTooltip: true }; + setParams(paramsUpdate); + }, [setParams]); + return ( - + ); }