diff --git a/native/chat/inner-text-message.react.js b/native/chat/inner-text-message.react.js --- a/native/chat/inner-text-message.react.js +++ b/native/chat/inner-text-message.react.js @@ -99,30 +99,6 @@ return [styles.text, textStyle]; }, [darkColor]); - // If we need to render a Text with an onPress prop inside, we're going to - // have an issue: the GestureTouchableOpacity below will trigger too when the - // the onPress is pressed. We have to use a GestureTouchableOpacity in order - // for the message touch gesture to play nice with the message swipe gesture, - // so we need to find a way to disable the GestureTouchableOpacity. - // - // Our solution is to keep using the GestureTouchableOpacity for the padding - // around the text, and to have the Texts inside ALL implement an onPress prop - // that will default to the message touch gesture. Luckily, Text with onPress - // plays nice with the message swipe gesture. - let secondMessage; - if (textMessageMarkdown.markdownHasPressable) { - secondMessage = ( - - - {text} - - - ); - } - const message = ( @@ -138,7 +114,6 @@ {text} - {secondMessage} diff --git a/native/markdown/markdown-spoiler.react.js b/native/markdown/markdown-spoiler.react.js --- a/native/markdown/markdown-spoiler.react.js +++ b/native/markdown/markdown-spoiler.react.js @@ -7,6 +7,7 @@ import type { ReactElement } from 'lib/shared/markdown'; import { TextMessageMarkdownContext } from '../chat/text-message-markdown-context'; +import GestureTouchableOpacity from '../components/gesture-touchable-opacity.react'; import { useStyles } from '../themes/colors'; import { MarkdownContext } from './markdown-context'; @@ -69,15 +70,67 @@ parsedSpoilerIdentifier, ]); - const memoizedSpoiler = React.useMemo(() => { - return ( - - {text} - - ); - }, [onSpoilerClick, styleBasedOnSpoilerState, text]); - - return memoizedSpoiler; + // The idea is to break down the spoiler text into individual words, + // and then encompass each word within a Text component, and + // each text component within a GestureTouchableOpacity. + // This is a way we can resolve the issue of long spoilers + // breaking the layout of the message, as the spoiler would force + // itself onto a new line if it exceeded the width of a text + // message. + + // The conditional for {index !== arrayOfSpoilerText.length - 1 ? ' ' : ''} + // is a way to not add a space after the last word in the spoiler text. + + // Sample text with nested markdown: + // Hey this is a spoiler **and this is bold text** + // This is converted to: + // [ + // "Hey this is a spoiler ", + // and this is bold text + // ] + + // We split this ReactElement into an array of individual ReactElements + // and then map over each individual ReactElement and return it + // within a GestureTouchableOpacity. + + const componentsArray: ReactElement[] = React.useMemo(() => { + return React.Children.map(text, (child, index) => { + // If the element is a string, we split it into an array of words + // and wrap each word in a Text within a GestureTouchableOpacity + if (typeof child === 'string') { + const words = child.split(' '); + return words.map((word, wordIndex) => { + return ( + + + {word} + {wordIndex !== words.length - 1 ? ' ' : ''} + + + ); + }); + } + // If it's a nested ReactElement, + // we return it within a GestureTouchableOpacity. + // We preserve the structure of the nested ReactElement + // since we don't want to break the markdown. + return ( + + + {child} + + + ); + }); + }, [text, onSpoilerClick, styleBasedOnSpoilerState]); + + return componentsArray; } const unboundStyles = { diff --git a/native/markdown/rules.react.js b/native/markdown/rules.react.js --- a/native/markdown/rules.react.js +++ b/native/markdown/rules.react.js @@ -12,7 +12,6 @@ import { useSelector } from '../redux/redux-utils'; import MarkdownLink from './markdown-link.react'; -import MarkdownParagraph from './markdown-paragraph.react'; import MarkdownSpoiler from './markdown-spoiler.react'; import { getMarkdownStyles } from './styles'; @@ -98,9 +97,9 @@ output: SharedMarkdown.Output, state: SharedMarkdown.State, ) => ( - + {output(node.content, state)} - + ), }, // This is the leaf node in the AST returned by the parse phase