Implemented message browser

This commit is contained in:
Chris Blanchard 2015-12-29 02:20:34 +00:00
parent 880649bdc0
commit 21a8e26147
4 changed files with 185 additions and 68 deletions

View File

@ -1,5 +1,177 @@
"use strict"; "use strict";
const MessageBrowser = React.createClass({
getInitialState() {
return {
browserState: "",
messages: [],
page: 0,
limit: 250,
search: ""
}
},
handleNextPage(e) {
e.preventDefault();
const page = this.state.page;
this.setState({ page: page + 1 });
this.loadMessages();
},
handlePreviousPage(e) {
e.preventDefault();
const page = this.state.page;
if (page < 1) return;
this.setState({ page: page - 1 });
this.loadMessages();
},
pageHandlers() {
let previous;
if (this.state.page > 0) {
previous = (
<a className="btn btn-xs btn-primary add-right"
onClick={this.handlePreviousPage}>Prev</a>
);
}
let next;
if (this.state.messages.length === this.state.limit) {
next = (
<a className="btn btn-xs btn-primary"
onClick={this.handleNextPage}>Next</a>
);
}
return (
<div>
{previous}
<span className="add-right">
{this.state.page}
</span>
{next}
</div>
);
},
loadMessages() {
const limit = this.state.limit;
const page = this.state.page;
let data = {
limit: limit,
page: page
};
if (this.state.search.length) {
data.query = this.state.search;
}
this.setState({ browserState: "Retrieving messages"});
$.ajax({
url: "/api/messages",
data: data
})
.done(data => {
this.setState({
messages: data.messages,
browserState: ""
});
})
.fail(error => {
console.error(error);
this.setState({
browserState: `Unable to retrieve messages.`
});
})
},
componentDidMount() {
this.loadMessages();
},
updateLimit(e) {
let newLimit = parseInt(e.target.value, 10);
if (isNaN(newLimit) || newLimit > 250) newLimit = 250;
this.setState({ limit: newLimit });
},
updateSearch(e) {
this.setState({ search: e.target.value });
},
render() {
let browserState;
if (this.state.browserState.length) {
browserState = (
<div className="col-xs-7">
<div className="well">{this.state.browserState}</div>
</div>
);
}
const messages = this.state.messages.map(message => {
return (
<tr key={message._id}>
<td className="col-xs-2">{(new Date(message.createdAt)).toString()}</td>
<td className="col-xs-3">{message.author.username}</td>
<td className="col-xs-5">{message.content}</td>
<td className="col-xs-2">{message._id}</td>
</tr>
);
});
return (
<div className="row">
<div className="col-xs-5">
<div className="form-horizontal">
<div className="form-group">
<label className="col-sm-3 control-label">Max Results</label>
<div className="col-sm-9">
<input type="number" className="form-control"
onChange={this.updateLimit}
value={this.state.limit}></input>
</div>
</div>
<div className="form-group">
<label className="col-sm-3 control-label">Search Filter</label>
<div className="col-sm-9">
<input type="text" className="form-control"
onChange={this.updateSearch}
value={this.state.search}></input>
</div>
</div>
<div className="form-group">
<div className="col-sm-offset-3 col-sm-9">
<button
className="btn btn-primary"
onClick={this.loadMessages}>Search</button>
</div>
</div>
<div className="row">
<div className="col-sm-offset-3 col-sm-9">
<p>Page Control</p>
{this.pageHandlers()}
</div>
</div>
</div>
</div>
{browserState}
<div className="col-xs-12">
<table className="table">
<thead>
<tr>
<th>Date</th>
<th>Author</th>
<th>Message</th>
<th>ID</th>
</tr>
</thead>
<tbody>
{messages}
</tbody>
</table>
</div>
</div>
);
}
});
var Chatroom = React.createClass({ var Chatroom = React.createClass({
getInitialState() { getInitialState() {
return { return {

View File

@ -41,8 +41,7 @@ describe("Messages", () => {
describe("JSON Api", () => { describe("JSON Api", () => {
it ("returns most recent messages", done => { it ("returns most recent messages", done => {
request(app) request(app)
.get("/messages") .get("/api/messages")
.set("Accept", "application/json")
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
.end((error, response) => { .end((error, response) => {
@ -56,11 +55,10 @@ describe("Messages", () => {
}); });
it ("is sensitive to limit", done => { it ("is sensitive to limit", done => {
request(app) request(app)
.get("/messages") .get("/api/messages")
.query({ .query({
limit: 1 limit: 1
}) })
.set("Accept", "application/json")
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
.end((error, response) => { .end((error, response) => {
@ -76,12 +74,11 @@ describe("Messages", () => {
it ("returns a maximum of last 250 messages"); it ("returns a maximum of last 250 messages");
it ("is sensitive to pagination", done => { it ("is sensitive to pagination", done => {
request(app) request(app)
.get("/messages") .get("/api/messages")
.query({ .query({
limit: 1, limit: 1,
page: 2 page: 2
}) })
.set("Accept", "application/json")
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
.end((error, response) => { .end((error, response) => {
@ -96,11 +93,10 @@ describe("Messages", () => {
}); });
it ("is sensitive to search terms", done => { it ("is sensitive to search terms", done => {
request(app) request(app)
.get("/messages") .get("/api/messages")
.query({ .query({
query: "5" query: "5"
}) })
.set("Accept", "application/json")
.expect("Content-Type", /json/) .expect("Content-Type", /json/)
.expect(200) .expect(200)
.end((error, response) => { .end((error, response) => {
@ -119,7 +115,6 @@ describe("Messages", () => {
it ("renders message browser", done => { it ("renders message browser", done => {
request(app) request(app)
.get("/messages") .get("/messages")
.set("Accept", "text/html; charset=utf-8")
.expect("Content-Type", /html/) .expect("Content-Type", /html/)
.expect(200) .expect(200)
.end((error, response) => { .end((error, response) => {

View File

@ -1,58 +0,0 @@
<div id="wrapper">
{{>menu}}
<div style="min-height: 750px;">
<div class="container-fluid">
<div class="row" id="authenticating">
<div class="col-lg-6 col-lg-offset-3">
<div class="add-top jumbotron jumbo-auth text-center">
<div>
<img src="/images/ensl_logo.png" class="jumbo-img" alt="ENSL Logo" />
</div>
<br />
<h3>Authenticating your ENSL account</h3>
<br />
<div>
<img src="/images/spinner.svg" class="spinner" alt="Loading" />
</div>
</div>
</div>
</div>
<div class="row" id="gather-banned" style="display:none;">
<div class="col-lg-6 col-lg-offset-3">
<div class="add-top jumbotron jumbo-auth text-center">
<div>
<img src="/images/ensl_logo.png" alt="ENSL Logo" />
</div>
<h3>You're currently barred from joining gathers</h3>
<h3><small>Either wait for the ban to expire or talk to an admin to get it lifted</small></h3>
<br />
<p><a class="btn btn-primary btn-lg" href="{{ redirect }}" role="button">See the ban list</a></p>
</div>
</div>
</div>
<div class="row" id="auth-required" style="display:none;">
<div class="col-lg-6 col-lg-offset-3">
<div class="add-top jumbotron jumbo-auth text-center">
<div>
<img src="/images/ensl_logo.png" alt="ENSL Logo" />
</div>
<h3>You need to be logged in to the ENSL website ({{ redirect }}) to access gathers</h3>
<h3><small>If you are logged on, try visiting a few pages on ENSL.org so the server can update your cookies</small></h3>
<h3><small>If this error persists please contact an admin to fix it</small></h3>
<br />
<p><a class="btn btn-primary btn-lg" href="{{ redirect }}" role="button">Go to website</a></p>
</div>
</div>
</div>
<div class="row">
<div class="col-md-2 hidden-xs">
<ul class="nav" id="side-menu"></ul>
</div>
<div class="col-md-4" id="chatroom"></div>
<div class="col-md-6" id="gathers"></div>
<div class="col-md-6 col-md-offset-6" id="archived-gathers"></div>
</div>
</div>
</div>
</div>
{{>foot}}

View File

@ -6,10 +6,18 @@
<div class="col-lg-12"> <div class="col-lg-12">
<h2>Message Browser</h2> <h2>Message Browser</h2>
</div> </div>
</div>
<div class="row">
<div class="col-lg-12"> <div class="col-lg-12">
<div id="message-browser"></div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
<script>
$(function () {
React.render(React.createElement(MessageBrowser, null), document.getElementById("message-browser"));
});
</script>