Desktop: Fixed various bugs and regressions following note editor refactoring

Squashed commit of the following:

commit 5fde36f5c3fa7c7efbce6d81f48fe841c823e88c
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 18:43:20 2020 +0100

    Cannot fix for now

commit 251284db3c8b7da6db83f3e06fd19bfdc8b8dd3f
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 18:31:08 2020 +0100

    Fixed print to multiple PDFs logic

commit 00d9557996fb984b400fe650594150ae2681e03f
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 17:49:20 2020 +0100

    Fixed local search in TinyMCE

commit 46778bf0a79f3bba9ddbc27389fadce4f8944507
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 17:37:31 2020 +0100

    Restored note toolbar buttons

commit 3e623c98f0a1cf08bf7d0136f0c8982c5e1ddcd8
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 12:30:57 2020 +0100

    Various fixes and moved note toolbar to left of title

commit 790262fe9df5b08d4a619e5244d2906047b39855
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 11:21:11 2020 +0100

    Clean up

commit cea30f42e69014ecabda6fa5083199a1ba7b7510
Author: Laurent Cozic <laurent@cozic.net>
Date:   Sun May 3 11:08:23 2020 +0100

    Fixed note loading in TinyMCE
pull/3165/head
Laurent Cozic 2020-05-03 18:44:49 +01:00
parent f51873d877
commit 51732a5adb
16 changed files with 144 additions and 2496 deletions

View File

@ -3,7 +3,6 @@ const { bridge } = require('electron').remote.require('./bridge');
const InteropService = require('lib/services/InteropService');
const Setting = require('lib/models/Setting');
const Note = require('lib/models/Note.js');
const Folder = require('lib/models/Folder.js');
const { friendlySafeFilename } = require('lib/path-utils');
const md5 = require('md5');
const url = require('url');
@ -70,7 +69,10 @@ class InteropServiceHelper {
cleanup();
}
} else {
// TODO: it is crashing at this point
// TODO: it is crashing at this point :(
// Appears to be a Chromium bug: https://github.com/electron/electron/issues/19946
// Maybe can be fixed by doing everything from main process?
// i.e. creating a function `print()` that takes the `htmlFile` variable as input.
win.webContents.print(options, (success, reason) => {
// TODO: This is correct but broken in Electron 4. Need to upgrade to 5+
@ -105,31 +107,12 @@ class InteropServiceHelper {
return this.exportNoteTo_('printer', noteId, options);
}
static async defaultFilename(noteIds, fileExtension) {
if (!noteIds) {
return '';
}
const note = await Note.load(noteIds[0]);
static async defaultFilename(noteId, fileExtension) {
if (!noteId) return '';
const note = await Note.load(noteId);
// In a rare case the passed not will be null, use the id for filename
if (note === null) {
const filename = friendlySafeFilename(noteIds[0], 100);
return `${filename}.${fileExtension}`;
}
const folder = await Folder.load(note.parent_id);
const filename = friendlySafeFilename(note.title, 100);
// In a less rare case the folder will be null, just ignore it
if (folder === null) {
return `${filename}.${fileExtension}`;
}
const foldername = friendlySafeFilename(folder.title, 100);
// friendlySafeFilename assumes that the file extension is added after
return `${foldername} - ${filename}.${fileExtension}`;
const filename = friendlySafeFilename(note ? note.title : noteId, 100);
return `${filename}.${fileExtension}`;
}
static async export(dispatch, module, options = null) {
@ -138,9 +121,10 @@ class InteropServiceHelper {
let path = null;
if (module.target === 'file') {
const noteId = options.sourceNoteIds && options.sourceNoteIds.length ? options.sourceNoteIds[0] : null;
path = bridge().showSaveDialog({
filters: [{ name: module.description, extensions: module.fileExtensions }],
defaultPath: await this.defaultFilename(options.sourceNoteIds, module.fileExtensions[0]),
defaultPath: await this.defaultFilename(noteId, module.fileExtensions[0]),
});
} else {
path = bridge().showOpenDialog({

View File

@ -535,7 +535,7 @@ class MainScreenComponent extends React.Component {
if (noteIds.length === 1) {
path = bridge().showSaveDialog({
filters: [{ name: _('PDF File'), extensions: ['pdf'] }],
defaultPath: await InteropServiceHelper.defaultFilename(noteIds, 'pdf'),
defaultPath: await InteropServiceHelper.defaultFilename(noteIds[0], 'pdf'),
});
} else {
@ -548,14 +548,20 @@ class MainScreenComponent extends React.Component {
for (let i = 0; i < noteIds.length; i++) {
const note = await Note.load(noteIds[i]);
const folder = Folder.byId(this.props.folders, note.parent_id);
const pdfPath = (noteIds.length === 1) ? path :
await shim.fsDriver().findUniqueFilename(`${path}/${this.pdfFileName_(note, folder)}`);
let pdfPath = '';
if (noteIds.length === 1) {
pdfPath = path;
} else {
const n = await InteropServiceHelper.defaultFilename(note.id, 'pdf');
pdfPath = await shim.fsDriver().findUniqueFilename(`${path}/${n}`);
}
await this.printTo_('pdf', { path: pdfPath, noteId: note.id });
}
} catch (error) {
console.error(error);
bridge().showErrorMessageBox(error.message);
}
}
@ -852,7 +858,6 @@ class MainScreenComponent extends React.Component {
const shareNoteDialogOptions = this.state.shareNoteDialogOptions;
const bodyEditor = this.props.settingEditorCodeView ? 'AceEditor' : 'TinyMCE';
const noteTextComp = <NoteEditor bodyEditor={bodyEditor} style={styles.noteText} />;
return (
<div style={style}>
@ -870,7 +875,7 @@ class MainScreenComponent extends React.Component {
<VerticalResizer style={styles.verticalResizer} onDrag={this.sidebar_onDrag} />
<NoteList style={styles.noteList} />
<VerticalResizer style={styles.verticalResizer} onDrag={this.noteList_onDrag} />
{noteTextComp}
<NoteEditor bodyEditor={bodyEditor} style={styles.noteText} />
{pluginDialog}
</div>
);

View File

@ -251,6 +251,11 @@ function AceEditor(props: NoteBodyEditorProps, ref: any) {
editor.clearSelection();
editor.moveCursorTo(0, 0);
},
supportsCommand: (/* name:string*/) => {
// TODO: not implemented, currently only used for "search" command
// which is not directly supported by Ace Editor.
return false;
},
execCommand: async (cmd: EditorCommand) => {
if (!editor) return false;

View File

@ -13,7 +13,7 @@ function styles_(props:ToolbarProps) {
return buildStyle('AceEditorToolbar', props.theme, (/* theme:any*/) => {
return {
root: {
// marginTop: 4,
flex: 1,
marginBottom: 0,
},
};

View File

@ -1,24 +0,0 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const React = require('react');
const react_1 = require('react');
const PlainEditor = (props, ref) => {
const editorRef = react_1.useRef();
react_1.useImperativeHandle(ref, () => {
return {
content: () => '',
};
}, []);
react_1.useEffect(() => {
if (!editorRef.current) { return; }
editorRef.current.value = props.defaultEditorState.value;
}, [props.defaultEditorState]);
const onChange = react_1.useCallback((event) => {
props.onChange({ changeId: null, content: event.target.value });
}, [props.onWillChange, props.onChange]);
return (React.createElement('div', { style: props.style },
React.createElement('textarea', { ref: editorRef, style: { width: '100%', height: '100%' }, defaultValue: props.defaultEditorState.value, onChange: onChange }),
';'));
};
exports.default = react_1.forwardRef(PlainEditor);
// # sourceMappingURL=PlainEditor.js.map

View File

@ -145,6 +145,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
const markupToHtml = useRef(null);
markupToHtml.current = props.markupToHtml;
const lastOnChangeEventContent = useRef<string>('');
const rootIdRef = useRef<string>(`tinymce-${Date.now()}${Math.round(Math.random() * 10000)}`);
const editorRef = useRef<any>(null);
editorRef.current = editor;
@ -170,15 +172,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
if (nodeName === 'A' && (event.ctrlKey || event.metaKey)) {
const href = event.target.getAttribute('href');
// const joplinUrl = href.indexOf('joplin://') === 0 ? href : null;
// if (joplinUrl) {
// props.onMessage({
// name: 'openInternal',
// args: {
// url: joplinUrl,
// },
// });
if (href.indexOf('#') === 0) {
const anchorName = href.substr(1);
const anchor = editor.getDoc().getElementById(anchorName);
@ -188,12 +182,7 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
reg.logger().warn('TinyMce: could not find anchor with ID ', anchorName);
}
} else {
props.onMessage({
name: 'openUrl',
args: {
url: href,
},
});
props.onMessage({ channel: href });
}
}
}, [editor, props.onMessage]);
@ -216,6 +205,10 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
clearState: () => {
console.warn('TinyMCE::clearState - not implemented');
},
supportsCommand: (name:string) => {
// TODO: should also handle commands that are not in this map (insertText, focus, etc);
return !!joplinCommandToTinyMceCommands[name];
},
execCommand: async (cmd:EditorCommand) => {
if (!editor) return false;
@ -398,6 +391,10 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
.tox .tox-dialog__footer {
border-color: ${theme.dividerColor} !important;
}
.tox-tinymce {
border-top: none !important;
}
`));
return () => {
@ -627,15 +624,16 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
let cancelled = false;
const loadContent = async () => {
if (lastOnChangeEventContent.current === props.content) return;
const result = await props.markupToHtml(props.contentMarkupLanguage, props.content, markupRenderOptions({ resourceInfos: props.resourceInfos }));
if (cancelled) return;
lastOnChangeEventContent.current = props.content;
editor.setContent(result.html);
await loadDocumentAssets(editor, await props.allAssets(props.contentMarkupLanguage));
editor.getDoc().addEventListener('click', onEditorContentClick);
// Need to clear UndoManager to avoid this problem:
// - Load note 1
// - Make a change
@ -650,9 +648,17 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
return () => {
cancelled = true;
};
}, [editor, props.markupToHtml, props.allAssets, props.content, props.resourceInfos]);
useEffect(() => {
if (!editor) return () => {};
editor.getDoc().addEventListener('click', onEditorContentClick);
return () => {
editor.getDoc().removeEventListener('click', onEditorContentClick);
};
}, [editor, props.markupToHtml, props.allAssets, onEditorContentClick, props.resourceInfos]);
}, [editor, onEditorContentClick]);
// -----------------------------------------------------------------------------------------
// Handle onChange event
@ -685,6 +691,8 @@ const TinyMCE = (props:NoteBodyEditorProps, ref:any) => {
if (!editor) return;
lastOnChangeEventContent.current = contentMd;
props_onChangeRef.current({
changeId: changeId,
content: contentMd,

View File

@ -34,6 +34,8 @@ const NoteRevisionViewer = require('../NoteRevisionViewer.min');
const TagList = require('../TagList.min.js');
function NoteEditor(props: NoteTextProps) {
const theme = themeStyle(props.theme);
const [showRevisions, setShowRevisions] = useState(false);
const [titleHasBeenManuallyChanged, setTitleHasBeenManuallyChanged] = useState(false);
const [scrollWhenReady, setScrollWhenReady] = useState<ScrollOptions>(null);
@ -268,7 +270,7 @@ function NoteEditor(props: NoteTextProps) {
});
}, [formNote, handleProvisionalFlag]);
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch);
const onMessage = useMessageHandler(scrollWhenReady, setScrollWhenReady, editorRef, setLocalSearchResultCount, props.dispatch, formNote);
const introductionPostLinkClick = useCallback(() => {
bridge().openExternal('https://www.patreon.com/posts/34246624');
@ -379,17 +381,13 @@ function NoteEditor(props: NoteTextProps) {
function renderNoteToolbar() {
const toolbarStyle = {
// marginTop: 4,
marginBottom: 0,
flex: 1,
};
return <NoteToolbar
theme={props.theme}
note={formNote}
dispatch={props.dispatch}
style={toolbarStyle}
watchedNoteFiles={props.watchedNoteFiles}
onButtonClick={noteToolbar_buttonClick}
/>;
}
@ -414,7 +412,7 @@ function NoteEditor(props: NoteTextProps) {
disabled: false,
theme: props.theme,
dispatch: props.dispatch,
noteToolbar: renderNoteToolbar(),
noteToolbar: null,// renderNoteToolbar(),
onScroll: onScroll,
searchMarkers: searchMarkers,
visiblePanes: props.noteVisiblePanes || ['editor', 'viewer'],
@ -432,7 +430,7 @@ function NoteEditor(props: NoteTextProps) {
}
const wysiwygBanner = props.bodyEditor !== 'TinyMCE' ? null : (
<div style={{ ...styles.warningBanner, marginBottom: 10 }}>
<div style={{ ...styles.warningBanner }}>
This is an experimental WYSIWYG 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.
</div>
);
@ -511,13 +509,12 @@ function NoteEditor(props: NoteTextProps) {
return (
<div style={styles.root} onDrop={onDrop}>
<div style={{ display: 'flex', flexDirection: 'column', height: '100%' }}>
{wysiwygBanner}
{tagList}
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center', paddingBottom: 5, borderBottomWidth: 1, borderBottomColor: theme.dividerColor, borderBottomStyle: 'solid' }}>
{renderNoteToolbar()}
<input
type="text"
ref={titleInputRef}
// disabled={waitingToSaveNote}
placeholder={props.isProvisional ? _('Creating new %s...', formNote.is_todo ? _('to-do') : _('note')) : ''}
style={styles.titleInput}
onChange={onTitleChange}
@ -532,6 +529,7 @@ function NoteEditor(props: NoteTextProps) {
<div style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
{renderSearchBar()}
</div>
{wysiwygBanner}
</div>
</div>
);

View File

@ -9,7 +9,7 @@ export default function styles(props: NoteTextProps) {
...props.style,
boxSizing: 'border-box',
paddingLeft: 10,
paddingTop: 10,
paddingTop: 5,
borderLeftWidth: 1,
borderLeftColor: theme.dividerColor,
borderLeftStyle: 'solid',
@ -23,7 +23,7 @@ export default function styles(props: NoteTextProps) {
paddingRight: 8,
marginRight: theme.paddingLeft,
color: theme.textStyle.color,
fontSize: theme.textStyle.fontSize * 1.25 * 1.5,
fontSize: theme.textStyle.fontSize * 1.25,
backgroundColor: theme.backgroundColor,
border: '1px solid',
borderColor: theme.dividerColor,
@ -33,6 +33,8 @@ export default function styles(props: NoteTextProps) {
fontFamily: theme.fontFamily,
padding: 10,
fontSize: theme.fontSize,
marginTop: 5,
marginBottom: 5,
},
tinyMCE: {
width: '100%',

View File

@ -49,6 +49,7 @@ export interface NoteBodyEditorProps {
visiblePanes: string[],
keyboardMode: string,
resourceInfos: ResourceInfos,
showLocalSearch: boolean,
}
export interface FormNote {

View File

@ -1,5 +1,5 @@
import { useCallback } from 'react';
import { FormNote } from './types';
const BaseItem = require('lib/models/BaseItem');
const { _ } = require('lib/locale');
const BaseModel = require('lib/BaseModel.js');
@ -15,7 +15,7 @@ const { clipboard } = require('electron');
const { toSystemSlashes } = require('lib/path-utils');
const { reg } = require('lib/registry.js');
export default function useMessageHandler(scrollWhenReady:any, setScrollWhenReady:Function, editorRef:any, setLocalSearchResultCount:Function, dispatch:Function) {
export default function useMessageHandler(scrollWhenReady:any, setScrollWhenReady:Function, editorRef:any, setLocalSearchResultCount:Function, dispatch:Function, formNote:FormNote) {
return useCallback(async (event: any) => {
const msg = event.channel ? event.channel : '';
const args = event.args;
@ -128,10 +128,10 @@ export default function useMessageHandler(scrollWhenReady:any, setScrollWhenRead
folderId: item.parent_id,
noteId: item.id,
hash: resourceUrlInfo.hash,
// historyNoteAction: {
// id: this.state.note.id,
// parent_id: this.state.note.parent_id,
// },
historyNoteAction: {
id: formNote.id,
parent_id: formNote.parent_id,
},
});
} else {
throw new Error(`Unsupported item type: ${item.type_}`);
@ -148,5 +148,5 @@ export default function useMessageHandler(scrollWhenReady:any, setScrollWhenRead
} else {
bridge().showErrorMessageBox(_('Unsupported link or message: %s', msg));
}
}, [dispatch, setLocalSearchResultCount, scrollWhenReady]);
}, [dispatch, setLocalSearchResultCount, scrollWhenReady, formNote]);
}

View File

@ -1,48 +0,0 @@
'use strict';
const __awaiter = (this && this.__awaiter) || function(thisArg, _arguments, P, generator) {
function adopt(value) { return value instanceof P ? value : new P(function(resolve) { resolve(value); }); }
return new (P || (P = Promise))(function(resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator['throw'](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
Object.defineProperty(exports, '__esModule', { value: true });
const react_1 = require('react');
const resourceHandling_1 = require('./resourceHandling');
const ResourceFetcher = require('lib/services/ResourceFetcher.js');
const DecryptionWorker = require('lib/services/DecryptionWorker.js');
const Note = require('lib/models/Note');
function useResourceInfos(dependencies) {
const { noteBody } = dependencies;
const [resourceInfos, setResourceInfos] = react_1.useState({});
function installResourceHandling(refreshResourceHandler) {
ResourceFetcher.instance().on('downloadComplete', refreshResourceHandler);
ResourceFetcher.instance().on('downloadStarted', refreshResourceHandler);
DecryptionWorker.instance().on('resourceDecrypted', refreshResourceHandler);
}
function uninstallResourceHandling(refreshResourceHandler) {
ResourceFetcher.instance().off('downloadComplete', refreshResourceHandler);
ResourceFetcher.instance().off('downloadStarted', refreshResourceHandler);
DecryptionWorker.instance().off('resourceDecrypted', refreshResourceHandler);
}
const refreshResource = react_1.useCallback(function(event) {
return __awaiter(this, void 0, void 0, function* () {
const resourceIds = yield Note.linkedResourceIds(noteBody);
if (resourceIds.indexOf(event.id) >= 0) {
resourceHandling_1.clearResourceCache();
setResourceInfos(yield resourceHandling_1.attachedResources(noteBody));
}
});
}, [noteBody]);
react_1.useEffect(() => {
installResourceHandling(refreshResource);
return () => {
uninstallResourceHandling(refreshResource);
};
}, [refreshResource]);
return { resourceInfos };
}
exports.default = useResourceInfos;
// # sourceMappingURL=useResourceRefresher.js.map

View File

@ -59,8 +59,12 @@ export default function useWindowCommandHandler(dependencies:HookDependencies) {
editorCmd.name = 'insertText',
editorCmd.value = time.formatMsToLocal(new Date().getTime());
} else if (command.name === 'showLocalSearch') {
setShowLocalSearch(true);
if (noteSearchBarRef.current) noteSearchBarRef.current.wrappedInstance.focus();
if (editorRef.current && editorRef.current.supportsCommand('search')) {
editorCmd.name = 'search';
} else {
setShowLocalSearch(true);
if (noteSearchBarRef.current) noteSearchBarRef.current.wrappedInstance.focus();
}
} else if (command.name === 'insertTemplate') {
editorCmd.name = 'insertText',
editorCmd.value = time.formatMsToLocal(new Date().getTime());

File diff suppressed because it is too large Load Diff

View File

@ -1,17 +1,12 @@
import * as React from 'react';
const { connect } = require('react-redux');
const { buildStyle } = require('../../theme.js');
const Toolbar = require('../Toolbar.min.js');
const Note = require('lib/models/Note');
const Folder = require('lib/models/Folder');
const { time } = require('lib/time-utils.js');
const { _ } = require('lib/locale');
// const { substrWithEllipsis } = require('lib/string-utils');
// const Folder = require('lib/models/Folder');
// const { MarkupToHtml } = require('lib/joplin-renderer');
const { substrWithEllipsis } = require('lib/string-utils');
interface ButtonClickEvent {
name: string,
@ -20,10 +15,14 @@ interface ButtonClickEvent {
interface NoteToolbarProps {
theme: number,
style: any,
selectedFolderId: string,
folders: any[],
watchedNoteFiles: string[],
notesParentType: string,
note: any,
dispatch: Function,
onButtonClick(event:ButtonClickEvent):void,
historyNotes: any[],
}
function styles_(props:NoteToolbarProps) {
@ -31,50 +30,52 @@ function styles_(props:NoteToolbarProps) {
return {
root: {
...props.style,
borderBottom: 'none',
},
};
});
}
function useToolbarItems(note:any, watchedNoteFiles:string[], dispatch:Function, onButtonClick:Function) {
function useToolbarItems(props:NoteToolbarProps) {
const { note, selectedFolderId, folders, watchedNoteFiles, notesParentType, dispatch, onButtonClick, historyNotes } = props;
const toolbarItems = [];
// TODO: add these two items
const folder = Folder.byId(folders, selectedFolderId);
// if (props.folder && ['Search', 'Tag', 'SmartFilter'].includes(props.notesParentType)) {
// toolbarItems.push({
// title: _('In: %s', substrWithEllipsis(props.folder.title, 0, 16)),
// iconName: 'fa-book',
// onClick: () => {
// props.dispatch({
// type: 'FOLDER_AND_NOTE_SELECT',
// folderId: props.folder.id,
// noteId: props.formNote.id,
// });
// Folder.expandTree(props.folders, props.folder.parent_id);
// },
// });
// }
if (folder && ['Search', 'Tag', 'SmartFilter'].includes(notesParentType)) {
toolbarItems.push({
title: _('In: %s', substrWithEllipsis(folder.title, 0, 16)),
iconName: 'fa-book',
onClick: () => {
props.dispatch({
type: 'FOLDER_AND_NOTE_SELECT',
folderId: folder.id,
noteId: note.id,
});
Folder.expandTree(folders, folder.parent_id);
},
});
}
// if (props.historyNotes.length) {
// toolbarItems.push({
// tooltip: _('Back'),
// iconName: 'fa-arrow-left',
// onClick: () => {
// if (!props.historyNotes.length) return;
if (historyNotes.length) {
toolbarItems.push({
tooltip: _('Back'),
iconName: 'fa-arrow-left',
onClick: () => {
if (!historyNotes.length) return;
// const lastItem = props.historyNotes[props.historyNotes.length - 1];
const lastItem = historyNotes[historyNotes.length - 1];
// props.dispatch({
// type: 'FOLDER_AND_NOTE_SELECT',
// folderId: lastItem.parent_id,
// noteId: lastItem.id,
// historyNoteAction: 'pop',
// });
// },
// });
// }
dispatch({
type: 'FOLDER_AND_NOTE_SELECT',
folderId: lastItem.parent_id,
noteId: lastItem.id,
historyNoteAction: 'pop',
});
},
});
}
if (watchedNoteFiles.indexOf(note.id) >= 0) {
toolbarItems.push({
@ -137,12 +138,20 @@ function useToolbarItems(note:any, watchedNoteFiles:string[], dispatch:Function,
return toolbarItems;
}
export default function NoteToolbar(props:NoteToolbarProps) {
function NoteToolbar(props:NoteToolbarProps) {
const styles = styles_(props);
const toolbarItems = useToolbarItems(props.note, props.watchedNoteFiles, props.dispatch, props.onButtonClick);
return (
<Toolbar style={styles.root} items={toolbarItems} />
);
const toolbarItems = useToolbarItems(props);
return <Toolbar style={styles.root} items={toolbarItems} />;
}
const mapStateToProps = (state:any) => {
return {
selectedFolderId: state.selectedFolderId,
folders: state.folders,
watchedNoteFiles: state.watchedNoteFiles,
historyNotes: state.historyNotes,
notesParentType: state.notesParentType,
};
};
export default connect(mapStateToProps)(NoteToolbar);

View File

@ -6,13 +6,15 @@ const ToolbarSpace = require('./ToolbarSpace.min.js');
class ToolbarComponent extends React.Component {
render() {
const style = Object.assign({}, this.props.style);
const theme = themeStyle(this.props.theme);
style.height = theme.toolbarHeight;
style.display = 'flex';
style.flexDirection = 'row';
style.borderBottom = `1px solid ${theme.dividerColor}`;
style.boxSizing = 'border-box';
const style = Object.assign({
height: theme.toolbarHeight,
display: 'flex',
flexDirection: 'row',
borderBottom: `1px solid ${theme.dividerColor}`,
boxSizing: 'border-box',
}, this.props.style);
const itemComps = [];

View File

@ -1,15 +0,0 @@
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
const joplinRendererUtils = require('lib/joplin-renderer').utils;
const Resource = require('lib/models/Resource');
function resourcesStatus(resourceInfos) {
let lowestIndex = joplinRendererUtils.resourceStatusIndex('ready');
for (const id in resourceInfos) {
const s = joplinRendererUtils.resourceStatus(Resource, resourceInfos[id]);
const idx = joplinRendererUtils.resourceStatusIndex(s);
if (idx < lowestIndex) { lowestIndex = idx; }
}
return joplinRendererUtils.resourceStatusName(lowestIndex);
}
exports.resourcesStatus = resourcesStatus;
// # sourceMappingURL=NoteText.js.map