Add socket.io, sync on button click

master
Vitaliy Filippov 2016-10-03 16:55:14 +03:00
parent ea874385c1
commit 606865864b
9 changed files with 210 additions and 25 deletions

View File

@ -45,7 +45,13 @@ var DropDownButton = module.exports = React.createClass({
if (this.props.whole || this.props.checkable && this.state.pressed)
this.toggle();
if (this.props.checkable)
{
this.setState({ checked: !this.state.checked });
if (this.props.onClick)
this.props.onClick(this.state.checked);
}
else if (this.props.onClick)
this.props.onClick();
ev.stopPropagation();
},
onClickDown: function(ev)

View File

@ -2,17 +2,19 @@ const React = require('react');
const AccountFolders = require('./AccountFolders.js');
const DropDownButton = require('./DropDownButton.js');
const Store = require('./Store.js');
const StoreListener = require('./StoreListenerClass.js');
const MailProgress = require('./MailProgress.js');
var FolderList = module.exports = React.createClass({
var FolderList = React.createClass({
render: function()
{
var self = this;
return <div className={"folder-list"+(self.props.progress !== undefined ? ' progress-visible' : '')}>
return <div className={"folder-list"+(self.props.progressText ? ' progress-visible' : '')}>
<div className="top-border-gradient"></div>
<div className="bottom-border-gradient"></div>
<div className="actions">
<a className="button"><img src="icons/compose.png" /> Compose</a>
<DropDownButton dropdownId="check-send" className="check-send" icon="mail_check_send" />
<DropDownButton dropdownId="check-send" className="check-send" icon="mail_check_send" onClick={self.onClickCheckSend} />
</div>
// TODO: keyboard navigation
<div className="listview" tabIndex="1">
@ -21,14 +23,13 @@ var FolderList = module.exports = React.createClass({
onSelect={self.onSelectFolder} selected={self.state.selectedAccount == i ? self.state.selectedFolder : -1} account={account} />
})}
</div>
<div className="progress-bar" ref="pbar">
<div className="pending" style={{ width: self.state.pbarWidth }}>Loading database ({self.props.progress||0}%)</div>
<div className="clip" style={{ width: (self.props.progress||0)+'%', overflow: 'hidden' }}>
<div className="done" ref="pdone" style={{ width: self.state.pbarWidth }}>Loading database ({self.props.progress||0}%)</div>
</div>
</div>
<MailProgress />
</div>
},
onClickCheckSend: function()
{
Store.startResync();
},
onSelectFolder: function(accIndex, folderIndex)
{
var acc = this.props.accounts[accIndex];
@ -39,19 +40,8 @@ var FolderList = module.exports = React.createClass({
},
getInitialState: function()
{
return { pbarWidth: '', selectedAccount: -1, selectedFolder: -1 };
},
onResize: function()
{
this.setState({ pbarWidth: this.refs.pbar.offsetWidth });
},
componentDidMount: function()
{
window.addEventListener('resize', this.onResize);
this.onResize();
},
componentWillUnmount: function()
{
window.removeEventListener('resize', this.onResize);
return { selectedAccount: -1, selectedFolder: -1 };
}
});
module.exports = StoreListener(FolderList, (data) => { return { accounts: data.accounts, progressText: data.progressText }; });

7
MailProgress.js Normal file
View File

@ -0,0 +1,7 @@
const StoreListener = require('./StoreListenerClass.js');
const ProgressBar = require('./ProgressBar.js');
module.exports = StoreListener(ProgressBar, function(data)
{
return { text: data.progressText, progress: data.progressPercent };
});

37
ProgressBar.js Normal file
View File

@ -0,0 +1,37 @@
const React = require('react');
var ProgressBar = module.exports = React.createClass({
render: function()
{
return <div className="progress-bar" ref="pbar" style={{ display: this.props.text ? '' : 'none' }}>
<div className="pending" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
<div className="clip" style={{ width: (this.props.progress||0)+'%', overflow: 'hidden' }}>
<div className="done" ref="pdone" style={{ width: this.state.width }}>{this.props.text} ({this.props.progress||0}%)</div>
</div>
</div>
},
componentDidUpdate: function(prevProps, prevState)
{
if (!prevState.width)
{
setTimeout(this.onResize, 50);
}
},
getInitialState: function()
{
return { width: '' };
},
onResize: function()
{
this.setState({ width: this.refs.pbar.offsetWidth });
},
componentDidMount: function()
{
window.addEventListener('resize', this.onResize);
this.onResize();
},
componentWillUnmount: function()
{
window.removeEventListener('resize', this.onResize);
}
});

View File

@ -1,4 +1,5 @@
const superagent = require('superagent');
const socket_io = require('socket.io-client');
const _ = require('./I18n.js');
var Store = module.exports = {
@ -31,6 +32,38 @@ var Store = module.exports = {
this.data[k] = v;
(this.listeners || []).map(i => i());
},
setAll: function(obj)
{
for (var k in obj)
this.data[k] = obj[k];
(this.listeners || []).map(i => i());
},
startIo: function()
{
var self = this;
this.io = socket_io('', { path: window.location.pathname.replace(/[^\/]+$/, 'backend/socket.io') });
this.io.on('sync', function(params)
{
if (params.state == 'start')
{
self.setAll({ progressText: 'Syncing '+params.email+' / '+params.folder, progressPercent: 0 });
}
else if (params.state == 'progress')
{
self.setAll({ progressPercent: Math.round(100*params.done/(params.total||1)) });
}
else if (params.state == 'finish-box')
{
self.setAll({ progressPercent: 100 });
}
else if (params.state == 'complete')
{
self.setAll({ progressText: '', progressPercent: 0 });
}
self.set('sync', params.progress);
});
},
loadAccounts: function()
{
@ -120,5 +153,14 @@ var Store = module.exports = {
{
callback(res.body.msg);
});
},
startResync: function()
{
superagent.post('backend/sync').send().end(function(err, res)
{
});
}
};
Store.startIo();

52
StoreListenerClass.js Normal file
View File

@ -0,0 +1,52 @@
const React = require('react');
const Store = require('./Store.js');
// "react-redux connect()"-like example
class StoreListener extends React.Component
{
componentDidMount()
{
Store.on(this.update);
}
componentWillUnmount()
{
Store.un(this.update);
}
update()
{
var newState = this.mapStateToProps(Store.data);
for (var i in newState)
{
if (this.state[i] != newState[i])
{
this.setState(newState);
return;
}
}
}
render()
{
var props = { ...this.initial, ...this.props, ...this.state };
return React.createElement(this.wrappedComponent, props);
}
}
module.exports = function(component, map, initial)
{
var cl = class extends StoreListener
{
constructor(props, context, updater)
{
super(props, context, updater);
this.wrappedComponent = component;
this.mapStateToProps = map;
this.initial = initial;
this.state = map(Store.data);
this.update = this.update.bind(this);
}
};
return cl;
};

50
StoreListenerWrapper.js Normal file
View File

@ -0,0 +1,50 @@
// НЕ РАБОТАЕТ!
const React = require('react');
const Store = require('./Store.js');
// "react-redux connect()"-like example
var StoreListener = React.createClass({
componentDidMount: function()
{
Store.on(this.update);
},
componentWillUnmount: function()
{
Store.un(this.update);
},
update: function()
{
var newState = this.mapStateToProps(Store.data);
for (var i in newState)
{
if (this.state[i] != newState[i])
{
this.setState(newState);
return;
}
}
},
getInitialState: function()
{
return { ...this.initial, ...this.mapStateToProps(Store.data) };
},
render: function()
{
var props = { ...this.initial, ...this.props, ...this.state };
return React.createElement(this.wrappedComponent, props);
}
});
module.exports = function(component, map, initial)
{
var fn = function(props, context, updater)
{
StoreListener.call(this, props, context, updater);
this.wrappedComponent = component;
this.mapStateToProps = map;
this.initial = initial||{};
};
fn.prototype = Object.create(StoreListener);
fn.prototype.constructor = fn;
return fn;
};

View File

@ -33,7 +33,7 @@ var AllTabs = StoreListener(TabPanel, function(data)
ReactDOM.render(
<div>
{AllDropdowns()}
{StoreListener(FolderList, (data) => { return { accounts: data.accounts }; }, { progress: 33 })}
<FolderList />
{AllTabs}
</div>,
document.body

View File

@ -27,7 +27,8 @@
"uglifyjs": "latest",
"uglifyify": "latest",
"eslint": "latest",
"superagent": "latest"
"superagent": "latest",
"socket.io-client": "latest"
},
"scripts": {
"compile": "browserify -t babelify -t uglifyify mail.js | uglifyjs -cm > mail.c.js",