1
0
mirror of https://github.com/laurent22/joplin.git synced 2024-12-24 10:27:10 +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_);
}
hasGui() {
return this.gui() && !this.gui().isDummy();
}
findCommandByName(name) {
if (this.commands_[name]) return this.commands_[name];

View File

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

View File

@ -17,14 +17,12 @@ const { ElectronAppWrapper } = require('./ElectronAppWrapper');
class Application extends BaseApplication {
constructor(electronApp) {
constructor() {
super();
this.electronApp_ = electronApp;
}
gui() {
return this.gui_;
hasGui() {
return true;
}
async start(argv) {
@ -32,11 +30,11 @@ class Application extends BaseApplication {
this.initRedux();
this.gui_ = new ElectronAppWrapper(this.electronApp_, this, this.store());
//this.gui_ = new ElectronAppWrapper(this.electronApp_, this, this.store());
try {
this.gui_.setLogger(this.logger());
await this.gui().start();
// this.gui_.setLogger(this.logger());
// await this.gui().start();
// 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
@ -57,7 +55,7 @@ class Application extends BaseApplication {
id: Setting.value('activeFolderId'),
});
} catch (error) {
await this.gui_.exit();
//await this.gui_.exit();
throw error;
}
}
@ -67,7 +65,8 @@ class Application extends BaseApplication {
let application_ = null;
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_;
}

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 {
constructor() {
@ -6,34 +8,32 @@ class ItemList extends React.Component {
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({
topItemIndex: this.topItemIndex(),
bottomItemIndex: this.bottomItemIndex(),
topItemIndex: topItemIndex,
bottomItemIndex: bottomItemIndex,
});
}
componentWillMount() {
this.updateStateItemIndexes();
}
componentWillReceiveProps(newProps) {
this.updateStateItemIndexes(newProps);
}
onScroll(scrollTop) {
this.scrollTop_ = scrollTop;
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;
this.updateStateItemIndexes();
}
render() {

View File

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

View File

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

View File

@ -3,13 +3,47 @@ const { render } = require('react-dom');
const { createStore } = require('redux');
const { connect, Provider } = require('react-redux');
const { NoteList } = require('./gui/NoteList.min.js');
const { NoteText } = require('./gui/NoteText.min.js');
const { NoteList } = require('./NoteList.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 {
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() {
const style = {
width: this.props.size.width,
@ -33,7 +67,7 @@ class ReactRootComponent extends React.Component {
return (
<div style={style}>
<NoteList itemHeight={40} style={noteListStyle}></NoteList>
<NoteText style={noteTextStyle}></NoteText>
{/*<NoteText style={noteTextStyle}></NoteText>*/}
</div>
);
}
@ -43,6 +77,7 @@ class ReactRootComponent extends React.Component {
const mapStateToProps = (state) => {
return {
size: state.windowContentSize,
appState: state.appState,
};
};

View File

@ -7,6 +7,7 @@
</head>
<body>
<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>
</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);
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');
const { ElectronAppWrapper } = require('./ElectronAppWrapper');
const { initBridge } = require('./bridge');
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;
const wrapper = new ElectronAppWrapper(electronApp);
// 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);
initBridge(wrapper);
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:');
wrapper.start().catch((error) => {
console.error('Electron App fatal 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(', ');
}
hasGui() {
return false;
}
generalMiddleware() {
const middleware = store => next => async (action) => {
this.logger().debug('Reducer action', this.reducerActionToString(action));
@ -213,7 +217,7 @@ class BaseApplication {
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();
}

View File

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