Changeset View
Changeset View
Standalone View
Standalone View
native/markdown/markdown-spoiler.react.js
// @flow | // @flow | ||||
import invariant from 'invariant'; | |||||
import * as React from 'react'; | import * as React from 'react'; | ||||
import { Text } from 'react-native'; | import { Text } from 'react-native'; | ||||
import type { ReactElement } from 'lib/shared/markdown'; | import type { ReactElement } from 'lib/shared/markdown'; | ||||
import { TextMessageMarkdownContext } from '../chat/text-message-markdown-context'; | |||||
import { useStyles } from '../themes/colors'; | import { useStyles } from '../themes/colors'; | ||||
import { MarkdownContext } from './markdown-context'; | |||||
type MarkdownSpoilerProps = { | type MarkdownSpoilerProps = { | ||||
+spoilerIdentifier: string | number | void, | |||||
+text: ReactElement, | +text: ReactElement, | ||||
+children?: React.Node, | +children?: React.Node, | ||||
}; | }; | ||||
function MarkdownSpoiler(props: MarkdownSpoilerProps): React.Node { | function MarkdownSpoiler(props: MarkdownSpoilerProps): React.Node { | ||||
const [isRevealed, setIsRevealed] = React.useState(false); | const markdownContext = React.useContext(MarkdownContext); | ||||
invariant(markdownContext, 'MarkdownContext should be set'); | |||||
const textMessageMarkdownContext = React.useContext( | |||||
TextMessageMarkdownContext, | |||||
); | |||||
const styles = useStyles(unboundStyles); | const styles = useStyles(unboundStyles); | ||||
const { text } = props; | |||||
const { text, spoilerIdentifier } = props; | |||||
const { spoilerRevealed, setSpoilerRevealed } = markdownContext; | |||||
const messageKey = textMessageMarkdownContext?.messageKey; | |||||
const parsedSpoilerIdentifier = spoilerIdentifier | |||||
? parseInt(spoilerIdentifier) | |||||
: -1; | |||||
const isRevealed = | |||||
(messageKey && spoilerRevealed[messageKey]?.[parsedSpoilerIdentifier]) ?? | |||||
false; | |||||
const styleBasedOnSpoilerState = React.useMemo(() => { | |||||
if (isRevealed) { | |||||
return null; | |||||
} | |||||
return styles.spoilerHidden; | |||||
}, [isRevealed, styles.spoilerHidden]); | |||||
const onSpoilerClick = React.useCallback(() => { | const onSpoilerClick = React.useCallback(() => { | ||||
setIsRevealed(true); | if (isRevealed) { | ||||
}, []); | return; | ||||
} | |||||
if (messageKey && parsedSpoilerIdentifier !== -1) { | |||||
setSpoilerRevealed({ | |||||
...spoilerRevealed, | |||||
[messageKey]: { | |||||
...spoilerRevealed[messageKey], | |||||
[parsedSpoilerIdentifier]: true, | |||||
}, | |||||
}); | |||||
} | |||||
}, [ | |||||
isRevealed, | |||||
spoilerRevealed, | |||||
setSpoilerRevealed, | |||||
messageKey, | |||||
parsedSpoilerIdentifier, | |||||
]); | |||||
const memoizedSpoiler = React.useMemo(() => { | const memoizedSpoiler = React.useMemo(() => { | ||||
return ( | return ( | ||||
<Text | <Text onPress={onSpoilerClick} style={styleBasedOnSpoilerState}> | ||||
onPress={onSpoilerClick} | |||||
style={!isRevealed ? styles.spoilerHidden : null} | |||||
> | |||||
{text} | {text} | ||||
</Text> | </Text> | ||||
); | ); | ||||
}, [onSpoilerClick, isRevealed, styles.spoilerHidden, text]); | }, [onSpoilerClick, styleBasedOnSpoilerState, text]); | ||||
return memoizedSpoiler; | return memoizedSpoiler; | ||||
} | } | ||||
const unboundStyles = { | const unboundStyles = { | ||||
spoilerHidden: { | spoilerHidden: { | ||||
color: 'spoiler', | color: 'spoiler', | ||||
backgroundColor: 'spoiler', | backgroundColor: 'spoiler', | ||||
}, | }, | ||||
}; | }; | ||||
export default MarkdownSpoiler; | export default MarkdownSpoiler; |