@@ -5,11 +5,14 @@ import FormattedText from './FormattedText';
55import DiscreeteDropdown from './DiscreeteDropdown' ;
66import PictureUploadAction from '../menu-items/PictureUploadAction' ;
77import { v4 as uuid } from 'uuid' ;
8+ import ImagesWithDelete from './ImageDisplay' ;
89
910function EditableText ( { id, text, rubric, isPartOf, links, fragment, setFragment, setHighlightedText, setSelectedText, backend, setLastUpdate} ) {
1011 const [ beingEdited , setBeingEdited ] = useState ( false ) ;
1112 const [ editedDocument , setEditedDocument ] = useState ( ) ;
1213 const [ editedText , setEditedText ] = useState ( ) ;
14+ const [ showDeleteModal , setShowDeleteModal ] = useState ( false ) ;
15+ const [ deleteTarget , setDeleteTarget ] = useState ( { src : '' , alt : '' , internal : false , name : '' } ) ;
1316 const PASSAGE = new RegExp ( `\\{${ rubric } } ?([^{]*)` ) ;
1417
1518 let parsePassage = ( rawText ) => ( rubric )
@@ -79,16 +82,85 @@ function EditableText({id, text, rubric, isPartOf, links, fragment, setFragment,
7982 . catch ( console . error ) ;
8083 } ;
8184
85+ const imageRegex = / ! \[ ( [ ^ \] ] * ) \] \( ( [ ^ ) ] + ) \) / g;
86+ let images = [ ] ;
87+ let textWithoutInternalImages = text || '' ;
88+ let match ;
89+
90+ while ( ( match = imageRegex . exec ( text || '' ) ) !== null ) {
91+ const alt = match [ 1 ] ;
92+ const src = match [ 2 ] ;
93+ const isInternal = src . includes ( `/${ id } /` ) ;
94+ if ( isInternal ) {
95+ images . push ( { alt, src } ) ;
96+ const esc = s => s . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
97+ const mdRx = new RegExp ( `!\\[${ esc ( alt ) } \\]\\(${ esc ( src ) } \\)` , 'g' ) ;
98+ textWithoutInternalImages = textWithoutInternalImages . replace ( mdRx , '' ) ;
99+ }
100+ }
101+ textWithoutInternalImages = textWithoutInternalImages . replace ( / \n { 2 , } / g, '\n\n' ) . trim ( ) ;
102+
103+ const confirmDelete = ( ) => {
104+ const { src, alt, internal, name } = deleteTarget ;
105+ const esc = s => s . replace ( / [ . * + ? ^ $ { } ( ) | [ \] \\ ] / g, '\\$&' ) ;
106+ const mdRx = new RegExp ( `!\\[${ esc ( alt ) } \\]\\(${ esc ( src ) } \\)` , 'g' ) ;
107+ const clean = t => ( t || '' ) . replace ( mdRx , '' ) . replace ( / \n { 2 , } / g, '\n\n' ) . trim ( ) ;
108+ if ( internal ) {
109+ backend . deleteAttachment ( id , name , res => {
110+ if ( ! res . ok ) return ;
111+ backend . getDocument ( id ) . then ( doc => {
112+ const cleaned = clean ( doc . text ) ;
113+ backend . putDocument ( { ...doc , text : cleaned } ) . then ( r => {
114+ setEditedText ( cleaned ) ;
115+ setEditedDocument ( { ...doc , text : cleaned , _rev : r . rev } ) ;
116+ setLastUpdate ( r . rev ) ;
117+ setShowDeleteModal ( false ) ;
118+ } ) ;
119+ } ) ;
120+ } ) ;
121+ } else {
122+ const cleaned = clean ( editedText ) ;
123+ setEditedText ( cleaned ) ;
124+ setEditedDocument ( p => ( { ...p , text : cleaned } ) ) ;
125+ setShowDeleteModal ( false ) ;
126+ }
127+ } ;
128+
82129 if ( ! beingEdited ) return (
83130 < div className = "editable content position-relative" title = "Edit content..." >
84131 < div className = "formatted-text" onClick = { handleClick } >
85132 < FormattedText { ...{ setHighlightedText, setSelectedText} } >
86- { text || ' ' }
133+ { textWithoutInternalImages || ' ' }
87134 </ FormattedText >
135+ < ImagesWithDelete
136+ id = { id }
137+ images = { images }
138+ setDeleteTarget = { setDeleteTarget }
139+ setShowDeleteModal = { setShowDeleteModal }
140+ />
88141 </ div >
89142 < DiscreeteDropdown >
90143 < PictureUploadAction { ... { id, backend, handleImageUrl} } />
91144 </ DiscreeteDropdown >
145+ { showDeleteModal && (
146+ < div className = "modal fade show d-block" tabIndex = "-1" role = "dialog" >
147+ < div className = "modal-dialog" role = "document" >
148+ < div className = "modal-content" >
149+ < div className = "modal-header" >
150+ < h5 className = "modal-title" > Confirm deletion</ h5 >
151+ < button type = "button" className = "btn-close" onClick = { ( ) => setShowDeleteModal ( false ) } />
152+ </ div >
153+ < div className = "modal-body" >
154+ < p > Delete image { deleteTarget . internal ? `"${ deleteTarget . name } "` : 'external' } ?</ p >
155+ </ div >
156+ < div className = "modal-footer" >
157+ < button type = "button" className = "btn btn-secondary" onClick = { ( ) => setShowDeleteModal ( false ) } > Cancel</ button >
158+ < button type = "button" className = "btn btn-danger" onClick = { confirmDelete } > Delete</ button >
159+ </ div >
160+ </ div >
161+ </ div >
162+ </ div >
163+ ) }
92164 </ div >
93165 ) ;
94166 return (
0 commit comments