1
- import type { MarkdownTextInputProps } from '@expensify/react-native-live-markdown' ;
1
+ import type { MarkdownRange , MarkdownTextInputProps } from '@expensify/react-native-live-markdown' ;
2
2
import { MarkdownTextInput , parseExpensiMark } from '@expensify/react-native-live-markdown' ;
3
3
import type { ForwardedRef } from 'react' ;
4
- import React from 'react' ;
4
+ import React , { forwardRef , useCallback } from 'react' ;
5
5
import Animated from 'react-native-reanimated' ;
6
+ import useCurrentUserPersonalDetails from '@hooks/useCurrentUserPersonalDetails' ;
6
7
import useTheme from '@hooks/useTheme' ;
7
8
import CONST from '@src/CONST' ;
8
9
@@ -11,25 +12,70 @@ const AnimatedMarkdownTextInput = Animated.createAnimatedComponent(MarkdownTextI
11
12
12
13
type AnimatedMarkdownTextInputRef = typeof AnimatedMarkdownTextInput & MarkdownTextInput & HTMLInputElement ;
13
14
14
- type RNMarkdownTextInputProps = Omit < MarkdownTextInputProps , 'parser' > ;
15
+ // Make the parser prop optional for this component because we are always defaulting to `parseExpensiMark`
16
+ type RNMarkdownTextInputWithRefProps = Omit < MarkdownTextInputProps , 'parser' > & {
17
+ parser ?: MarkdownTextInputProps [ 'parser' ] ;
18
+ } ;
15
19
16
- function RNMarkdownTextInputWithRef ( { maxLength, ...props } : RNMarkdownTextInputProps , ref : ForwardedRef < AnimatedMarkdownTextInputRef > ) {
20
+ function decorateRangesWithCurrentUser ( ranges : MarkdownRange [ ] , text : string , currentUser : string ) : MarkdownRange [ ] {
21
+ 'worklet' ;
22
+
23
+ return ranges . map ( ( range ) => {
24
+ if ( range . type === 'mention-user' ) {
25
+ const mentionText = text . slice ( range . start , range . start + range . length ) ;
26
+ const isCurrentUser = mentionText === `@${ currentUser } ` ;
27
+ if ( isCurrentUser ) {
28
+ return {
29
+ ...range ,
30
+ type : 'mention-here' ,
31
+ } ;
32
+ }
33
+ }
34
+
35
+ return range ;
36
+ } ) ;
37
+ }
38
+
39
+ function RNMarkdownTextInputWithRef ( { maxLength, ...props } : RNMarkdownTextInputWithRefProps , ref : ForwardedRef < AnimatedMarkdownTextInputRef > ) {
17
40
const theme = useTheme ( ) ;
41
+ const currentUserPersonalDetails = useCurrentUserPersonalDetails ( ) ;
42
+
43
+ const { parser, ...restProps } = props ;
44
+ const currentUserLogin = currentUserPersonalDetails . login ;
45
+
46
+ // We accept parser passed down as an argument or use expensiMark
47
+ const parserFunction = useCallback (
48
+ ( text : string ) => {
49
+ 'worklet' ;
50
+
51
+ if ( parser ) {
52
+ return parser ( text ) ;
53
+ }
54
+
55
+ const parsedMentions = parseExpensiMark ( text ) ;
56
+ if ( ! currentUserLogin ) {
57
+ return parsedMentions ;
58
+ }
59
+
60
+ return decorateRangesWithCurrentUser ( parsedMentions , text , currentUserLogin ) ;
61
+ } ,
62
+ [ currentUserLogin , parser ] ,
63
+ ) ;
18
64
19
65
return (
20
66
< AnimatedMarkdownTextInput
21
67
allowFontScaling = { false }
22
68
textBreakStrategy = "simple"
23
69
keyboardAppearance = { theme . colorScheme }
24
- parser = { parseExpensiMark }
70
+ parser = { parserFunction }
25
71
ref = { ( refHandle ) => {
26
72
if ( typeof ref !== 'function' ) {
27
73
return ;
28
74
}
29
75
ref ( refHandle as AnimatedMarkdownTextInputRef ) ;
30
76
} }
31
77
// eslint-disable-next-line
32
- { ...props }
78
+ { ...restProps }
33
79
/**
34
80
* If maxLength is not set, we should set the it to CONST.MAX_COMMENT_LENGTH + 1, to avoid parsing markdown for large text
35
81
*/
@@ -40,5 +86,6 @@ function RNMarkdownTextInputWithRef({maxLength, ...props}: RNMarkdownTextInputPr
40
86
41
87
RNMarkdownTextInputWithRef . displayName = 'RNTextInputWithRef' ;
42
88
43
- export default React . forwardRef ( RNMarkdownTextInputWithRef ) ;
89
+ export default forwardRef ( RNMarkdownTextInputWithRef ) ;
90
+ export { decorateRangesWithCurrentUser } ;
44
91
export type { AnimatedMarkdownTextInputRef } ;
0 commit comments