1
1
import { useNavigation } from '@react-navigation/native' ;
2
2
import { useCallback , useEffect } from 'react' ;
3
3
import Parser from '@libs/Parser' ;
4
+ import CONST from '@src/CONST' ;
4
5
import type UseHtmlPaste from './types' ;
5
6
6
- const insertByCommand = ( text : string ) => {
7
- document . execCommand ( 'insertText' , false , text ) ;
8
- } ;
7
+ const insertAtCaret = ( target : HTMLElement , insertedText : string , maxLength : number ) => {
8
+ const currentText = target . textContent ?? '' ;
9
+
10
+ let availableLength = maxLength - currentText . length ;
11
+ if ( availableLength <= 0 ) {
12
+ return ;
13
+ }
14
+
15
+ let text = insertedText ;
9
16
10
- const insertAtCaret = ( target : HTMLElement , text : string ) => {
11
17
const selection = window . getSelection ( ) ;
12
18
if ( selection ?. rangeCount ) {
13
19
const range = selection . getRangeAt ( 0 ) ;
20
+ const selectedText = range . toString ( ) ;
21
+ availableLength -= selectedText . length ;
22
+ if ( availableLength <= 0 ) {
23
+ return ;
24
+ }
25
+ text = text . slice ( 0 , availableLength ) ;
14
26
range . deleteContents ( ) ;
27
+
15
28
const node = document . createTextNode ( text ) ;
16
29
range . insertNode ( node ) ;
17
30
@@ -22,40 +35,43 @@ const insertAtCaret = (target: HTMLElement, text: string) => {
22
35
23
36
// Dispatch input event to trigger Markdown Input to parse the new text
24
37
target . dispatchEvent ( new Event ( 'input' , { bubbles : true } ) ) ;
25
- } else {
26
- insertByCommand ( text ) ;
27
38
}
28
39
} ;
29
40
30
- const useHtmlPaste : UseHtmlPaste = ( textInputRef , preHtmlPasteCallback , removeListenerOnScreenBlur = false ) => {
41
+ const useHtmlPaste : UseHtmlPaste = ( textInputRef , preHtmlPasteCallback , removeListenerOnScreenBlur = false , maxLength = CONST . MAX_COMMENT_LENGTH + 1 ) => {
31
42
const navigation = useNavigation ( ) ;
32
43
33
44
/**
34
45
* Set pasted text to clipboard
35
46
* @param {String } text
36
47
*/
37
- const paste = useCallback ( ( text : string ) => {
38
- try {
39
- const textInputHTMLElement = textInputRef . current as HTMLElement ;
40
- if ( textInputHTMLElement ?. hasAttribute ( 'contenteditable' ) ) {
41
- insertAtCaret ( textInputHTMLElement , text ) ;
42
- } else {
43
- insertByCommand ( text ) ;
44
- }
48
+ const paste = useCallback (
49
+ ( text : string ) => {
50
+ try {
51
+ const textInputHTMLElement = textInputRef . current as HTMLElement ;
52
+ if ( textInputHTMLElement ?. hasAttribute ( 'contenteditable' ) ) {
53
+ insertAtCaret ( textInputHTMLElement , text , maxLength ) ;
54
+ } else {
55
+ const htmlInput = textInputRef . current as unknown as HTMLInputElement ;
56
+ const availableLength = maxLength - ( htmlInput . value ?. length ?? 0 ) ;
57
+ htmlInput . setRangeText ( text . slice ( 0 , availableLength ) ) ;
58
+ }
45
59
46
- // Pointer will go out of sight when a large paragraph is pasted on the web. Refocusing the input keeps the cursor in view.
47
- // To avoid the keyboard toggle issue in mWeb if using blur() and focus() functions, we just need to dispatch the event to trigger the onFocus handler
48
- // We need to trigger the bubbled "focusin" event to make sure the onFocus handler is triggered
49
- textInputHTMLElement . dispatchEvent (
50
- new FocusEvent ( 'focusin' , {
51
- bubbles : true ,
52
- } ) ,
53
- ) ;
54
- // eslint-disable-next-line no-empty
55
- } catch ( e ) { }
56
- // We only need to set the callback once.
57
- // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
58
- } , [ ] ) ;
60
+ // Pointer will go out of sight when a large paragraph is pasted on the web. Refocusing the input keeps the cursor in view.
61
+ // To avoid the keyboard toggle issue in mWeb if using blur() and focus() functions, we just need to dispatch the event to trigger the onFocus handler
62
+ // We need to trigger the bubbled "focusin" event to make sure the onFocus handler is triggered
63
+ textInputHTMLElement . dispatchEvent (
64
+ new FocusEvent ( 'focusin' , {
65
+ bubbles : true ,
66
+ } ) ,
67
+ ) ;
68
+ // eslint-disable-next-line no-empty
69
+ } catch ( e ) { }
70
+ // We only need to set the callback once.
71
+ // eslint-disable-next-line react-compiler/react-compiler, react-hooks/exhaustive-deps
72
+ } ,
73
+ [ maxLength , textInputRef ] ,
74
+ ) ;
59
75
60
76
/**
61
77
* Manually place the pasted HTML into Composer
@@ -64,9 +80,9 @@ const useHtmlPaste: UseHtmlPaste = (textInputRef, preHtmlPasteCallback, removeLi
64
80
*/
65
81
const handlePastedHTML = useCallback (
66
82
( html : string ) => {
67
- paste ( Parser . htmlToMarkdown ( html ) ) ;
83
+ paste ( Parser . htmlToMarkdown ( html . slice ( 0 , maxLength ) ) ) ;
68
84
} ,
69
- [ paste ] ,
85
+ [ paste , maxLength ] ,
70
86
) ;
71
87
72
88
/**
0 commit comments