diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js index 064fca849..89af82d4c 100644 --- a/native/chat/text-message.react.js +++ b/native/chat/text-message.react.js @@ -1,257 +1,251 @@ // @flow import invariant from 'invariant'; import * as React from 'react'; import { View } from 'react-native'; import { messageKey } from 'lib/shared/message-utils'; import { threadHasPermission, useCanCreateSidebarFromMessage, } from 'lib/shared/thread-utils'; import { threadPermissions } from 'lib/types/thread-types'; import { ChatContext, type ChatContextType } from '../chat/chat-context'; import { MarkdownLinkContext } from '../markdown/markdown-link-context'; import { OverlayContext, type OverlayContextType, } from '../navigation/overlay-context'; import type { NavigationRoute } from '../navigation/route-names'; import { TextMessageTooltipModalRouteName } from '../navigation/route-names'; import { fixedTooltipHeight } from '../navigation/tooltip.react'; import type { ChatTextMessageInfoItemWithHeight } from '../types/chat-types'; import type { VerticalBounds } from '../types/layout-types'; import type { ChatNavigationProp } from './chat.react'; import ComposedMessage from './composed-message.react'; import { InnerTextMessage } from './inner-text-message.react'; import { MessagePressResponderContext, type MessagePressResponderContextType, } from './message-press-responder-context'; import textMessageSendFailed from './text-message-send-failed'; import { getMessageTooltipKey } from './utils'; type BaseProps = { ...React.ElementConfig, +item: ChatTextMessageInfoItemWithHeight, +navigation: ChatNavigationProp<'MessageList'>, +route: NavigationRoute<'MessageList'>, +focused: boolean, +toggleFocus: (messageKey: string) => void, +verticalBounds: ?VerticalBounds, }; type Props = { ...BaseProps, // Redux state +canCreateSidebarFromMessage: boolean, // withOverlayContext +overlayContext: ?OverlayContextType, // MarkdownLinkContext +chatContext: ?ChatContextType, +linkModalActive: boolean, - +linkIsBlockingPresses: boolean, }; class TextMessage extends React.PureComponent { message: ?React.ElementRef; messagePressResponderContext: MessagePressResponderContextType; constructor(props: Props) { super(props); this.messagePressResponderContext = { onPressMessage: this.onPress, }; } render() { const { item, navigation, route, focused, toggleFocus, verticalBounds, overlayContext, chatContext, linkModalActive, - linkIsBlockingPresses, canCreateSidebarFromMessage, ...viewProps } = this.props; let swipeOptions = 'none'; const canReply = this.canReply(); const canNavigateToSidebar = this.canNavigateToSidebar(); if (linkModalActive) { swipeOptions = 'none'; } else if (canReply && canNavigateToSidebar) { swipeOptions = 'both'; } else if (canReply) { swipeOptions = 'reply'; } else if (canNavigateToSidebar) { swipeOptions = 'sidebar'; } return ( ); } messageRef = (message: ?React.ElementRef) => { this.message = message; }; canReply() { return threadHasPermission( this.props.item.threadInfo, threadPermissions.VOICED, ); } canNavigateToSidebar() { return ( this.props.item.threadCreatedFromMessage || this.props.canCreateSidebarFromMessage ); } visibleEntryIDs() { const result = ['copy']; if (this.canReply()) { result.push('reply'); } if ( this.props.item.threadCreatedFromMessage || this.props.canCreateSidebarFromMessage ) { result.push('sidebar'); } if (!this.props.item.messageInfo.creator.isViewer) { result.push('report'); } return result; } onPress = () => { const visibleEntryIDs = this.visibleEntryIDs(); if (visibleEntryIDs.length === 0) { return; } const { message, - props: { verticalBounds, linkIsBlockingPresses }, + props: { verticalBounds, linkModalActive }, } = this; - if (!message || !verticalBounds || linkIsBlockingPresses) { + if (!message || !verticalBounds || linkModalActive) { return; } const { focused, toggleFocus, item } = this.props; if (!focused) { toggleFocus(messageKey(item.messageInfo)); } const { overlayContext } = this.props; invariant(overlayContext, 'TextMessage should have OverlayContext'); overlayContext.setScrollBlockingModalStatus('open'); message.measure((x, y, width, height, pageX, pageY) => { const coordinates = { x: pageX, y: pageY, width, height }; const messageTop = pageY; const messageBottom = pageY + height; const boundsTop = verticalBounds.y; const boundsBottom = verticalBounds.y + verticalBounds.height; const belowMargin = 20; const belowSpace = fixedTooltipHeight + belowMargin; const { isViewer } = item.messageInfo.creator; const aboveMargin = isViewer ? 30 : 50; const aboveSpace = fixedTooltipHeight + aboveMargin; let margin = belowMargin; if ( messageBottom + belowSpace > boundsBottom && messageTop - aboveSpace > boundsTop ) { margin = aboveMargin; } const currentInputBarHeight = this.props.chatContext?.chatInputBarHeights.get(item.threadInfo.id) ?? 0; this.props.navigation.navigate<'TextMessageTooltipModal'>({ name: TextMessageTooltipModalRouteName, params: { presentedFrom: this.props.route.key, initialCoordinates: coordinates, verticalBounds, visibleEntryIDs, location: 'fixed', margin, item, chatInputBarHeight: currentInputBarHeight, }, key: getMessageTooltipKey(item), }); }); }; } const ConnectedTextMessage: React.ComponentType = React.memo( function ConnectedTextMessage(props: BaseProps) { const overlayContext = React.useContext(OverlayContext); const chatContext = React.useContext(ChatContext); const [linkModalActive, setLinkModalActive] = React.useState(false); - const [linkPressActive, setLinkPressActive] = React.useState(false); const markdownLinkContext = React.useMemo( () => ({ setLinkModalActive, - setLinkPressActive, }), - [setLinkModalActive, setLinkPressActive], + [setLinkModalActive], ); const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( props.item.threadInfo, props.item.messageInfo, ); - const linkIsBlockingPresses = linkModalActive || linkPressActive; return ( ); }, ); export { ConnectedTextMessage as TextMessage }; diff --git a/native/markdown/markdown-link-context.js b/native/markdown/markdown-link-context.js index 60f820c33..0eab92fab 100644 --- a/native/markdown/markdown-link-context.js +++ b/native/markdown/markdown-link-context.js @@ -1,14 +1,13 @@ // @flow import * as React from 'react'; export type MarkdownLinkContextType = { +setLinkModalActive: boolean => void, - +setLinkPressActive: boolean => void, }; const MarkdownLinkContext: React.Context = React.createContext( null, ); export { MarkdownLinkContext }; diff --git a/native/markdown/markdown-link.react.js b/native/markdown/markdown-link.react.js index 6592c8a25..6bc1b6acf 100644 --- a/native/markdown/markdown-link.react.js +++ b/native/markdown/markdown-link.react.js @@ -1,91 +1,59 @@ // @flow import * as React from 'react'; -import { Text, Linking, Alert, Platform } from 'react-native'; +import { Text, Linking, Alert } from 'react-native'; import { normalizeURL } from 'lib/utils/url-utils'; import { MarkdownLinkContext, type MarkdownLinkContextType, } from './markdown-link-context'; function useDisplayLinkPrompt( inputURL: string, markdownLinkContext: ?MarkdownLinkContextType, ) { const setLinkModalActive = markdownLinkContext?.setLinkModalActive; const onDismiss = React.useCallback(() => { setLinkModalActive?.(false); }, [setLinkModalActive]); const url = normalizeURL(inputURL); const onConfirm = React.useCallback(() => { onDismiss(); Linking.openURL(url); }, [url, onDismiss]); let displayURL = url.substring(0, 64); if (url.length > displayURL.length) { displayURL += '…'; } return React.useCallback(() => { setLinkModalActive && setLinkModalActive(true); Alert.alert( 'External link', `You sure you want to open this link?\n\n${displayURL}`, [ { text: 'Cancel', style: 'cancel', onPress: onDismiss }, { text: 'Open', onPress: onConfirm }, ], { cancelable: true, onDismiss }, ); }, [setLinkModalActive, displayURL, onConfirm, onDismiss]); } type TextProps = React.ElementConfig; type Props = { +target: string, +children: React.Node, ...TextProps, }; function MarkdownLink(props: Props): React.Node { - const markdownLinkContext = React.useContext(MarkdownLinkContext); - const { target, ...rest } = props; + const markdownLinkContext = React.useContext(MarkdownLinkContext); const onPressLink = useDisplayLinkPrompt(target, markdownLinkContext); - - const setLinkPressActive = markdownLinkContext?.setLinkPressActive; - const androidOnStartShouldSetResponderCapture = React.useCallback(() => { - setLinkPressActive?.(true); - return true; - }, [setLinkPressActive]); - - const activePressHasMoved = React.useRef(false); - const androidOnResponderMove = React.useCallback(() => { - activePressHasMoved.current = true; - }, []); - - const androidOnResponderTerminate = React.useCallback(() => { - if (!activePressHasMoved.current) { - onPressLink(); - } - activePressHasMoved.current = false; - setLinkPressActive?.(false); - }, [onPressLink, setLinkPressActive]); - - if (Platform.OS !== 'android') { - return ; - } - - // The Flow type for Text's props is missing onStartShouldSetResponderCapture - const gestureProps: any = { - onStartShouldSetResponderCapture: androidOnStartShouldSetResponderCapture, - onResponderMove: androidOnResponderMove, - onResponderTerminate: androidOnResponderTerminate, - }; - - return ; + return ; } export default MarkdownLink;