2017-11-05 02:17:48 +02:00
|
|
|
const React = require('react');
|
2017-11-05 20:36:27 +02:00
|
|
|
const { Note } = require('lib/models/note.js');
|
2017-11-13 20:47:35 +02:00
|
|
|
const { Setting } = require('lib/models/setting.js');
|
2017-11-11 00:18:00 +02:00
|
|
|
const { IconButton } = require('./IconButton.min.js');
|
2017-11-05 02:17:48 +02:00
|
|
|
const { connect } = require('react-redux');
|
2017-11-07 23:11:14 +02:00
|
|
|
const { _ } = require('lib/locale.js');
|
|
|
|
const { reg } = require('lib/registry.js');
|
2017-11-07 20:39:11 +02:00
|
|
|
const MdToHtml = require('lib/MdToHtml');
|
2017-11-05 20:36:27 +02:00
|
|
|
const shared = require('lib/components/shared/note-screen-shared.js');
|
2017-11-07 23:11:14 +02:00
|
|
|
const { bridge } = require('electron').remote.require('./bridge');
|
2017-11-08 19:51:55 +02:00
|
|
|
const { themeStyle } = require('../theme.js');
|
2017-11-10 00:44:10 +02:00
|
|
|
const AceEditor = require('react-ace').default;
|
2017-11-11 00:18:00 +02:00
|
|
|
const Menu = bridge().Menu;
|
|
|
|
const MenuItem = bridge().MenuItem;
|
|
|
|
const { shim } = require('lib/shim.js');
|
2017-11-10 20:43:54 +02:00
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
require('brace/mode/markdown');
|
2017-11-10 20:43:54 +02:00
|
|
|
// https://ace.c9.io/build/kitchen-sink.html
|
|
|
|
// https://highlightjs.org/static/demo/
|
2017-11-10 00:44:10 +02:00
|
|
|
require('brace/theme/chrome');
|
2017-11-05 02:17:48 +02:00
|
|
|
|
2017-11-05 01:27:13 +02:00
|
|
|
class NoteTextComponent extends React.Component {
|
|
|
|
|
2017-11-05 20:36:27 +02:00
|
|
|
constructor() {
|
|
|
|
super();
|
|
|
|
|
|
|
|
this.state = {
|
2017-11-10 19:58:17 +02:00
|
|
|
note: null,
|
2017-11-05 20:36:27 +02:00
|
|
|
noteMetadata: '',
|
|
|
|
showNoteMetadata: false,
|
|
|
|
folder: null,
|
|
|
|
lastSavedNote: null,
|
|
|
|
isLoading: true,
|
|
|
|
webviewReady: false,
|
2017-11-07 23:11:14 +02:00
|
|
|
scrollHeight: null,
|
2017-11-10 00:44:10 +02:00
|
|
|
editorScrollTop: 0,
|
2017-11-05 20:36:27 +02:00
|
|
|
};
|
2017-11-07 01:56:33 +02:00
|
|
|
|
|
|
|
this.lastLoadedNoteId_ = null;
|
2017-11-07 23:11:14 +02:00
|
|
|
|
|
|
|
this.webviewListeners_ = null;
|
|
|
|
this.ignoreNextEditorScroll_ = false;
|
2017-11-07 23:46:23 +02:00
|
|
|
this.scheduleSaveTimeout_ = null;
|
2017-11-10 00:44:10 +02:00
|
|
|
this.restoreScrollTop_ = null;
|
|
|
|
|
|
|
|
// Complicated but reliable method to get editor content height
|
|
|
|
// https://github.com/ajaxorg/ace/issues/2046
|
|
|
|
this.editorMaxScrollTop_ = 0;
|
|
|
|
this.onAfterEditorRender_ = () => {
|
|
|
|
const r = this.editor_.editor.renderer;
|
|
|
|
this.editorMaxScrollTop_ = Math.max(0, r.layerConfig.maxHeight - r.$size.scrollerHeight);
|
|
|
|
|
2017-11-13 02:23:12 +02:00
|
|
|
if (this.restoreScrollTop_ !== null) {
|
2017-11-10 00:44:10 +02:00
|
|
|
this.editorSetScrollTop(this.restoreScrollTop_);
|
|
|
|
this.restoreScrollTop_ = null;
|
|
|
|
}
|
|
|
|
}
|
2017-11-05 20:36:27 +02:00
|
|
|
}
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
mdToHtml() {
|
|
|
|
if (this.mdToHtml_) return this.mdToHtml_;
|
2017-11-21 21:31:21 +02:00
|
|
|
this.mdToHtml_ = new MdToHtml({
|
|
|
|
supportsResourceLinks: true,
|
|
|
|
resourceBaseUrl: 'file://' + Setting.value('resourceDir') + '/',
|
|
|
|
});
|
2017-11-07 23:11:14 +02:00
|
|
|
return this.mdToHtml_;
|
2017-11-05 01:27:13 +02:00
|
|
|
}
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
async componentWillMount() {
|
2017-11-10 19:58:17 +02:00
|
|
|
let note = null;
|
|
|
|
if (this.props.noteId) {
|
|
|
|
note = await Note.load(this.props.noteId);
|
|
|
|
}
|
|
|
|
|
|
|
|
const folder = note ? Folder.byId(this.props.folders, note.parent_id) : null;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
lastSavedNote: Object.assign({}, note),
|
|
|
|
note: note,
|
|
|
|
folder: folder,
|
|
|
|
isLoading: false,
|
|
|
|
});
|
|
|
|
|
|
|
|
this.lastLoadedNoteId_ = note ? note.id : null;
|
2017-11-05 18:51:03 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
componentWillUnmount() {
|
2017-11-07 23:46:23 +02:00
|
|
|
this.saveIfNeeded();
|
|
|
|
|
2017-11-05 18:51:03 +02:00
|
|
|
this.mdToHtml_ = null;
|
2017-11-07 23:11:14 +02:00
|
|
|
this.destroyWebview();
|
2017-11-05 18:51:03 +02:00
|
|
|
}
|
|
|
|
|
2017-11-07 23:46:23 +02:00
|
|
|
async saveIfNeeded() {
|
|
|
|
if (this.scheduleSaveTimeout_) clearTimeout(this.scheduleSaveTimeout_);
|
|
|
|
this.scheduleSaveTimeout_ = null;
|
|
|
|
if (!shared.isModified(this)) return;
|
|
|
|
await shared.saveNoteButton_press(this);
|
|
|
|
}
|
|
|
|
|
2017-11-08 19:51:55 +02:00
|
|
|
async saveOneProperty(name, value) {
|
|
|
|
await shared.saveOneProperty(this, name, value);
|
|
|
|
}
|
|
|
|
|
2017-11-07 23:46:23 +02:00
|
|
|
scheduleSave() {
|
|
|
|
if (this.scheduleSaveTimeout_) clearTimeout(this.scheduleSaveTimeout_);
|
|
|
|
this.scheduleSaveTimeout_ = setTimeout(() => {
|
|
|
|
this.saveIfNeeded();
|
|
|
|
}, 500);
|
|
|
|
}
|
|
|
|
|
2017-11-12 20:12:05 +02:00
|
|
|
async reloadNote(props) {
|
|
|
|
this.mdToHtml_ = null;
|
2017-11-07 23:11:14 +02:00
|
|
|
|
2017-11-12 20:12:05 +02:00
|
|
|
const noteId = props.noteId;
|
|
|
|
this.lastLoadedNoteId_ = noteId;
|
|
|
|
const note = noteId ? await Note.load(noteId) : null;
|
|
|
|
if (noteId !== this.lastLoadedNoteId_) return; // Race condition - current note was changed while this one was loading
|
2017-11-07 01:56:33 +02:00
|
|
|
|
2017-11-12 20:12:05 +02:00
|
|
|
// If we are loading nothing (noteId == null), make sure to
|
|
|
|
// set webviewReady to false too because the webview component
|
|
|
|
// is going to be removed in render().
|
|
|
|
const webviewReady = this.webview_ && this.state.webviewReady && noteId;
|
2017-11-10 19:58:17 +02:00
|
|
|
|
2017-11-12 20:12:05 +02:00
|
|
|
this.editorMaxScrollTop_ = 0;
|
2017-11-10 22:12:38 +02:00
|
|
|
|
2017-11-13 02:23:12 +02:00
|
|
|
// HACK: To go around a bug in Ace editor, we first set the scroll position to 1
|
|
|
|
// and then (in the renderer callback) to the value we actually need. The first
|
|
|
|
// operation helps clear the scroll position cache. See:
|
|
|
|
// https://github.com/ajaxorg/ace/issues/2195
|
|
|
|
this.editorSetScrollTop(1);
|
|
|
|
this.restoreScrollTop_ = 0;
|
2017-11-10 22:12:38 +02:00
|
|
|
|
2017-11-12 20:12:05 +02:00
|
|
|
this.setState({
|
|
|
|
note: note,
|
|
|
|
lastSavedNote: Object.assign({}, note),
|
|
|
|
webviewReady: webviewReady,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
async componentWillReceiveProps(nextProps) {
|
|
|
|
if ('noteId' in nextProps && nextProps.noteId !== this.props.noteId) {
|
|
|
|
await this.reloadNote(nextProps);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ('syncStarted' in nextProps && !nextProps.syncStarted && !this.isModified()) {
|
|
|
|
await this.reloadNote(nextProps);
|
2017-11-07 01:56:33 +02:00
|
|
|
}
|
2017-11-05 20:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
isModified() {
|
|
|
|
return shared.isModified(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
refreshNoteMetadata(force = null) {
|
|
|
|
return shared.refreshNoteMetadata(this, force);
|
|
|
|
}
|
|
|
|
|
2017-11-10 22:12:38 +02:00
|
|
|
title_changeText(event) {
|
|
|
|
shared.noteComponent_change(this, 'title', event.target.value);
|
2017-11-07 23:46:23 +02:00
|
|
|
this.scheduleSave();
|
2017-11-05 20:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
toggleIsTodo_onPress() {
|
|
|
|
shared.toggleIsTodo_onPress(this);
|
2017-11-07 23:46:23 +02:00
|
|
|
this.scheduleSave();
|
2017-11-05 20:36:27 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
showMetadata_onPress() {
|
|
|
|
shared.showMetadata_onPress(this);
|
2017-11-05 01:27:13 +02:00
|
|
|
}
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
webview_ipcMessage(event) {
|
|
|
|
const msg = event.channel ? event.channel : '';
|
|
|
|
const args = event.args;
|
|
|
|
const arg0 = args && args.length >= 1 ? args[0] : null;
|
|
|
|
const arg1 = args && args.length >= 2 ? args[1] : null;
|
|
|
|
|
2017-11-10 22:12:38 +02:00
|
|
|
reg.logger().debug('Got ipc-message: ' + msg, args);
|
2017-11-07 23:11:14 +02:00
|
|
|
|
|
|
|
if (msg.indexOf('checkboxclick:') === 0) {
|
2017-11-10 00:44:10 +02:00
|
|
|
// Ugly hack because setting the body here will make the scrollbar
|
|
|
|
// go to some random position. So we save the scrollTop here and it
|
|
|
|
// will be restored after the editor ref has been reset, and the
|
|
|
|
// "afterRender" event has been called.
|
|
|
|
this.restoreScrollTop_ = this.editorScrollTop();
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
const newBody = this.mdToHtml_.handleCheckboxClick(msg, this.state.note.body);
|
|
|
|
this.saveOneProperty('body', newBody);
|
|
|
|
} else if (msg.toLowerCase().indexOf('http') === 0) {
|
|
|
|
require('electron').shell.openExternal(msg);
|
|
|
|
} else if (msg === 'percentScroll') {
|
|
|
|
this.ignoreNextEditorScroll_ = true;
|
|
|
|
this.setEditorPercentScroll(arg0);
|
2017-11-12 18:33:34 +02:00
|
|
|
} else if (msg.indexOf('joplin://') === 0) {
|
|
|
|
const resourceId = msg.substr('joplin://'.length);
|
|
|
|
Resource.load(resourceId).then((resource) => {
|
|
|
|
const filePath = Resource.fullPath(resource);
|
|
|
|
bridge().openItem(filePath);
|
|
|
|
});
|
2017-11-07 23:11:14 +02:00
|
|
|
} else {
|
|
|
|
bridge().showMessageBox({
|
|
|
|
type: 'error',
|
|
|
|
message: _('Unsupported link or message: %s', msg),
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
editorMaxScroll() {
|
2017-11-10 00:44:10 +02:00
|
|
|
return this.editorMaxScrollTop_;
|
|
|
|
}
|
|
|
|
|
|
|
|
editorScrollTop() {
|
|
|
|
return this.editor_.editor.getSession().getScrollTop();
|
|
|
|
}
|
|
|
|
|
|
|
|
editorSetScrollTop(v) {
|
2017-11-10 22:12:38 +02:00
|
|
|
if (!this.editor_) return;
|
2017-11-10 00:44:10 +02:00
|
|
|
this.editor_.editor.getSession().setScrollTop(v);
|
2017-11-07 23:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setEditorPercentScroll(p) {
|
2017-11-10 00:44:10 +02:00
|
|
|
this.editorSetScrollTop(p * this.editorMaxScroll());
|
2017-11-07 23:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
setViewerPercentScroll(p) {
|
|
|
|
this.webview_.send('setPercentScroll', p);
|
|
|
|
}
|
|
|
|
|
|
|
|
editor_scroll() {
|
|
|
|
if (this.ignoreNextEditorScroll_) {
|
|
|
|
this.ignoreNextEditorScroll_ = false;
|
|
|
|
return;
|
|
|
|
}
|
2017-11-10 00:44:10 +02:00
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
const m = this.editorMaxScroll();
|
2017-11-10 00:44:10 +02:00
|
|
|
this.setViewerPercentScroll(m ? this.editorScrollTop() / m : 0);
|
2017-11-07 23:11:14 +02:00
|
|
|
}
|
|
|
|
|
2017-11-05 18:51:03 +02:00
|
|
|
webview_domReady() {
|
2017-11-07 23:11:14 +02:00
|
|
|
if (!this.webview_) return;
|
|
|
|
|
2017-11-05 18:51:03 +02:00
|
|
|
this.setState({
|
|
|
|
webviewReady: true,
|
|
|
|
});
|
|
|
|
|
2017-11-13 20:47:35 +02:00
|
|
|
// if (Setting.value('env') === 'dev') this.webview_.openDevTools();
|
2017-11-07 23:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
webview_ref(element) {
|
|
|
|
if (this.webview_) {
|
|
|
|
if (this.webview_ === element) return;
|
|
|
|
this.destroyWebview();
|
|
|
|
}
|
2017-11-05 18:51:03 +02:00
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
if (!element) {
|
|
|
|
this.destroyWebview();
|
|
|
|
} else {
|
|
|
|
this.initWebview(element);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
editor_ref(element) {
|
|
|
|
if (this.editor_ === element) return;
|
2017-11-10 00:44:10 +02:00
|
|
|
|
|
|
|
if (this.editor_) {
|
|
|
|
this.editor_.editor.renderer.off('afterRender', this.onAfterEditorRender_);
|
|
|
|
}
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
this.editor_ = element;
|
2017-11-10 00:44:10 +02:00
|
|
|
|
|
|
|
if (this.editor_) {
|
|
|
|
this.editor_.editor.renderer.on('afterRender', this.onAfterEditorRender_);
|
|
|
|
}
|
2017-11-07 23:11:14 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
initWebview(wv) {
|
|
|
|
if (!this.webviewListeners_) {
|
|
|
|
this.webviewListeners_ = {
|
|
|
|
'dom-ready': this.webview_domReady.bind(this),
|
|
|
|
'ipc-message': this.webview_ipcMessage.bind(this),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
for (let n in this.webviewListeners_) {
|
|
|
|
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
|
|
|
|
const fn = this.webviewListeners_[n];
|
|
|
|
wv.addEventListener(n, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.webview_ = wv;
|
|
|
|
}
|
|
|
|
|
|
|
|
destroyWebview() {
|
|
|
|
if (!this.webview_) return;
|
2017-11-05 18:51:03 +02:00
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
for (let n in this.webviewListeners_) {
|
|
|
|
if (!this.webviewListeners_.hasOwnProperty(n)) continue;
|
|
|
|
const fn = this.webviewListeners_[n];
|
|
|
|
this.webview_.removeEventListener(n, fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
this.webview_ = null;
|
2017-11-05 18:51:03 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 00:44:10 +02:00
|
|
|
aceEditor_change(body) {
|
|
|
|
shared.noteComponent_change(this, 'body', body);
|
|
|
|
this.scheduleSave();
|
|
|
|
}
|
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
itemContextMenu(event) {
|
|
|
|
const noteId = this.props.noteId;
|
|
|
|
if (!noteId) return;
|
|
|
|
|
|
|
|
const menu = new Menu()
|
|
|
|
|
|
|
|
menu.append(new MenuItem({label: _('Attach file'), click: async () => {
|
|
|
|
const filePaths = bridge().showOpenDialog({
|
|
|
|
properties: ['openFile', 'createDirectory'],
|
|
|
|
});
|
|
|
|
if (!filePaths || !filePaths.length) return;
|
|
|
|
|
|
|
|
await this.saveIfNeeded();
|
|
|
|
const note = await Note.load(noteId);
|
|
|
|
|
2017-11-17 00:33:45 +02:00
|
|
|
try {
|
|
|
|
reg.logger().info('Attaching ' + filePaths[0]);
|
|
|
|
const newNote = await shim.attachFileToNote(note, filePaths[0]);
|
|
|
|
reg.logger().info('File was attached.');
|
|
|
|
this.setState({
|
|
|
|
note: newNote,
|
|
|
|
lastSavedNote: Object.assign({}, newNote),
|
|
|
|
});
|
|
|
|
} catch (error) {
|
|
|
|
reg.logger().error(error);
|
|
|
|
}
|
2017-11-11 00:18:00 +02:00
|
|
|
}}));
|
|
|
|
|
2017-11-28 00:50:46 +02:00
|
|
|
menu.append(new MenuItem({label: _('Set or clear alarm'), click: async () => {
|
|
|
|
this.props.dispatch({
|
|
|
|
type: 'WINDOW_COMMAND',
|
|
|
|
name: 'editAlarm',
|
|
|
|
noteId: noteId,
|
|
|
|
});
|
|
|
|
}}));
|
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
menu.popup(bridge().window());
|
|
|
|
}
|
|
|
|
|
2017-11-05 01:27:13 +02:00
|
|
|
render() {
|
2017-11-07 23:11:14 +02:00
|
|
|
const style = this.props.style;
|
2017-11-05 01:27:13 +02:00
|
|
|
const note = this.state.note;
|
2017-11-07 23:11:14 +02:00
|
|
|
const body = note ? note.body : '';
|
2017-11-08 19:51:55 +02:00
|
|
|
const theme = themeStyle(this.props.theme);
|
2017-11-10 21:18:19 +02:00
|
|
|
const visiblePanes = this.props.visiblePanes || ['editor', 'viewer'];
|
2017-11-07 23:11:14 +02:00
|
|
|
|
2017-11-10 22:12:38 +02:00
|
|
|
const borderWidth = 1;
|
|
|
|
|
|
|
|
const rootStyle = Object.assign({
|
|
|
|
borderLeft: borderWidth + 'px solid ' + theme.dividerColor,
|
|
|
|
boxSizing: 'border-box',
|
|
|
|
paddingLeft: 10,
|
|
|
|
paddingRight: 0,
|
|
|
|
}, style);
|
|
|
|
|
|
|
|
const innerWidth = rootStyle.width - rootStyle.paddingLeft - rootStyle.paddingRight - borderWidth;
|
|
|
|
|
2017-11-10 19:58:17 +02:00
|
|
|
if (!note) {
|
|
|
|
const emptyDivStyle = Object.assign({
|
|
|
|
backgroundColor: 'black',
|
|
|
|
opacity: 0.1,
|
2017-11-10 22:12:38 +02:00
|
|
|
}, rootStyle);
|
2017-11-10 19:58:17 +02:00
|
|
|
return <div style={emptyDivStyle}></div>
|
|
|
|
}
|
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
const titleBarStyle = {
|
2017-11-10 22:12:38 +02:00
|
|
|
width: innerWidth - rootStyle.paddingLeft,
|
2017-11-11 00:18:00 +02:00
|
|
|
height: 30,
|
2017-11-10 22:12:38 +02:00
|
|
|
boxSizing: 'border-box',
|
2017-11-11 00:18:00 +02:00
|
|
|
marginTop: 10,
|
|
|
|
marginBottom: 10,
|
|
|
|
display: 'flex',
|
|
|
|
flexDirection: 'row',
|
|
|
|
};
|
|
|
|
|
|
|
|
const titleEditorStyle = {
|
|
|
|
display: 'flex',
|
|
|
|
flex: 1,
|
|
|
|
display: 'inline-block',
|
2017-11-10 22:12:38 +02:00
|
|
|
paddingTop: 5,
|
|
|
|
paddingBottom: 5,
|
|
|
|
paddingLeft: 8,
|
|
|
|
paddingRight: 8,
|
|
|
|
marginRight: rootStyle.paddingLeft,
|
|
|
|
};
|
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
const bottomRowHeight = rootStyle.height - titleBarStyle.height - titleBarStyle.marginBottom - titleBarStyle.marginTop;
|
2017-11-10 22:12:38 +02:00
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
const viewerStyle = {
|
2017-11-10 22:12:38 +02:00
|
|
|
width: Math.floor(innerWidth / 2),
|
|
|
|
height: bottomRowHeight,
|
2017-11-07 23:11:14 +02:00
|
|
|
overflow: 'hidden',
|
|
|
|
float: 'left',
|
|
|
|
verticalAlign: 'top',
|
2017-11-10 22:43:44 +02:00
|
|
|
boxSizing: 'border-box',
|
2017-11-07 23:11:14 +02:00
|
|
|
};
|
|
|
|
|
2017-11-08 19:51:55 +02:00
|
|
|
const paddingTop = 14;
|
|
|
|
|
2017-11-07 23:11:14 +02:00
|
|
|
const editorStyle = {
|
2017-11-10 22:12:38 +02:00
|
|
|
width: innerWidth - viewerStyle.width,
|
|
|
|
height: bottomRowHeight - paddingTop,
|
2017-11-10 00:44:10 +02:00
|
|
|
overflowY: 'hidden',
|
2017-11-07 23:11:14 +02:00
|
|
|
float: 'left',
|
|
|
|
verticalAlign: 'top',
|
2017-11-08 19:51:55 +02:00
|
|
|
paddingTop: paddingTop + 'px',
|
|
|
|
lineHeight: theme.textAreaLineHeight + 'px',
|
|
|
|
fontSize: theme.fontSize + 'px',
|
2017-11-07 23:11:14 +02:00
|
|
|
};
|
2017-11-05 01:27:13 +02:00
|
|
|
|
2017-11-10 21:18:19 +02:00
|
|
|
if (visiblePanes.indexOf('viewer') < 0) {
|
|
|
|
// Note: setting webview.display to "none" is currently not supported due
|
|
|
|
// to this bug: https://github.com/electron/electron/issues/8277
|
|
|
|
// So instead setting the width 0.
|
|
|
|
viewerStyle.width = 0;
|
2017-11-10 22:12:38 +02:00
|
|
|
editorStyle.width = innerWidth;
|
2017-11-10 21:18:19 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (visiblePanes.indexOf('editor') < 0) {
|
|
|
|
editorStyle.display = 'none';
|
2017-11-10 22:12:38 +02:00
|
|
|
viewerStyle.width = innerWidth;
|
2017-11-10 21:18:19 +02:00
|
|
|
}
|
|
|
|
|
2017-11-10 23:04:53 +02:00
|
|
|
if (visiblePanes.indexOf('viewer') >= 0 && visiblePanes.indexOf('editor') >= 0) {
|
|
|
|
viewerStyle.borderLeft = '1px solid ' + theme.dividerColor;
|
|
|
|
} else {
|
|
|
|
viewerStyle.borderLeft = 'none';
|
|
|
|
}
|
|
|
|
|
2017-11-05 18:51:03 +02:00
|
|
|
if (this.state.webviewReady) {
|
|
|
|
const mdOptions = {
|
|
|
|
onResourceLoaded: () => {
|
|
|
|
this.forceUpdate();
|
|
|
|
},
|
|
|
|
postMessageSyntax: 'ipcRenderer.sendToHost',
|
|
|
|
};
|
2017-11-08 19:51:55 +02:00
|
|
|
const html = this.mdToHtml().render(body, theme, mdOptions);
|
2017-11-05 18:51:03 +02:00
|
|
|
this.webview_.send('setHtml', html);
|
|
|
|
}
|
|
|
|
|
2017-11-10 22:12:38 +02:00
|
|
|
const titleEditor = <input
|
|
|
|
type="text"
|
|
|
|
style={titleEditorStyle}
|
|
|
|
value={note ? note.title : ''}
|
|
|
|
onChange={(event) => { this.title_changeText(event); }}
|
|
|
|
/>
|
|
|
|
|
2017-11-11 00:18:00 +02:00
|
|
|
const titleBarMenuButton = <IconButton style={{
|
|
|
|
display: 'flex',
|
|
|
|
}} iconName="fa-caret-down" theme={this.props.theme} onClick={() => { this.itemContextMenu() }} />
|
|
|
|
|
2017-11-10 21:18:19 +02:00
|
|
|
const viewer = <webview
|
|
|
|
style={viewerStyle}
|
|
|
|
nodeintegration="1"
|
2017-11-11 00:27:38 +02:00
|
|
|
src="gui/note-viewer/index.html"
|
2017-11-10 21:18:19 +02:00
|
|
|
ref={(elem) => { this.webview_ref(elem); } }
|
|
|
|
/>
|
2017-11-10 00:44:10 +02:00
|
|
|
|
|
|
|
const editorRootStyle = Object.assign({}, editorStyle);
|
|
|
|
delete editorRootStyle.width;
|
|
|
|
delete editorRootStyle.height;
|
|
|
|
delete editorRootStyle.fontSize;
|
|
|
|
|
|
|
|
const editor = <AceEditor
|
|
|
|
value={body}
|
|
|
|
mode="markdown"
|
|
|
|
theme="chrome"
|
|
|
|
style={editorRootStyle}
|
|
|
|
width={editorStyle.width + 'px'}
|
|
|
|
height={editorStyle.height + 'px'}
|
|
|
|
fontSize={editorStyle.fontSize}
|
|
|
|
showGutter={false}
|
|
|
|
name="note-editor"
|
|
|
|
wrapEnabled={true}
|
|
|
|
onScroll={(event) => { this.editor_scroll(); }}
|
|
|
|
ref={(elem) => { this.editor_ref(elem); } }
|
|
|
|
onChange={(body) => { this.aceEditor_change(body) }}
|
2017-11-10 23:04:53 +02:00
|
|
|
showPrintMargin={false}
|
2017-11-10 00:44:10 +02:00
|
|
|
|
|
|
|
// Disable warning: "Automatically scrolling cursor into view after
|
|
|
|
// selection change this will be disabled in the next version set
|
|
|
|
// editor.$blockScrolling = Infinity to disable this message"
|
|
|
|
editorProps={{$blockScrolling: true}}
|
2017-11-10 01:28:08 +02:00
|
|
|
|
|
|
|
// This is buggy (gets outside the container)
|
|
|
|
highlightActiveLine={false}
|
2017-11-10 00:44:10 +02:00
|
|
|
/>
|
2017-11-05 18:51:03 +02:00
|
|
|
|
2017-11-05 01:27:13 +02:00
|
|
|
return (
|
2017-11-10 22:12:38 +02:00
|
|
|
<div style={rootStyle}>
|
2017-11-11 00:18:00 +02:00
|
|
|
<div style={titleBarStyle}>
|
|
|
|
{ titleEditor }
|
|
|
|
{ titleBarMenuButton }
|
|
|
|
</div>
|
2017-11-07 23:11:14 +02:00
|
|
|
{ editor }
|
|
|
|
{ viewer }
|
2017-11-05 01:27:13 +02:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
const mapStateToProps = (state) => {
|
|
|
|
return {
|
2017-11-22 20:35:31 +02:00
|
|
|
noteId: state.selectedNoteIds.length === 1 ? state.selectedNoteIds[0] : null,
|
2017-11-05 20:36:27 +02:00
|
|
|
folderId: state.selectedFolderId,
|
|
|
|
itemType: state.selectedItemType,
|
|
|
|
folders: state.folders,
|
|
|
|
theme: state.settings.theme,
|
|
|
|
showAdvancedOptions: state.settings.showAdvancedOptions,
|
2017-11-12 20:12:05 +02:00
|
|
|
syncStarted: state.syncStarted,
|
2017-11-05 01:27:13 +02:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
|
|
|
const NoteText = connect(mapStateToProps)(NoteTextComponent);
|
|
|
|
|
|
|
|
module.exports = { NoteText };
|