React server side rendering of home page
This commit is contained in:
parent
4bfdf0dc45
commit
08e0aa7665
5 changed files with 210 additions and 8 deletions
33
app.js
33
app.js
|
@ -2,11 +2,13 @@
|
||||||
var express = require('express');
|
var express = require('express');
|
||||||
var helmet = require('helmet');
|
var helmet = require('helmet');
|
||||||
var path = require('path');
|
var path = require('path');
|
||||||
|
var url = require('url');
|
||||||
var favicon = require('serve-favicon');
|
var favicon = require('serve-favicon');
|
||||||
var logger = require('morgan');
|
var logger = require('morgan');
|
||||||
var session = require('express-session');
|
var session = require('express-session');
|
||||||
var cookieParser = require('cookie-parser');
|
var cookieParser = require('cookie-parser');
|
||||||
var flash = require('connect-flash');
|
var flash = require('connect-flash');
|
||||||
|
var compress = require('compression');
|
||||||
var bodyParser = require('body-parser');
|
var bodyParser = require('body-parser');
|
||||||
var pmongo = require('promised-mongo');
|
var pmongo = require('promised-mongo');
|
||||||
|
|
||||||
|
@ -14,13 +16,20 @@ var search = require('./routes/search');
|
||||||
var share = require('./routes/share');
|
var share = require('./routes/share');
|
||||||
var itunesProxy = require('./routes/itunes-proxy');
|
var itunesProxy = require('./routes/itunes-proxy');
|
||||||
|
|
||||||
|
var browserify = require('connect-browserify');
|
||||||
|
var React = require('react');
|
||||||
|
var nodejsx = require('node-jsx').install();
|
||||||
|
var Home = React.createFactory(require('./client').Home);
|
||||||
|
|
||||||
var app = express();
|
var app = express();
|
||||||
|
|
||||||
|
var development = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
// view engine setup
|
// view engine setup
|
||||||
app.set('views', path.join(__dirname, 'views'));
|
app.set('views', path.join(__dirname, 'views'));
|
||||||
app.set('view engine', 'ejs');
|
app.set('view engine', 'ejs');
|
||||||
|
|
||||||
|
app.use(compress());
|
||||||
app.use(favicon(__dirname + '/public/images/favicon.png'));
|
app.use(favicon(__dirname + '/public/images/favicon.png'));
|
||||||
app.use(helmet());
|
app.use(helmet());
|
||||||
app.use(logger('dev'));
|
app.use(logger('dev'));
|
||||||
|
@ -41,6 +50,13 @@ app.use(function(req, res, next) {
|
||||||
next();
|
next();
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (development) {
|
||||||
|
app.get('/javascript/bundle.js',
|
||||||
|
browserify('./client', {
|
||||||
|
debug: true,
|
||||||
|
watch: true
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
|
||||||
app.get('*', function(req,res,next) {
|
app.get('*', function(req,res,next) {
|
||||||
// force SSL
|
// force SSL
|
||||||
|
@ -59,9 +75,18 @@ app.get('*', function(req,res,next) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
app.get('/', function(req, res) {
|
app.get('/', function(req, res, next) {
|
||||||
req.db.matches.find().sort({'$natural':-1}).limit(6).toArray().then(function(docs){
|
|
||||||
res.render('index', { page: "home", recent: docs, error: req.flash('search-error') });
|
var path = url.parse(req.url).pathname;
|
||||||
|
|
||||||
|
req.db.matches.find().sort({created_at:-1}).limit(6).toArray().then(function(docs){
|
||||||
|
var recent = [];
|
||||||
|
docs.forEach(function(doc) {
|
||||||
|
recent.push(doc.services[doc._id.split("$$")[0]]);
|
||||||
|
})
|
||||||
|
|
||||||
|
var home = Home({recent: recent});
|
||||||
|
res.send('<!doctype html>\n' + React.renderToString(home).replace("</body></html>", "<script>var recent = " + JSON.stringify(recent) + "</script></body></html>"));
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
155
client/index.js
Normal file
155
client/index.js
Normal file
|
@ -0,0 +1,155 @@
|
||||||
|
/**
|
||||||
|
* @jsx React.DOM
|
||||||
|
*/
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var React = require('react');
|
||||||
|
var Router = require('react-router');
|
||||||
|
var request = require('superagent');
|
||||||
|
var ga = require('react-google-analytics');
|
||||||
|
var GAInitiailizer = ga.Initializer;
|
||||||
|
|
||||||
|
var Head = React.createClass({
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<head>
|
||||||
|
<meta charSet="utf-8" />
|
||||||
|
<meta httpEquiv="X-UA-Compatible" content="IE=edge" />
|
||||||
|
<title>Match Audio</title>
|
||||||
|
<meta name="description" content="" />
|
||||||
|
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||||
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
|
<meta name="twitter:site" content="@MatchAudio" />
|
||||||
|
<meta name="twitter:title" property="og:title" content="" />
|
||||||
|
<meta name="twitter:description" property="og:description" content="We've matched this music on Rdio, Spotify, Deezer, Beats Music, Google Music and iTunes so you can open it in the service you use." />
|
||||||
|
<meta name="twitter:image:src" property="og:image" content="" />
|
||||||
|
<meta property="og:url" content="" />
|
||||||
|
<link rel="shortcut icon" href="/images/favicon.png" />
|
||||||
|
<link href='//fonts.googleapis.com/css?family=Open+Sans:400,300,700' rel='stylesheet' type='text/css' />
|
||||||
|
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
|
||||||
|
<link rel="stylesheet" href="/stylesheets/style.css" />
|
||||||
|
</head>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Foot = React.createClass({
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<footer>
|
||||||
|
<div className="container">
|
||||||
|
<div className="row">
|
||||||
|
<div className={this.props.page == "home" ? "col-md-6 col-md-offset-3" : "col-md-12"}>
|
||||||
|
<a href="https://twitter.com/MatchAudio">Tweet</a> or <a href="https://github.com/kudos/match.audio">Fork</a>. A work in progress by <a href="http://crem.in">this guy</a>.
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
var Recent = React.createClass({
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (<div className="row">
|
||||||
|
<div className="col-md-6 col-md-offset-3">
|
||||||
|
<h2>Recently Shared</h2>
|
||||||
|
<div className="row recent">
|
||||||
|
{this.props.items.map(function(item, i){
|
||||||
|
return (<RecentItem item={item} key={i} />);
|
||||||
|
})}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var RecentItem = React.createClass({
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div className="col-sm-4 col-xs-6">
|
||||||
|
<a href={"/" + this.props.item.service + "/" + this.props.item.type + "/" + this.props.item.id}><img src={this.props.item.artwork.small} width="100%" /></a>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
var SearchForm = React.createClass({
|
||||||
|
|
||||||
|
handleSubmit: function(e) {
|
||||||
|
e.preventDefault();
|
||||||
|
var url = this.refs.url.getDOMNode().value.trim();
|
||||||
|
if (!url) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
request.post('/search').send({url:url}).end(function(res) {
|
||||||
|
window.location = "/" + res.body.service + "/" + res.body.type + "/" + res.body.id
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<form role="form" method="post" action="/search" onSubmit={this.handleSubmit}>
|
||||||
|
<div className="input-group input-group-lg">
|
||||||
|
<input type="text" name="url" placeholder="Paste link here" className="form-control" autofocus ref="url" />
|
||||||
|
<span className="input-group-btn">
|
||||||
|
<input type="submit" className="btn btn-lg btn-custom" value="Share Music" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var Home = React.createClass({
|
||||||
|
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<html>
|
||||||
|
<Head />
|
||||||
|
<body className="home">
|
||||||
|
<div className="page-wrap">
|
||||||
|
<header>
|
||||||
|
<h1><a href="/">match<span className="audio-lighten">.audio</span></a></h1>
|
||||||
|
</header>
|
||||||
|
<div className="container">
|
||||||
|
<div className="row share-form">
|
||||||
|
<div className="col-md-6 col-md-offset-3">
|
||||||
|
<SearchForm />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="row blurb">
|
||||||
|
<div className="col-md-6 col-md-offset-3">
|
||||||
|
<p>Make sharing from music services better.
|
||||||
|
We match album and track links from Rdio, Spotify, Deezer, Beats Music, Google Music and iTunes and give you back a link with all of them.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Recent items={this.props.recent} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<Foot page="home" />
|
||||||
|
<GAInitiailizer />
|
||||||
|
<script src="/javascript/bundle.js" />
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports.Home = Home;
|
||||||
|
|
||||||
|
if (typeof window !== 'undefined') {
|
||||||
|
window.onload = function() {
|
||||||
|
React.render(<Home recent={recent} />, document);
|
||||||
|
ga('create', 'UA-66209-8', 'auto');
|
||||||
|
ga('send', 'pageview');
|
||||||
|
}
|
||||||
|
}
|
25
package.json
25
package.json
|
@ -4,14 +4,27 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "node ./bin/www",
|
"start": "node ./bin/www",
|
||||||
"test": "./node_modules/mocha/bin/mocha test/**/*.js --timeout=10000"
|
"test": "./node_modules/mocha/bin/mocha test/**/*.js --timeout=10000",
|
||||||
|
"build": "NODE_ENV=production browserify ./client | uglifyjs -cm 2>/dev/null > ./public/javascript/bundle.js",
|
||||||
|
"clean": "rm -f ./public/javascript/bundle.js"
|
||||||
},
|
},
|
||||||
"engines": {
|
"engines": {
|
||||||
"node": "0.10.x"
|
"node": "0.10.x"
|
||||||
},
|
},
|
||||||
|
"browserify": {
|
||||||
|
"transform": [
|
||||||
|
[
|
||||||
|
"reactify",
|
||||||
|
{
|
||||||
|
"harmony": true
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bluebird": "^2.3.11",
|
"bluebird": "^2.3.11",
|
||||||
"body-parser": "~1.8.1",
|
"body-parser": "~1.8.1",
|
||||||
|
"compression": "^1.2.2",
|
||||||
"connect-flash": "^0.1.1",
|
"connect-flash": "^0.1.1",
|
||||||
"cookie-parser": "~1.3.3",
|
"cookie-parser": "~1.3.3",
|
||||||
"crypto-js": "^3.1.2-5",
|
"crypto-js": "^3.1.2-5",
|
||||||
|
@ -21,17 +34,25 @@
|
||||||
"express-session": "^1.9.2",
|
"express-session": "^1.9.2",
|
||||||
"helmet": "^0.5.2",
|
"helmet": "^0.5.2",
|
||||||
"morgan": "~1.3.0",
|
"morgan": "~1.3.0",
|
||||||
|
"node-jsx": "^0.12.4",
|
||||||
"node-uuid": "^1.4.2",
|
"node-uuid": "^1.4.2",
|
||||||
"promised-mongo": "^0.11.1",
|
"promised-mongo": "^0.11.1",
|
||||||
"querystring": "^0.2.0",
|
"querystring": "^0.2.0",
|
||||||
"rdio": "^1.5.2",
|
"rdio": "^1.5.2",
|
||||||
|
"react": "^0.12.1",
|
||||||
|
"react-google-analytics": "^0.2.0",
|
||||||
|
"react-router": "^0.11.4",
|
||||||
"serve-favicon": "~2.1.3",
|
"serve-favicon": "~2.1.3",
|
||||||
"spotify": "^0.3.0",
|
"spotify": "^0.3.0",
|
||||||
"superagent": "^0.21.0",
|
"superagent": "^0.21.0",
|
||||||
"superagent-bluebird-promise": "^0.5.1"
|
"superagent-bluebird-promise": "^0.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"browserify": "^7.0.0",
|
||||||
|
"connect-browserify": "^3.2.1",
|
||||||
"should": "^4.3.0",
|
"should": "^4.3.0",
|
||||||
"mocha": "^2.0.1"
|
"mocha": "^2.0.1",
|
||||||
|
"envify": "^3.2.0",
|
||||||
|
"reactify": "^0.17.1"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -100,7 +100,7 @@ h3 {
|
||||||
.btn-custom {
|
.btn-custom {
|
||||||
background-color: #FE4365;
|
background-color: #FE4365;
|
||||||
}
|
}
|
||||||
.btn-custom, .btn-custom:active, .btn-custom:hover {
|
.btn-custom, .btn-custom:active, .btn-custom:hover, .btn-custom:focus {
|
||||||
color: #fff;
|
color: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,8 @@ module.exports = function(req, res, next) {
|
||||||
services[id].lookupId(result.id, result.type).then(function(item) {
|
services[id].lookupId(result.id, result.type).then(function(item) {
|
||||||
items[id] = item;
|
items[id] = item;
|
||||||
req.db.matches.save({_id:id + "$$" + result.id, created_at: new Date(), services:items}).then(function() {
|
req.db.matches.save({_id:id + "$$" + result.id, created_at: new Date(), services:items}).then(function() {
|
||||||
res.redirect("/" + id + "/" + result.type + "/" + result.id);
|
res.json(item);
|
||||||
|
//res.redirect("/" + id + "/" + result.type + "/" + result.id);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue