mirror of https://github.com/laurent22/joplin.git
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 TinyMCEpull/3165/head
parent
f51873d877
commit
51732a5adb
|
@ -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({
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -13,7 +13,7 @@ function styles_(props:ToolbarProps) {
|
|||
return buildStyle('AceEditorToolbar', props.theme, (/* theme:any*/) => {
|
||||
return {
|
||||
root: {
|
||||
// marginTop: 4,
|
||||
flex: 1,
|
||||
marginBottom: 0,
|
||||
},
|
||||
};
|
||||
|
|
|
@ -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
|
|
@ -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,
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
|
|
|
@ -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%',
|
||||
|
|
|
@ -49,6 +49,7 @@ export interface NoteBodyEditorProps {
|
|||
visiblePanes: string[],
|
||||
keyboardMode: string,
|
||||
resourceInfos: ResourceInfos,
|
||||
showLocalSearch: boolean,
|
||||
}
|
||||
|
||||
export interface FormNote {
|
||||
|
|
|
@ -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]);
|
||||
}
|
||||
|
|
|
@ -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
|
|
@ -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
|
@ -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);
|
||||
|
|
|
@ -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 = [];
|
||||
|
||||
|
|
|
@ -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
|
Loading…
Reference in New Issue