2020-05-02 17:41:07 +02:00
import * as React from 'react' ;
import { useState , useEffect , useCallback , useRef } from 'react' ;
import TinyMCE from './NoteBody/TinyMCE/TinyMCE' ;
2020-06-06 17:00:20 +02:00
import CodeMirror from './NoteBody/CodeMirror/CodeMirror' ;
2020-05-02 17:41:07 +02:00
import { connect } from 'react-redux' ;
import MultiNoteActions from '../MultiNoteActions' ;
import { htmlToMarkdown , formNoteToNote } from './utils' ;
import useSearchMarkers from './utils/useSearchMarkers' ;
import useNoteSearchBar from './utils/useNoteSearchBar' ;
import useMessageHandler from './utils/useMessageHandler' ;
import useWindowCommandHandler from './utils/useWindowCommandHandler' ;
import useDropHandler from './utils/useDropHandler' ;
import useMarkupToHtml from './utils/useMarkupToHtml' ;
import useFormNote , { OnLoadEvent } from './utils/useFormNote' ;
2020-09-15 15:01:07 +02:00
import useFolder from './utils/useFolder' ;
2020-05-02 17:41:07 +02:00
import styles_ from './styles' ;
2020-05-04 19:37:22 +02:00
import { NoteEditorProps , FormNote , ScrollOptions , ScrollOptionTypes , OnChangeEvent , NoteBodyEditorProps } from './utils/types' ;
2020-11-07 17:59:37 +02:00
import ResourceEditWatcher from '@joplin/lib/services/ResourceEditWatcher/index' ;
import CommandService from '@joplin/lib/services/CommandService' ;
2020-09-15 15:01:07 +02:00
import ToolbarButton from '../ToolbarButton/ToolbarButton' ;
import Button , { ButtonLevel } from '../Button/Button' ;
2020-11-07 17:59:37 +02:00
import eventManager from '@joplin/lib/eventManager' ;
2020-10-09 19:35:46 +02:00
import { AppState } from '../../app' ;
2020-11-07 17:59:37 +02:00
import ToolbarButtonUtils from '@joplin/lib/services/commands/ToolbarButtonUtils' ;
import { _ } from '@joplin/lib/locale' ;
2020-10-20 00:24:40 +02:00
import TagList from '../TagList' ;
2020-11-05 18:58:23 +02:00
import NoteTitleBar from './NoteTitle/NoteTitleBar' ;
2020-11-07 17:59:37 +02:00
import markupLanguageUtils from '@joplin/lib/markupLanguageUtils' ;
2020-11-05 18:58:23 +02:00
import usePrevious from '../hooks/usePrevious' ;
2020-11-07 17:59:37 +02:00
import Setting from '@joplin/lib/models/Setting' ;
2020-11-13 19:09:28 +02:00
import stateToWhenClauseContext from '../../services/commands/stateToWhenClauseContext' ;
2020-11-16 13:03:44 +02:00
import ExternalEditWatcher from '@joplin/lib/services/ExternalEditWatcher' ;
2020-05-02 17:41:07 +02:00
2020-11-07 17:59:37 +02:00
const { themeStyle } = require ( '@joplin/lib/theme' ) ;
const { substrWithEllipsis } = require ( '@joplin/lib/string-utils' ) ;
2020-05-02 17:41:07 +02:00
const NoteSearchBar = require ( '../NoteSearchBar.min.js' ) ;
2020-11-07 17:59:37 +02:00
const { reg } = require ( '@joplin/lib/registry.js' ) ;
const Note = require ( '@joplin/lib/models/Note.js' ) ;
2020-11-17 13:50:46 +02:00
const Folder = require ( '@joplin/lib/models/Folder.js' ) ;
2020-10-09 19:35:46 +02:00
const bridge = require ( 'electron' ) . remote . require ( './bridge' ) . default ;
2020-05-02 17:41:07 +02:00
const NoteRevisionViewer = require ( '../NoteRevisionViewer.min' ) ;
2020-07-03 23:32:39 +02:00
const commands = [
require ( './commands/showRevisions' ) ,
] ;
2020-10-09 19:35:46 +02:00
const toolbarButtonUtils = new ToolbarButtonUtils ( CommandService . instance ( ) ) ;
2020-05-04 19:37:22 +02:00
function NoteEditor ( props : NoteEditorProps ) {
2020-05-02 17:41:07 +02:00
const [ showRevisions , setShowRevisions ] = useState ( false ) ;
const [ titleHasBeenManuallyChanged , setTitleHasBeenManuallyChanged ] = useState ( false ) ;
const [ scrollWhenReady , setScrollWhenReady ] = useState < ScrollOptions > ( null ) ;
const editorRef = useRef < any > ( ) ;
const titleInputRef = useRef < any > ( ) ;
const isMountedRef = useRef ( true ) ;
const noteSearchBarRef = useRef ( null ) ;
2020-11-12 21:13:28 +02:00
const formNote_beforeLoad = useCallback ( async ( event : OnLoadEvent ) = > {
2020-05-02 17:41:07 +02:00
await saveNoteIfWillChange ( event . formNote ) ;
setShowRevisions ( false ) ;
} , [ ] ) ;
const formNote_afterLoad = useCallback ( async ( ) = > {
setTitleHasBeenManuallyChanged ( false ) ;
} , [ ] ) ;
const { formNote , setFormNote , isNewNote , resourceInfos } = useFormNote ( {
syncStarted : props.syncStarted ,
noteId : props.noteId ,
isProvisional : props.isProvisional ,
titleInputRef : titleInputRef ,
editorRef : editorRef ,
onBeforeLoad : formNote_beforeLoad ,
onAfterLoad : formNote_afterLoad ,
} ) ;
const formNoteRef = useRef < FormNote > ( ) ;
formNoteRef . current = { . . . formNote } ;
2020-09-15 15:01:07 +02:00
const formNoteFolder = useFolder ( { folderId : formNote.parent_id } ) ;
2020-05-02 17:41:07 +02:00
const {
localSearch ,
onChange : localSearch_change ,
onNext : localSearch_next ,
onPrevious : localSearch_previous ,
onClose : localSearch_close ,
setResultCount : setLocalSearchResultCount ,
showLocalSearch ,
setShowLocalSearch ,
searchMarkers : localSearchMarkerOptions ,
} = useNoteSearchBar ( ) ;
// If the note has been modified in another editor, wait for it to be saved
// before loading it in this editor.
// const waitingToSaveNote = props.noteId && formNote.id !== props.noteId && props.editorNoteStatuses[props.noteId] === 'saving';
const styles = styles_ ( props ) ;
function scheduleSaveNote ( formNote : FormNote ) {
if ( ! formNote . saveActionQueue ) throw new Error ( 'saveActionQueue is not set!!' ) ; // Sanity check
2020-05-20 01:51:00 +02:00
// reg.logger().debug('Scheduling...', formNote);
2020-05-02 17:41:07 +02:00
const makeAction = ( formNote : FormNote ) = > {
return async function ( ) {
const note = await formNoteToNote ( formNote ) ;
reg . logger ( ) . debug ( 'Saving note...' , note ) ;
2020-11-12 21:13:28 +02:00
const savedNote : any = await Note . save ( note ) ;
2020-05-02 17:41:07 +02:00
setFormNote ( ( prev : FormNote ) = > {
return { . . . prev , user_updated_time : savedNote.user_updated_time } ;
} ) ;
ExternalEditWatcher . instance ( ) . updateNoteFile ( savedNote ) ;
props . dispatch ( {
type : 'EDITOR_NOTE_STATUS_REMOVE' ,
id : formNote.id ,
} ) ;
2020-10-09 19:35:46 +02:00
eventManager . emit ( 'noteContentChange' , { note : savedNote } ) ;
2020-05-02 17:41:07 +02:00
} ;
} ;
formNote . saveActionQueue . push ( makeAction ( formNote ) ) ;
}
async function saveNoteIfWillChange ( formNote : FormNote ) {
if ( ! formNote . id || ! formNote . bodyWillChangeId ) return ;
const body = await editorRef . current . content ( ) ;
scheduleSaveNote ( {
. . . formNote ,
body : body ,
bodyWillChangeId : 0 ,
bodyChangeId : 0 ,
} ) ;
}
async function saveNoteAndWait ( formNote : FormNote ) {
saveNoteIfWillChange ( formNote ) ;
return formNote . saveActionQueue . waitForAllDone ( ) ;
}
2020-10-21 01:23:55 +02:00
const markupToHtml = useMarkupToHtml ( {
themeId : props.themeId ,
customCss : props.customCss ,
plugins : props.plugins ,
} ) ;
2020-05-02 17:41:07 +02:00
const allAssets = useCallback ( async ( markupLanguage : number ) : Promise < any [ ] > = > {
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( props . themeId ) ;
2020-05-02 17:41:07 +02:00
const markupToHtml = markupLanguageUtils . newMarkupToHtml ( {
resourceBaseUrl : ` file:// ${ Setting . value ( 'resourceDir' ) } / ` ,
} ) ;
return markupToHtml . allAssets ( markupLanguage , theme ) ;
2020-09-15 15:01:07 +02:00
} , [ props . themeId ] ) ;
2020-05-02 17:41:07 +02:00
const handleProvisionalFlag = useCallback ( ( ) = > {
if ( props . isProvisional ) {
props . dispatch ( {
type : 'NOTE_PROVISIONAL_FLAG_CLEAR' ,
id : formNote.id ,
} ) ;
}
} , [ props . isProvisional , formNote . id ] ) ;
const previousNoteId = usePrevious ( formNote . id ) ;
useEffect ( ( ) = > {
if ( formNote . id === previousNoteId ) return ;
if ( editorRef . current ) {
editorRef . current . resetScroll ( ) ;
}
setScrollWhenReady ( {
type : props . selectedNoteHash ? ScrollOptionTypes.Hash : ScrollOptionTypes.Percent ,
value : props.selectedNoteHash ? props.selectedNoteHash : props.lastEditorScrollPercents [ props . noteId ] || 0 ,
} ) ;
2020-05-30 18:49:29 +02:00
ResourceEditWatcher . instance ( ) . stopWatchingAll ( ) ;
2020-05-02 17:41:07 +02:00
} , [ formNote . id , previousNoteId ] ) ;
const onFieldChange = useCallback ( ( field : string , value : any , changeId = 0 ) = > {
if ( ! isMountedRef . current ) {
// When the component is unmounted, various actions can happen which can
// trigger onChange events, for example the textarea might be cleared.
// We need to ignore these events, otherwise the note is going to be saved
// with an invalid body.
reg . logger ( ) . debug ( 'Skipping change event because the component is unmounted' ) ;
return ;
}
handleProvisionalFlag ( ) ;
const change = field === 'body' ? {
body : value ,
} : {
title : value ,
} ;
const newNote = {
. . . formNote ,
. . . change ,
bodyWillChangeId : 0 ,
bodyChangeId : 0 ,
hasChanged : true ,
} ;
if ( field === 'title' ) {
setTitleHasBeenManuallyChanged ( true ) ;
}
if ( isNewNote && ! titleHasBeenManuallyChanged && field === 'body' ) {
// TODO: Handle HTML/Markdown format
newNote . title = Note . defaultTitle ( value ) ;
}
if ( changeId !== null && field === 'body' && formNote . bodyWillChangeId !== changeId ) {
// Note was changed, but another note was loaded before save - skipping
// The previously loaded note, that was modified, will be saved via saveNoteIfWillChange()
} else {
setFormNote ( newNote ) ;
scheduleSaveNote ( newNote ) ;
}
} , [ handleProvisionalFlag , formNote , isNewNote , titleHasBeenManuallyChanged ] ) ;
2020-11-14 02:02:17 +02:00
useWindowCommandHandler ( {
dispatch : props.dispatch ,
formNote ,
setShowLocalSearch ,
noteSearchBarRef ,
editorRef ,
titleInputRef ,
saveNoteAndWait ,
setFormNote ,
} ) ;
2020-05-02 17:41:07 +02:00
const onDrop = useDropHandler ( { editorRef } ) ;
const onBodyChange = useCallback ( ( event : OnChangeEvent ) = > onFieldChange ( 'body' , event . content , event . changeId ) , [ onFieldChange ] ) ;
const onTitleChange = useCallback ( ( event : any ) = > onFieldChange ( 'title' , event . target . value ) , [ onFieldChange ] ) ;
2020-11-05 18:58:23 +02:00
// const onTitleKeydown = useCallback((event:any) => {
// const keyCode = event.keyCode;
2020-05-02 17:41:07 +02:00
2020-11-05 18:58:23 +02:00
// if (keyCode === 9) {
// // TAB
// event.preventDefault();
2020-05-02 17:41:07 +02:00
2020-11-05 18:58:23 +02:00
// if (event.shiftKey) {
// CommandService.instance().execute('focusElement', 'noteList');
// } else {
// CommandService.instance().execute('focusElement', 'noteBody');
// }
// }
// }, [props.dispatch]);
2020-05-02 17:41:07 +02:00
const onBodyWillChange = useCallback ( ( event : any ) = > {
handleProvisionalFlag ( ) ;
2020-05-21 10:14:33 +02:00
setFormNote ( prev = > {
2020-05-02 17:41:07 +02:00
return {
. . . prev ,
bodyWillChangeId : event.changeId ,
hasChanged : true ,
} ;
} ) ;
props . dispatch ( {
type : 'EDITOR_NOTE_STATUS_SET' ,
id : formNote.id ,
status : 'saving' ,
} ) ;
} , [ formNote , handleProvisionalFlag ] ) ;
2020-05-03 19:44:49 +02:00
const onMessage = useMessageHandler ( scrollWhenReady , setScrollWhenReady , editorRef , setLocalSearchResultCount , props . dispatch , formNote ) ;
2020-05-02 17:41:07 +02:00
const introductionPostLinkClick = useCallback ( ( ) = > {
bridge ( ) . openExternal ( 'https://www.patreon.com/posts/34246624' ) ;
} , [ ] ) ;
const externalEditWatcher_noteChange = useCallback ( ( event ) = > {
if ( event . id === formNote . id ) {
const newFormNote = {
. . . formNote ,
title : event.note.title ,
body : event.note.body ,
} ;
setFormNote ( newFormNote ) ;
}
} , [ formNote ] ) ;
const onNotePropertyChange = useCallback ( ( event ) = > {
2020-05-21 10:14:33 +02:00
setFormNote ( formNote = > {
2020-05-02 17:41:07 +02:00
if ( formNote . id !== event . note . id ) return formNote ;
const newFormNote : FormNote = { . . . formNote } ;
for ( const key in event . note ) {
if ( key === 'id' ) continue ;
( newFormNote as any ) [ key ] = event . note [ key ] ;
}
return newFormNote ;
} ) ;
} , [ ] ) ;
useEffect ( ( ) = > {
eventManager . on ( 'alarmChange' , onNotePropertyChange ) ;
ExternalEditWatcher . instance ( ) . on ( 'noteChange' , externalEditWatcher_noteChange ) ;
return ( ) = > {
eventManager . off ( 'alarmChange' , onNotePropertyChange ) ;
ExternalEditWatcher . instance ( ) . off ( 'noteChange' , externalEditWatcher_noteChange ) ;
} ;
} , [ externalEditWatcher_noteChange , onNotePropertyChange ] ) ;
2020-07-03 23:32:39 +02:00
useEffect ( ( ) = > {
const dependencies = {
setShowRevisions ,
2020-05-02 17:41:07 +02:00
} ;
2020-07-03 23:32:39 +02:00
CommandService . instance ( ) . componentRegisterCommands ( dependencies , commands ) ;
2020-05-02 17:41:07 +02:00
2020-07-03 23:32:39 +02:00
return ( ) = > {
CommandService . instance ( ) . componentUnregisterCommands ( commands ) ;
} ;
} , [ setShowRevisions ] ) ;
2020-05-02 17:41:07 +02:00
const onScroll = useCallback ( ( event : any ) = > {
props . dispatch ( {
type : 'EDITOR_SCROLL_PERCENT_SET' ,
noteId : formNote.id ,
percent : event.percent ,
} ) ;
} , [ props . dispatch , formNote ] ) ;
2020-11-12 21:13:28 +02:00
function renderNoNotes ( rootStyle : any ) {
2020-05-02 17:41:07 +02:00
const emptyDivStyle = Object . assign (
{
backgroundColor : 'black' ,
opacity : 0.1 ,
} ,
rootStyle
) ;
return < div style = { emptyDivStyle } > < / div > ;
}
2020-09-15 15:01:07 +02:00
function renderTagButton() {
return < ToolbarButton
themeId = { props . themeId }
2020-10-09 19:35:46 +02:00
toolbarButtonInfo = { props . setTagsToolbarButtonInfo }
2020-09-15 15:01:07 +02:00
/ > ;
}
2020-05-17 15:01:42 +02:00
function renderTagBar() {
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( props . themeId ) ;
2020-09-29 12:33:22 +02:00
const noteIds = [ formNote . id ] ;
2020-10-18 22:52:10 +02:00
const instructions = < span onClick = { ( ) = > { CommandService . instance ( ) . execute ( 'setTags' , noteIds ) ; } } style = { { . . . theme . clickableTextStyle , whiteSpace : 'nowrap' } } > Click to add tags . . . < / span > ;
2020-09-29 12:33:22 +02:00
const tagList = props . selectedNoteTags . length ? < TagList items = { props . selectedNoteTags } / > : null ;
2020-09-15 15:01:07 +02:00
return (
2020-09-29 12:33:22 +02:00
< div style = { { paddingLeft : 8 , display : 'flex' , flexDirection : 'row' , alignItems : 'center' } } > { tagList } { instructions } < / div >
2020-09-15 15:01:07 +02:00
) ;
2020-05-17 15:01:42 +02:00
}
2020-09-06 14:07:00 +02:00
const searchMarkers = useSearchMarkers ( showLocalSearch , localSearchMarkerOptions , props . searches , props . selectedSearchId , props . highlightedWords ) ;
2020-05-02 17:41:07 +02:00
2020-11-12 21:13:28 +02:00
const editorProps : NoteBodyEditorProps = {
2020-05-02 17:41:07 +02:00
ref : editorRef ,
contentKey : formNote.id ,
style : styles.tinyMCE ,
onChange : onBodyChange ,
onWillChange : onBodyWillChange ,
onMessage : onMessage ,
content : formNote.body ,
contentMarkupLanguage : formNote.markup_language ,
contentOriginalCss : formNote.originalCss ,
resourceInfos : resourceInfos ,
htmlToMarkdown : htmlToMarkdown ,
markupToHtml : markupToHtml ,
allAssets : allAssets ,
disabled : false ,
2020-09-15 15:01:07 +02:00
themeId : props.themeId ,
2020-05-02 17:41:07 +02:00
dispatch : props.dispatch ,
2020-10-09 19:35:46 +02:00
noteToolbar : null ,
2020-05-02 17:41:07 +02:00
onScroll : onScroll ,
2020-07-23 00:13:23 +02:00
setLocalSearchResultCount : setLocalSearchResultCount ,
2020-05-02 17:41:07 +02:00
searchMarkers : searchMarkers ,
visiblePanes : props.noteVisiblePanes || [ 'editor' , 'viewer' ] ,
keyboardMode : Setting.value ( 'editor.keyboardMode' ) ,
2020-05-04 01:55:41 +02:00
locale : Setting.value ( 'locale' ) ,
2020-05-10 17:28:22 +02:00
onDrop : onDrop ,
2020-10-09 19:35:46 +02:00
noteToolbarButtonInfos : props.toolbarButtonInfos ,
plugins : props.plugins ,
2020-05-02 17:41:07 +02:00
} ;
let editor = null ;
if ( props . bodyEditor === 'TinyMCE' ) {
editor = < TinyMCE { ...editorProps } / > ;
2020-06-06 17:00:20 +02:00
} else if ( props . bodyEditor === 'CodeMirror' ) {
editor = < CodeMirror { ...editorProps } / > ;
2020-05-02 17:41:07 +02:00
} else {
throw new Error ( ` Invalid editor: ${ props . bodyEditor } ` ) ;
}
const wysiwygBanner = props . bodyEditor !== 'TinyMCE' ? null : (
2020-05-03 19:44:49 +02:00
< div style = { { . . . styles . warningBanner } } >
2020-10-16 19:52:17 +02:00
This is an experimental Rich Text editor for evaluation only . Please do not use with important notes as you may lose some data ! See the < a style = { styles . urlColor } onClick = { introductionPostLinkClick } href = "#" > introduction post < / a > for more information . To switch to the Markdown Editor please press the "Toggle editors" in the top right - hand corner .
2020-05-02 17:41:07 +02:00
< / div >
) ;
const noteRevisionViewer_onBack = useCallback ( ( ) = > {
setShowRevisions ( false ) ;
} , [ ] ) ;
if ( showRevisions ) {
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( props . themeId ) ;
2020-05-02 17:41:07 +02:00
2020-11-12 21:13:28 +02:00
const revStyle : any = {
2020-09-15 15:01:07 +02:00
// ...props.style,
2020-05-02 17:41:07 +02:00
display : 'inline-flex' ,
padding : theme.margin ,
verticalAlign : 'top' ,
boxSizing : 'border-box' ,
} ;
return (
< div style = { revStyle } >
< NoteRevisionViewer customCss = { props . customCss } noteId = { formNote . id } onBack = { noteRevisionViewer_onBack } / >
< / div >
) ;
}
if ( props . selectedNoteIds . length > 1 ) {
return < MultiNoteActions
2020-09-15 15:01:07 +02:00
themeId = { props . themeId }
2020-05-02 17:41:07 +02:00
selectedNoteIds = { props . selectedNoteIds }
notes = { props . notes }
dispatch = { props . dispatch }
watchedNoteFiles = { props . watchedNoteFiles }
2020-10-09 19:35:46 +02:00
plugins = { props . plugins }
2020-11-17 13:50:46 +02:00
inConflictFolder = { props . selectedFolderId === Folder . conflictFolderId ( ) }
2020-05-02 17:41:07 +02:00
/ > ;
}
function renderSearchBar() {
if ( ! showLocalSearch ) return false ;
2020-09-15 15:01:07 +02:00
const theme = themeStyle ( props . themeId ) ;
2020-05-02 17:41:07 +02:00
return (
< NoteSearchBar
ref = { noteSearchBarRef }
style = { {
display : 'flex' ,
height : 35 ,
borderTop : ` 1px solid ${ theme . dividerColor } ` ,
} }
query = { localSearch . query }
searching = { localSearch . searching }
resultCount = { localSearch . resultCount }
selectedIndex = { localSearch . selectedIndex }
onChange = { localSearch_change }
onNext = { localSearch_next }
onPrevious = { localSearch_previous }
onClose = { localSearch_close }
2020-09-06 17:28:23 +02:00
visiblePanes = { props . noteVisiblePanes }
2020-05-02 17:41:07 +02:00
/ >
) ;
}
2020-07-22 20:03:31 +02:00
function renderResourceWatchingNotification() {
if ( ! Object . keys ( props . watchedResources ) . length ) return null ;
const resourceTitles = Object . keys ( props . watchedResources ) . map ( id = > props . watchedResources [ id ] . title ) ;
return (
< div style = { styles . resourceWatchBanner } >
< p style = { styles . resourceWatchBannerLine } > { _ ( 'The following attachments are being watched for changes:' ) } < strong > { resourceTitles . join ( ', ' ) } < / strong > < / p >
< p style = { { . . . styles . resourceWatchBannerLine , marginBottom : 0 } } > { _ ( 'The attachments will no longer be watched when you switch to a different note.' ) } < / p >
< / div >
) ;
}
2020-09-15 15:01:07 +02:00
function renderSearchInfo() {
if ( formNoteFolder && [ 'Search' , 'Tag' , 'SmartFilter' ] . includes ( props . notesParentType ) ) {
return (
< div style = { { paddingTop : 10 , paddingBottom : 10 } } >
< Button
iconName = "icon-notebooks"
level = { ButtonLevel . Primary }
title = { _ ( 'In: %s' , substrWithEllipsis ( formNoteFolder . title , 0 , 100 ) ) }
onClick = { ( ) = > {
props . dispatch ( {
type : 'FOLDER_AND_NOTE_SELECT' ,
folderId : formNoteFolder.id ,
noteId : formNote.id ,
} ) ;
} }
/ >
< div style = { { flex : 1 } } > < / div >
< / div >
) ;
} else {
return null ;
}
}
2020-05-20 02:13:59 +02:00
if ( formNote . encryption_applied || ! formNote . id || ! props . noteId ) {
2020-05-02 17:41:07 +02:00
return renderNoNotes ( styles . root ) ;
}
return (
< div style = { styles . root } onDrop = { onDrop } >
< div style = { { display : 'flex' , flexDirection : 'column' , height : '100%' } } >
2020-07-22 20:03:31 +02:00
{ renderResourceWatchingNotification ( ) }
2020-11-05 18:58:23 +02:00
< NoteTitleBar
titleInputRef = { titleInputRef }
themeId = { props . themeId }
isProvisional = { props . isProvisional }
noteIsTodo = { formNote . is_todo }
noteTitle = { formNote . title }
noteUserUpdatedTime = { formNote . user_updated_time }
onTitleChange = { onTitleChange }
/ >
2020-09-15 15:01:07 +02:00
{ renderSearchInfo ( ) }
2020-05-02 17:41:07 +02:00
< div style = { { display : 'flex' , flex : 1 } } >
{ editor }
< / div >
< div style = { { display : 'flex' , flexDirection : 'row' , alignItems : 'center' } } >
{ renderSearchBar ( ) }
< / div >
2020-09-15 15:01:07 +02:00
< div style = { { display : 'flex' , flexDirection : 'row' , alignItems : 'center' , height : 40 } } >
{ renderTagButton ( ) }
{ renderTagBar ( ) }
< / div >
2020-05-03 19:44:49 +02:00
{ wysiwygBanner }
2020-05-02 17:41:07 +02:00
< / div >
< / div >
) ;
}
export {
NoteEditor as NoteEditorComponent ,
} ;
2020-10-09 19:35:46 +02:00
const mapStateToProps = ( state : AppState ) = > {
2020-05-02 17:41:07 +02:00
const noteId = state . selectedNoteIds . length === 1 ? state . selectedNoteIds [ 0 ] : null ;
2020-10-18 22:52:10 +02:00
const whenClauseContext = stateToWhenClauseContext ( state ) ;
2020-05-02 17:41:07 +02:00
return {
noteId : noteId ,
notes : state.notes ,
folders : state.folders ,
selectedNoteIds : state.selectedNoteIds ,
2020-11-17 13:50:46 +02:00
selectedFolderId : state.selectedFolderId ,
2020-05-02 17:41:07 +02:00
isProvisional : state.provisionalNoteIds.includes ( noteId ) ,
editorNoteStatuses : state.editorNoteStatuses ,
syncStarted : state.syncStarted ,
2020-09-15 15:01:07 +02:00
themeId : state.settings.theme ,
2020-05-02 17:41:07 +02:00
watchedNoteFiles : state.watchedNoteFiles ,
notesParentType : state.notesParentType ,
selectedNoteTags : state.selectedNoteTags ,
lastEditorScrollPercents : state.lastEditorScrollPercents ,
selectedNoteHash : state.selectedNoteHash ,
searches : state.searches ,
selectedSearchId : state.selectedSearchId ,
customCss : state.customCss ,
noteVisiblePanes : state.noteVisiblePanes ,
2020-07-22 20:03:31 +02:00
watchedResources : state.watchedResources ,
2020-09-06 14:07:00 +02:00
highlightedWords : state.highlightedWords ,
2020-10-09 19:35:46 +02:00
plugins : state.pluginService.plugins ,
2020-10-18 22:52:10 +02:00
toolbarButtonInfos : toolbarButtonUtils.commandsToToolbarButtons ( [
2020-10-09 19:35:46 +02:00
'historyBackward' ,
'historyForward' ,
'toggleEditors' ,
2020-10-10 14:32:30 +02:00
'toggleExternalEditing' ,
2020-10-18 22:52:10 +02:00
] , whenClauseContext ) ,
setTagsToolbarButtonInfo : toolbarButtonUtils.commandsToToolbarButtons ( [
2020-10-09 19:35:46 +02:00
'setTags' ,
2020-10-18 22:52:10 +02:00
] , whenClauseContext ) [ 0 ] ,
2020-05-02 17:41:07 +02:00
} ;
} ;
export default connect ( mapStateToProps ) ( NoteEditor ) ;