1
0
mirror of https://github.com/laurent22/joplin.git synced 2025-01-11 18:24:43 +02:00

Refactor to move state changes to browser process

This commit is contained in:
Laurent Cozic 2017-11-05 00:17:48 +00:00
parent cc277018b7
commit dbf51382c8
13 changed files with 241 additions and 92 deletions

View File

@ -235,6 +235,10 @@ class Application extends BaseApplication {
return Object.assign({}, this.commandMetadata_); return Object.assign({}, this.commandMetadata_);
} }
hasGui() {
return this.gui() && !this.gui().isDummy();
}
findCommandByName(name) { findCommandByName(name) {
if (this.commands_[name]) return this.commands_[name]; if (this.commands_[name]) return this.commands_[name];

View File

@ -5,10 +5,9 @@ const path = require('path')
class ElectronAppWrapper { class ElectronAppWrapper {
constructor(electronApp, app, store) { constructor(electronApp) {
this.app_ = app;
this.electronApp_ = electronApp; this.electronApp_ = electronApp;
this.store_ = store; //this.store_ = store;
this.win_ = null; this.win_ = null;
} }
@ -24,19 +23,23 @@ class ElectronAppWrapper {
return this.logger_; return this.logger_;
} }
store() { window() {
return this.store_; return this.win_;
} }
dispatch(action) { // store() {
return this.store().dispatch(action); // return this.store_;
} // }
windowContentSize() { // dispatch(action) {
if (!this.win_) return { width: 0, height: 0 }; // return this.store().dispatch(action);
const s = this.win_.getContentSize(); // }
return { width: s[0], height: s[1] };
} // windowContentSize() {
// if (!this.win_) return { width: 0, height: 0 };
// const s = this.win_.getContentSize();
// return { width: s[0], height: s[1] };
// }
createWindow() { createWindow() {
this.win_ = new BrowserWindow({width: 800, height: 600}) this.win_ = new BrowserWindow({width: 800, height: 600})
@ -54,16 +57,16 @@ class ElectronAppWrapper {
}) })
this.win_.on('resize', () => { this.win_.on('resize', () => {
this.dispatch({ // this.dispatch({
type: 'WINDOW_CONTENT_SIZE_SET', // type: 'WINDOW_CONTENT_SIZE_SET',
size: this.windowContentSize(), // size: this.windowContentSize(),
}); // });
}); });
this.dispatch({ // this.dispatch({
type: 'WINDOW_CONTENT_SIZE_SET', // type: 'WINDOW_CONTENT_SIZE_SET',
size: this.windowContentSize(), // size: this.windowContentSize(),
}); // });
} }
async waitForElectronAppReady() { async waitForElectronAppReady() {

View File

@ -17,14 +17,12 @@ const { ElectronAppWrapper } = require('./ElectronAppWrapper');
class Application extends BaseApplication { class Application extends BaseApplication {
constructor(electronApp) { constructor() {
super(); super();
this.electronApp_ = electronApp;
} }
gui() { hasGui() {
return this.gui_; return true;
} }
async start(argv) { async start(argv) {
@ -32,11 +30,11 @@ class Application extends BaseApplication {
this.initRedux(); this.initRedux();
this.gui_ = new ElectronAppWrapper(this.electronApp_, this, this.store()); //this.gui_ = new ElectronAppWrapper(this.electronApp_, this, this.store());
try { try {
this.gui_.setLogger(this.logger()); // this.gui_.setLogger(this.logger());
await this.gui().start(); // await this.gui().start();
// Since the settings need to be loaded before the store is created, it will never // Since the settings need to be loaded before the store is created, it will never
// receive the SETTINGS_UPDATE_ALL even, which mean state.settings will not be // receive the SETTINGS_UPDATE_ALL even, which mean state.settings will not be
@ -57,7 +55,7 @@ class Application extends BaseApplication {
id: Setting.value('activeFolderId'), id: Setting.value('activeFolderId'),
}); });
} catch (error) { } catch (error) {
await this.gui_.exit(); //await this.gui_.exit();
throw error; throw error;
} }
} }
@ -67,7 +65,8 @@ class Application extends BaseApplication {
let application_ = null; let application_ = null;
function app() { function app() {
if (!application_) throw new Error('Application has not been initialized'); //if (!application_) throw new Error('Application has not been initialized');
if (!application_) application_ = new Application();
return application_; return application_;
} }

View File

@ -0,0 +1,36 @@
class Bridge {
constructor(electronWrapper) {
this.electronWrapper_ = electronWrapper;
}
processArgv() {
return process.argv;
}
window() {
return this.electronWrapper_.window();
}
windowContentSize() {
if (!this.window()) return { width: 0, height: 0 };
const s = this.window().getContentSize();
return { width: s[0], height: s[1] };
}
}
let bridge_ = null;
function initBridge(wrapper) {
if (bridge_) throw new Error('Bridge already initialized');
bridge_ = new Bridge(wrapper);
return bridge_;
}
function bridge() {
if (!bridge_) throw new Error('Bridge not initialized');
return bridge_;
}
module.exports = { bridge, initBridge }

View File

@ -1,3 +1,5 @@
const React = require('react');
class ItemList extends React.Component { class ItemList extends React.Component {
constructor() { constructor() {
@ -6,34 +8,32 @@ class ItemList extends React.Component {
this.scrollTop_ = 0; this.scrollTop_ = 0;
} }
componentWillMount() { updateStateItemIndexes(props) {
if (typeof props === 'undefined') props = this.props;
const topItemIndex = Math.floor(this.scrollTop_ / props.itemHeight);
const visibleItemCount = Math.ceil(props.style.height / props.itemHeight);
let bottomItemIndex = topItemIndex + visibleItemCount;
if (bottomItemIndex >= props.items.length) bottomItemIndex = props.items.length - 1;
this.setState({ this.setState({
topItemIndex: this.topItemIndex(), topItemIndex: topItemIndex,
bottomItemIndex: this.bottomItemIndex(), bottomItemIndex: bottomItemIndex,
}); });
} }
componentWillMount() {
this.updateStateItemIndexes();
}
componentWillReceiveProps(newProps) {
this.updateStateItemIndexes(newProps);
}
onScroll(scrollTop) { onScroll(scrollTop) {
this.scrollTop_ = scrollTop; this.scrollTop_ = scrollTop;
this.updateStateItemIndexes();
this.setState({
topItemIndex: this.topItemIndex(),
bottomItemIndex: this.bottomItemIndex(),
});
}
topItemIndex() {
return Math.floor(this.scrollTop_ / this.props.itemHeight);
}
visibleItemCount() {
return Math.ceil(this.props.style.height / this.props.itemHeight);
}
bottomItemIndex() {
let r = this.topItemIndex() + this.visibleItemCount();
if (r >= this.props.items.length) r = this.props.items.length - 1;
return r;
} }
render() { render() {

View File

@ -1,4 +1,6 @@
const { ItemList } = require('./ItemList.min.js'); const { ItemList } = require('./ItemList.min.js');
const React = require('react');
const { connect } = require('react-redux');
class NoteListComponent extends React.Component { class NoteListComponent extends React.Component {

View File

@ -1,5 +1,8 @@
//const { BaseModel } = require('lib/base-model.js'); //const { BaseModel } = require('lib/base-model.js');
const React = require('react');
const { connect } = require('react-redux');
class NoteTextComponent extends React.Component { class NoteTextComponent extends React.Component {
componentWillMount() { componentWillMount() {

View File

@ -3,13 +3,47 @@ const { render } = require('react-dom');
const { createStore } = require('redux'); const { createStore } = require('redux');
const { connect, Provider } = require('react-redux'); const { connect, Provider } = require('react-redux');
const { NoteList } = require('./gui/NoteList.min.js'); const { NoteList } = require('./NoteList.min.js');
const { NoteText } = require('./gui/NoteText.min.js'); const { NoteText } = require('./NoteText.min.js');
const { app } = require('electron').remote.require('./app'); const { app } = require('../app');
const { bridge } = require('electron').remote.require('./bridge');
//const { app } = require('electron').remote.require('./app');
async function initialize(dispatch) {
bridge().window().on('resize', function() {
store.dispatch({
type: 'WINDOW_CONTENT_SIZE_SET',
size: bridge().windowContentSize(),
});
});
store.dispatch({
type: 'WINDOW_CONTENT_SIZE_SET',
size: bridge().windowContentSize(),
});
}
class ReactRootComponent extends React.Component { class ReactRootComponent extends React.Component {
async componentDidMount() {
if (this.props.appState == 'starting') {
this.props.dispatch({
type: 'SET_APP_STATE',
state: 'initializing',
});
await initialize(this.props.dispatch);
this.props.dispatch({
type: 'SET_APP_STATE',
state: 'ready',
});
}
}
render() { render() {
const style = { const style = {
width: this.props.size.width, width: this.props.size.width,
@ -33,7 +67,7 @@ class ReactRootComponent extends React.Component {
return ( return (
<div style={style}> <div style={style}>
<NoteList itemHeight={40} style={noteListStyle}></NoteList> <NoteList itemHeight={40} style={noteListStyle}></NoteList>
<NoteText style={noteTextStyle}></NoteText> {/*<NoteText style={noteTextStyle}></NoteText>*/}
</div> </div>
); );
} }
@ -43,6 +77,7 @@ class ReactRootComponent extends React.Component {
const mapStateToProps = (state) => { const mapStateToProps = (state) => {
return { return {
size: state.windowContentSize, size: state.windowContentSize,
appState: state.appState,
}; };
}; };

View File

@ -7,6 +7,7 @@
</head> </head>
<body> <body>
<div id="react-root"></div> <div id="react-root"></div>
<script src="gui/Root.min.js"></script> <!-- <script src="gui/Root.min.js"></script> -->
<script src="main-html.js"></script>
</body> </body>
</html> </html>

View File

@ -0,0 +1,40 @@
// Make it possible to require("/lib/...") without specifying full path
require('app-module-path').addPath(__dirname);
const { app } = require('./app.js');
const { Folder } = require('lib/models/folder.js');
const { Resource } = require('lib/models/resource.js');
const { BaseItem } = require('lib/models/base-item.js');
const { Note } = require('lib/models/note.js');
const { Tag } = require('lib/models/tag.js');
const { NoteTag } = require('lib/models/note-tag.js');
const { Setting } = require('lib/models/setting.js');
const { Logger } = require('lib/logger.js');
const { FsDriverNode } = require('lib/fs-driver-node.js');
const { shimInit } = require('lib/shim-init-node.js');
const { bridge } = require('electron').remote.require('./bridge');
const fsDriver = new FsDriverNode();
Logger.fsDriver_ = fsDriver;
Resource.fsDriver_ = fsDriver;
// That's not good, but it's to avoid circular dependency issues
// in the BaseItem class.
BaseItem.loadClass('Note', Note);
BaseItem.loadClass('Folder', Folder);
BaseItem.loadClass('Resource', Resource);
BaseItem.loadClass('Tag', Tag);
BaseItem.loadClass('NoteTag', NoteTag);
Setting.setConstant('appId', 'net.cozic.joplin-desktop');
Setting.setConstant('appType', 'desktop');
shimInit();
app().start(bridge().processArgv()).then(() => {
require('./gui/Root.min.js');
}).catch((error) => {
console.error('Fatal error:');
console.error(error);
});

View File

@ -2,43 +2,61 @@
require('app-module-path').addPath(__dirname); require('app-module-path').addPath(__dirname);
const electronApp = require('electron').app; const electronApp = require('electron').app;
const { initApp } = require('./app.js'); const { ElectronAppWrapper } = require('./ElectronAppWrapper');
const { Folder } = require('lib/models/folder.js'); const { initBridge } = require('./bridge');
const { Resource } = require('lib/models/resource.js');
const { BaseItem } = require('lib/models/base-item.js');
const { Note } = require('lib/models/note.js');
const { Tag } = require('lib/models/tag.js');
const { NoteTag } = require('lib/models/note-tag.js');
const { Setting } = require('lib/models/setting.js');
const { Logger } = require('lib/logger.js');
const { FsDriverNode } = require('lib/fs-driver-node.js');
const { shimInit } = require('lib/shim-init-node.js');
process.on('unhandledRejection', (reason, p) => { process.on('unhandledRejection', (reason, p) => {
console.error('Unhandled promise rejection', p, 'reason:', reason); console.error('Unhandled promise rejection', p, 'reason:', reason);
process.exit(1); process.exit(1);
}); });
const fsDriver = new FsDriverNode(); const wrapper = new ElectronAppWrapper(electronApp);
Logger.fsDriver_ = fsDriver;
Resource.fsDriver_ = fsDriver;
// That's not good, but it's to avoid circular dependency issues initBridge(wrapper);
// in the BaseItem class.
BaseItem.loadClass('Note', Note);
BaseItem.loadClass('Folder', Folder);
BaseItem.loadClass('Resource', Resource);
BaseItem.loadClass('Tag', Tag);
BaseItem.loadClass('NoteTag', NoteTag);
Setting.setConstant('appId', 'net.cozic.joplin-desktop'); wrapper.start().catch((error) => {
Setting.setConstant('appType', 'desktop'); console.error('Electron App fatal error:');
shimInit();
const app = initApp(electronApp);
app.start(process.argv).catch((error) => {
console.error('Fatal error:');
console.error(error); console.error(error);
}); });
// const electronApp = require('electron').app;
// const { initApp } = require('./app.js');
// const { Folder } = require('lib/models/folder.js');
// const { Resource } = require('lib/models/resource.js');
// const { BaseItem } = require('lib/models/base-item.js');
// const { Note } = require('lib/models/note.js');
// const { Tag } = require('lib/models/tag.js');
// const { NoteTag } = require('lib/models/note-tag.js');
// const { Setting } = require('lib/models/setting.js');
// const { Logger } = require('lib/logger.js');
// const { FsDriverNode } = require('lib/fs-driver-node.js');
// const { shimInit } = require('lib/shim-init-node.js');
// process.on('unhandledRejection', (reason, p) => {
// console.error('Unhandled promise rejection', p, 'reason:', reason);
// process.exit(1);
// });
// const fsDriver = new FsDriverNode();
// Logger.fsDriver_ = fsDriver;
// Resource.fsDriver_ = fsDriver;
// // That's not good, but it's to avoid circular dependency issues
// // in the BaseItem class.
// BaseItem.loadClass('Note', Note);
// BaseItem.loadClass('Folder', Folder);
// BaseItem.loadClass('Resource', Resource);
// BaseItem.loadClass('Tag', Tag);
// BaseItem.loadClass('NoteTag', NoteTag);
// Setting.setConstant('appId', 'net.cozic.joplin-desktop');
// Setting.setConstant('appType', 'desktop');
// shimInit();
// const app = initApp(electronApp);
// app.start(process.argv).catch((error) => {
// console.error('Fatal error:');
// console.error(error);
// });

View File

@ -192,6 +192,10 @@ class BaseApplication {
return o.join(', '); return o.join(', ');
} }
hasGui() {
return false;
}
generalMiddleware() { generalMiddleware() {
const middleware = store => next => async (action) => { const middleware = store => next => async (action) => {
this.logger().debug('Reducer action', this.reducerActionToString(action)); this.logger().debug('Reducer action', this.reducerActionToString(action));
@ -213,7 +217,7 @@ class BaseApplication {
await this.refreshNotes(BaseModel.TYPE_SEARCH, action.id); await this.refreshNotes(BaseModel.TYPE_SEARCH, action.id);
} }
if (this.gui() && action.type == 'SETTINGS_UPDATE_ONE' && action.key == 'sync.interval' || action.type == 'SETTINGS_UPDATE_ALL') { if (this.hasGui() && action.type == 'SETTINGS_UPDATE_ONE' && action.key == 'sync.interval' || action.type == 'SETTINGS_UPDATE_ALL') {
reg.setupRecurrentSync(); reg.setupRecurrentSync();
} }

View File

@ -15,8 +15,12 @@ shim.fs = null;
shim.FileApiDriverLocal = null; shim.FileApiDriverLocal = null;
shim.readLocalFileBase64 = () => { throw new Error('Not implemented'); } shim.readLocalFileBase64 = () => { throw new Error('Not implemented'); }
shim.uploadBlob = () => { throw new Error('Not implemented'); } shim.uploadBlob = () => { throw new Error('Not implemented'); }
shim.setInterval = setInterval; shim.setInterval = function(fn, interval) {
shim.clearInterval = clearInterval; return setInterval(fn, interval);
}
shim.clearInterval = function(id) {
return clearInterval(id);
}
shim.detectAndSetLocale = null; shim.detectAndSetLocale = null;
module.exports = { shim }; module.exports = { shim };