diff --git a/native/chat/text-message.react.js b/native/chat/text-message.react.js --- a/native/chat/text-message.react.js +++ b/native/chat/text-message.react.js @@ -50,7 +50,7 @@ // ChatContext +chatContext: ?ChatContextType, // MarkdownContext - +linkModalActive: boolean, + +isLinkModalActive: boolean, }; class TextMessage extends React.PureComponent { message: ?React.ElementRef; @@ -73,7 +73,7 @@ verticalBounds, overlayContext, chatContext, - linkModalActive, + isLinkModalActive, canCreateSidebarFromMessage, ...viewProps } = this.props; @@ -81,7 +81,7 @@ let swipeOptions = 'none'; const canReply = this.canReply(); const canNavigateToSidebar = this.canNavigateToSidebar(); - if (linkModalActive) { + if (isLinkModalActive) { swipeOptions = 'none'; } else if (canReply && canNavigateToSidebar) { swipeOptions = 'both'; @@ -159,9 +159,9 @@ const { message, - props: { verticalBounds, linkModalActive }, + props: { verticalBounds, isLinkModalActive }, } = this; - if (!message || !verticalBounds || linkModalActive) { + if (!message || !verticalBounds || isLinkModalActive) { return; } @@ -222,29 +222,33 @@ function ConnectedTextMessage(props: BaseProps) { const overlayContext = React.useContext(OverlayContext); const chatContext = React.useContext(ChatContext); + const markdownContext = React.useContext(MarkdownContext); + invariant(markdownContext, 'markdownContext should be set'); + + const { linkModalActive, clearMarkdownContextData } = markdownContext; + + const key = messageKey(props.item.messageInfo); + + // We check if there is an key in the object - if not, we + // default to false. The likely situation where the former statement + // evaluates to null is when the thread is opened for the first time. + const isLinkModalActive = linkModalActive[key] ?? false; - const [linkModalActive, setLinkModalActive] = React.useState(false); - const markdownContext = React.useMemo( - () => ({ - setLinkModalActive, - }), - [setLinkModalActive], - ); const canCreateSidebarFromMessage = useCanCreateSidebarFromMessage( props.item.threadInfo, props.item.messageInfo, ); + React.useEffect(() => clearMarkdownContextData, [clearMarkdownContextData]); + return ( - - - + ); }, ); diff --git a/native/markdown/markdown-context-provider.react.js b/native/markdown/markdown-context-provider.react.js new file mode 100644 --- /dev/null +++ b/native/markdown/markdown-context-provider.react.js @@ -0,0 +1,36 @@ +// @flow + +import * as React from 'react'; + +import { MarkdownContext } from './markdown-context.js'; + +type Props = { + +children: React.Node, +}; + +function MarkdownContextProvider(props: Props): React.Node { + const [linkModalActive, setLinkModalActive] = React.useState<{ + [key: string]: boolean, + }>({}); + + const clearMarkdownContextData = React.useCallback(() => { + setLinkModalActive({}); + }, []); + + const contextValue = React.useMemo( + () => ({ + setLinkModalActive, + linkModalActive, + clearMarkdownContextData, + }), + [setLinkModalActive, linkModalActive, clearMarkdownContextData], + ); + + return ( + + {props.children} + + ); +} + +export default MarkdownContextProvider; diff --git a/native/markdown/markdown-context.js b/native/markdown/markdown-context.js --- a/native/markdown/markdown-context.js +++ b/native/markdown/markdown-context.js @@ -2,8 +2,12 @@ import * as React from 'react'; +import type { SetState } from 'lib/types/hook-types'; + export type MarkdownContextType = { - +setLinkModalActive: boolean => void, + +setLinkModalActive: SetState<{ [key: string]: boolean }>, + +linkModalActive: { [key: string]: boolean }, + +clearMarkdownContextData: () => void, }; const MarkdownContext: React.Context = React.createContext( diff --git a/native/markdown/markdown-link.react.js b/native/markdown/markdown-link.react.js --- a/native/markdown/markdown-link.react.js +++ b/native/markdown/markdown-link.react.js @@ -1,20 +1,23 @@ // @flow +import invariant from 'invariant'; import * as React from 'react'; import { Text, Linking, Alert } from 'react-native'; import { normalizeURL } from 'lib/utils/url-utils'; +import { TextMessageMarkdownContext } from '../chat/text-message-markdown-context'; import { MarkdownContext, type MarkdownContextType } from './markdown-context'; function useDisplayLinkPrompt( inputURL: string, - markdownContext: ?MarkdownContextType, + markdownContext: MarkdownContextType, + messageKey: ?string, ) { - const setLinkModalActive = markdownContext?.setLinkModalActive; + const { setLinkModalActive } = markdownContext; const onDismiss = React.useCallback(() => { - setLinkModalActive?.(false); - }, [setLinkModalActive]); + messageKey && setLinkModalActive({ [messageKey]: false }); + }, [setLinkModalActive, messageKey]); const url = normalizeURL(inputURL); const onConfirm = React.useCallback(() => { @@ -27,7 +30,7 @@ displayURL += '…'; } return React.useCallback(() => { - setLinkModalActive && setLinkModalActive(true); + messageKey && setLinkModalActive({ [messageKey]: true }); Alert.alert( 'External link', `You sure you want to open this link?\n\n${displayURL}`, @@ -37,7 +40,7 @@ ], { cancelable: true, onDismiss }, ); - }, [setLinkModalActive, displayURL, onConfirm, onDismiss]); + }, [setLinkModalActive, messageKey, displayURL, onConfirm, onDismiss]); } type TextProps = React.ElementConfig; @@ -48,8 +51,17 @@ }; function MarkdownLink(props: Props): React.Node { const { target, ...rest } = props; + const markdownContext = React.useContext(MarkdownContext); - const onPressLink = useDisplayLinkPrompt(target, markdownContext); + invariant(markdownContext, 'MarkdownContext should be set'); + + const textMessageMarkdownContext = React.useContext( + TextMessageMarkdownContext, + ); + const messageKey = textMessageMarkdownContext?.messageKey; + + const onPressLink = useDisplayLinkPrompt(target, markdownContext, messageKey); + return ; } diff --git a/native/root.react.js b/native/root.react.js --- a/native/root.react.js +++ b/native/root.react.js @@ -26,6 +26,7 @@ import ErrorBoundary from './error-boundary.react'; import InputStateContainer from './input/input-state-container.react'; import LifecycleHandler from './lifecycle/lifecycle-handler.react'; +import MarkdownContextProvider from './markdown/markdown-context-provider.react'; import { defaultNavigationState } from './navigation/default-state'; import DisconnectedBarVisibilityHandler from './navigation/disconnected-bar-visibility-handler.react'; import { setGlobalNavContext } from './navigation/icky-global'; @@ -246,22 +247,24 @@ - - - - - {gated} - - - - - {navigation} - - + + + + + + {gated} + + + + + {navigation} + + +