Add socket.io, sync on button click
parent
ea874385c1
commit
606865864b
|
@ -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)
|
||||
|
|
|
@ -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 }; });
|
||||
|
|
|
@ -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 };
|
||||
});
|
|
@ -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);
|
||||
}
|
||||
});
|
42
Store.js
42
Store.js
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
};
|
|
@ -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;
|
||||
};
|
2
mail.js
2
mail.js
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
|
|
Loading…
Reference in New Issue