First version to show (raw) email bodies.
Made xwebmail work with new handlers package. Pulls important headers from the database and provides extremely basic folder view on webpage. Reverted layout customizations returning folder view to original wider width. JS App now handles all the rendering, index.html only contains placeholder with background to indicate loading.
This commit is contained in:
@@ -1,15 +1,39 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var App = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
// TODO make this change by clicking on folder view.
|
||||
source: "/l/[all]",
|
||||
folderContent: [],
|
||||
currentMessage: null
|
||||
};
|
||||
},
|
||||
|
||||
componentWillMount: function() {
|
||||
$.get(this.state.source, function(result) {
|
||||
this.setState({folderContent: result});
|
||||
this.setMessage(result[0]);
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
setMessage: function(msg) {
|
||||
this.setState({currentMessage: msg});
|
||||
},
|
||||
|
||||
render: function() {
|
||||
return React.DOM.div({className: ''});
|
||||
if (this.state.currentMessage == null) {
|
||||
return (<div id="content" className="loading">Loading...</div>);
|
||||
}
|
||||
return (
|
||||
<div id="layout" className="content pure-g">
|
||||
<NavView source="//mail.z.xinu.tv/unread"/>
|
||||
{/* TODO make '[all]' be set by clicking folders. */}
|
||||
<FolderView handleMessage={this.setMessage} folderContent={this.state.folderContent}/>
|
||||
<MainView message={this.state.currentMessage}/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
React.DOM.div({id: 'layout', className: 'content pure-g'},
|
||||
<UnreadCount source="//mail.z.xinu.tv/unread" />,
|
||||
// TODO make '[all]' be set by clicking folders.
|
||||
<FolderView source="/l/[all]" />),
|
||||
$('body').get(0)
|
||||
);
|
||||
React.renderComponent(<App/>, document.body);
|
||||
|
||||
@@ -1,64 +1,47 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
/*
|
||||
<div class="email-item email-item-selected pure-g">
|
||||
<div class="pure-u">
|
||||
<img class="email-avatar" alt="Tilo Mitra's avatar" height="64" width="64" src="img/common/tilo-avatar.png">
|
||||
</div>
|
||||
var Summary = React.createClass({
|
||||
handleClick: function(e) {
|
||||
this.props.handleMessage(this.props.message);
|
||||
},
|
||||
|
||||
<div class="pure-u-3-4">
|
||||
<h5 class="email-name">Tilo Mitra</h5>
|
||||
<h4 class="email-subject">Hello from Toronto</h4>
|
||||
<p class="email-desc">
|
||||
Hey, I just wanted to check in with you from Toronto. I got here earlier today.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
*/
|
||||
|
||||
var FolderView = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
list: {}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
$.get(this.props.source, function(result) {
|
||||
console.log('FolderView $.get', result);
|
||||
this.setState({list: result});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
// TODO:
|
||||
// - fill this out with data
|
||||
// - make unread conditional
|
||||
// - remove profile pic
|
||||
// - drop message excerpt
|
||||
// - trim 'Re:' from messages and group/thread them.
|
||||
return (
|
||||
<div id="list" className="pure-u-1">
|
||||
<div className="email-item email-item-selected pure-g">
|
||||
<div className="pure-u">
|
||||
<img className="email-avatar" alt="Tilo Mitra's avatar" height="64" width="64" src="img/common/tilo-avatar.png" />
|
||||
</div>
|
||||
|
||||
<div className="pure-u-3-4">
|
||||
<h5 className="email-name">Tilo Mitra</h5>
|
||||
<h4 className="email-subject">Hello from Toronto</h4>
|
||||
<p className="email-desc">
|
||||
Hey, I just wanted to check in with you from Toronto. I got here earlier today.
|
||||
</p>
|
||||
</div>
|
||||
render: function() {
|
||||
var m = this.props.message;
|
||||
return (
|
||||
<div key={m.Hash} onClick={this.handleClick}
|
||||
className={m.Seen ? "email-item pure-g" : "email-item pure-g email-item-unread"}>
|
||||
<div className="pure-u">
|
||||
<h5 className="email-name">{m.From}</h5>
|
||||
<h4 className="email-subject">{m.Subject}</h4>
|
||||
<p className="email-desc">{m.Summary}</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
React.renderComponent(
|
||||
// TODO make '[all]' be set by clicking folders.
|
||||
<FolderView source="/l/[all]" />,
|
||||
$('#list').get(0)
|
||||
);
|
||||
var FolderView = React.createClass({
|
||||
propTypes: {
|
||||
folderContent: React.PropTypes.array.isRequired,
|
||||
handleMessage: React.PropTypes.func.isRequired
|
||||
},
|
||||
|
||||
// Highlight types: email-item-{selected,unread} and none
|
||||
render: function() {
|
||||
// TODO:
|
||||
// - fill this out with data
|
||||
// - make unread conditional
|
||||
// - remove profile pic
|
||||
// - drop message excerpt
|
||||
// - trim 'Re:' from messages and group/thread them.
|
||||
var messages = this.props.folderContent,
|
||||
view = this;
|
||||
return (
|
||||
<div id="list" className="pure-u-1">
|
||||
{messages.map(function(m) {
|
||||
return <Summary key={m.Hash} handleMessage={view.props.handleMessage} message={m}/>
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
42
static/js/main.js
Normal file
42
static/js/main.js
Normal file
@@ -0,0 +1,42 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var MainView = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
body: ""
|
||||
};
|
||||
},
|
||||
|
||||
render: function() {
|
||||
$.get("/raw/" + this.props.message.Hash, function(data) {
|
||||
var body = data.substring(data.indexOf('\n\n')+2);
|
||||
this.setState({body: body});
|
||||
}.bind(this));
|
||||
|
||||
// TODO:
|
||||
// - trim 'Re:' from messages and group/thread them.
|
||||
var m = this.props.message;
|
||||
return (
|
||||
<div id="main" className="pure-u-1">
|
||||
<div className="email-content">
|
||||
<div className="email-content-header pure-g">
|
||||
<div className="pure-u-1">
|
||||
<h1 className="email-content-title">{m.Subject}</h1>
|
||||
<p className="email-content-subtitle">
|
||||
From <a>{m.From}</a> at <span className="date">{m.Date.toLocaleString()}</span>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div className="email-content-controls pure-u-1">
|
||||
<button className="secondary-button pure-button">Reply</button>
|
||||
<button className="secondary-button pure-button">Forward</button>
|
||||
<a href={"/raw/"+m.Hash} className="secondary-button pure-button">Original</a>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="email-content-body"><pre>{this.state.body}</pre></div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
36
static/js/nav.js
Normal file
36
static/js/nav.js
Normal file
@@ -0,0 +1,36 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
var NavView = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
unreadCount: {}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
$.get(this.props.source, function(result) {
|
||||
console.log('NavView $.get', result);
|
||||
this.setState({unreadCount: result});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var keys = Object.keys(this.state.unreadCount).sort();
|
||||
var lis = [];
|
||||
for (var i in keys) {
|
||||
var key = keys[i];
|
||||
var value = this.state.unreadCount[key];
|
||||
lis.push(<li key={key}><a href="#">{key}</a></li>);
|
||||
}
|
||||
return (
|
||||
<div id="nav" className="pure-u">
|
||||
<div className="nav-inner">
|
||||
<button className="primary-button pure-button">Compose</button>
|
||||
<div id="unread-list" className="pure-menu pure-menu-open">
|
||||
<ul>{lis}</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
});
|
||||
@@ -1,42 +0,0 @@
|
||||
/** @jsx React.DOM */
|
||||
|
||||
// TODO rename this nav.
|
||||
|
||||
var UnreadCount = React.createClass({
|
||||
getInitialState: function() {
|
||||
return {
|
||||
unreadCount: {}
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount: function() {
|
||||
$.get(this.props.source, function(result) {
|
||||
console.log('UnreadCount $.get', result);
|
||||
this.setState({unreadCount: result});
|
||||
}.bind(this));
|
||||
},
|
||||
|
||||
render: function() {
|
||||
var keys = Object.keys(this.state.unreadCount).sort();
|
||||
console.log('unread keys', keys);
|
||||
var lis = [];
|
||||
for (var i in keys) {
|
||||
var key = keys[i];
|
||||
var value = this.state.unreadCount[key];
|
||||
var dom = React.DOM.li({key: key}, React.DOM.a({href: '#'},
|
||||
React.DOM.span({className: 'email-count'}, '(', value, ')'),
|
||||
' ', key
|
||||
));
|
||||
lis.push(dom);
|
||||
}
|
||||
console.log('Found', lis);
|
||||
// TODO rewrite as JSX template? How to handle repreating fields?
|
||||
return React.DOM.div({id: 'nav', className: 'pure-u'},
|
||||
React.DOM.div({className: 'nav-inner'},
|
||||
React.DOM.div({
|
||||
id: 'unread-list',
|
||||
className: 'pure-menu pure-menu-open'
|
||||
}, React.DOM.ul({},
|
||||
lis))));
|
||||
}
|
||||
});
|
||||
Reference in New Issue
Block a user