1- import { memo , useEffect , useMemo , useRef } from "react"
1+ import { memo , useCallback , useEffect , useMemo , useRef , useState } from "react"
22import { StyleSheet , View } from "react-native"
33import {
44 EnrichedMarkdownTextInput ,
55 type EnrichedMarkdownTextInputInstance ,
66 type MarkdownTextInputStyle ,
77} from "react-native-enriched-markdown"
88import { KeyboardAvoidingView } from "react-native-keyboard-controller"
9+ import { useSafeAreaInsets } from "react-native-safe-area-context"
910
11+ import {
12+ escapeMarkdownForPlainEditing ,
13+ normalizeNoteBody ,
14+ shouldEditAsPlainMarkdown ,
15+ } from "./markdownInputMode"
1016import { MarkdownToolbar } from "./MarkdownToolbar"
11- import { useSafeAreaInsets } from "react-native-safe-area-context"
1217
1318type NoteEditorBodyProps = {
1419 defaultBody : string
@@ -44,8 +49,25 @@ function NoteEditorBodyComponent({
4449 if ( __DEV__ ) {
4550 console . log ( "NoteEditorBody render" , JSON . stringify ( { defaultBody, noteId } ) )
4651 }
52+ const normalizedDefaultBody = useMemo (
53+ ( ) => normalizeNoteBody ( defaultBody ) ,
54+ [ defaultBody ] ,
55+ )
56+ const editAsPlainMarkdown = useMemo (
57+ ( ) => shouldEditAsPlainMarkdown ( normalizedDefaultBody ) ,
58+ [ normalizedDefaultBody ] ,
59+ )
60+ const editorDefaultValue = useMemo (
61+ ( ) =>
62+ editAsPlainMarkdown
63+ ? escapeMarkdownForPlainEditing ( normalizedDefaultBody )
64+ : normalizedDefaultBody ,
65+ [ editAsPlainMarkdown , normalizedDefaultBody ] ,
66+ )
4767 const insets = useSafeAreaInsets ( )
4868 const editorRef = useRef < EnrichedMarkdownTextInputInstance > ( null )
69+ const currentBodyRef = useRef ( normalizedDefaultBody )
70+ const [ selection , setSelection ] = useState ( { start : 0 , end : 0 } )
4971
5072 useEffect ( ( ) => {
5173 if ( __DEV__ ) {
@@ -57,6 +79,67 @@ function NoteEditorBodyComponent({
5779 }
5880 }
5981 } , [ noteId ] )
82+
83+ useEffect ( ( ) => {
84+ currentBodyRef . current = normalizedDefaultBody
85+ setSelection ( { start : 0 , end : 0 } )
86+ } , [ normalizedDefaultBody , noteId ] )
87+
88+ const handlePlainMarkdownChange = useCallback (
89+ ( nextBody : string ) => {
90+ const normalizedBody = normalizeNoteBody ( nextBody )
91+ currentBodyRef . current = normalizedBody
92+ onBodyChange ( normalizedBody )
93+ } ,
94+ [ onBodyChange ] ,
95+ )
96+
97+ const applyPlainMarkdownDelimiter = useCallback (
98+ ( delimiter : string ) => {
99+ const body = currentBodyRef . current
100+ const start = Math . max ( 0 , Math . min ( selection . start , body . length ) )
101+ const end = Math . max ( start , Math . min ( selection . end , body . length ) )
102+ const selectedText = body . slice ( start , end )
103+ const nextBody =
104+ body . slice ( 0 , start ) +
105+ delimiter +
106+ selectedText +
107+ delimiter +
108+ body . slice ( end )
109+ const nextSelection = selectedText
110+ ? {
111+ start : start + delimiter . length ,
112+ end : end + delimiter . length ,
113+ }
114+ : {
115+ start : start + delimiter . length ,
116+ end : start + delimiter . length ,
117+ }
118+
119+ currentBodyRef . current = nextBody
120+ onBodyChange ( nextBody )
121+ editorRef . current ?. setValue ( escapeMarkdownForPlainEditing ( nextBody ) )
122+ setSelection ( nextSelection )
123+ setTimeout ( ( ) => {
124+ editorRef . current ?. setSelection ( nextSelection . start , nextSelection . end )
125+ } , 0 )
126+ } ,
127+ [ onBodyChange , selection ] ,
128+ )
129+
130+ const plainMarkdownActions = useMemo (
131+ ( ) =>
132+ editAsPlainMarkdown
133+ ? {
134+ bold : ( ) => applyPlainMarkdownDelimiter ( "**" ) ,
135+ italic : ( ) => applyPlainMarkdownDelimiter ( "*" ) ,
136+ strikethrough : ( ) => applyPlainMarkdownDelimiter ( "~~" ) ,
137+ underline : ( ) => applyPlainMarkdownDelimiter ( "_" ) ,
138+ }
139+ : undefined ,
140+ [ applyPlainMarkdownDelimiter , editAsPlainMarkdown ] ,
141+ )
142+
60143 const markdownStyle : MarkdownTextInputStyle = useMemo (
61144 ( ) => ( {
62145 strong : { color : "#f8fafc" } ,
@@ -76,9 +159,13 @@ function NoteEditorBodyComponent({
76159 < EnrichedMarkdownTextInput
77160 key = { noteId }
78161 ref = { editorRef }
79- defaultValue = { defaultBody }
162+ defaultValue = { editorDefaultValue }
80163 markdownStyle = { markdownStyle }
81- onChangeMarkdown = { onBodyChange }
164+ onChangeMarkdown = { editAsPlainMarkdown ? undefined : onBodyChange }
165+ onChangeSelection = { setSelection }
166+ onChangeText = {
167+ editAsPlainMarkdown ? handlePlainMarkdownChange : undefined
168+ }
82169 placeholder = "Start writing..."
83170 placeholderTextColor = "#71717a"
84171 selectionColor = "#94a3b8"
@@ -87,6 +174,7 @@ function NoteEditorBodyComponent({
87174 < MarkdownToolbar
88175 bottomInset = { insets . bottom }
89176 editorRef = { editorRef }
177+ plainMarkdownActions = { plainMarkdownActions }
90178 styleState = { null }
91179 />
92180 </ View >
0 commit comments