From 0650e6b3b81771b79ed68e4a1d19c589b35f5798 Mon Sep 17 00:00:00 2001 From: Jonathan Cremin Date: Mon, 15 Jun 2015 02:34:39 +0100 Subject: [PATCH] Add more ES6 --- app.js | 22 ++-- lib/react-handler.js | 2 +- package.json | 16 ++- public/config.js | 9 +- routes/index.js | 2 +- routes/share.js | 2 +- views/{app.jsx => app.js} | 37 +++---- views/error.js | 37 +++++++ views/error.jsx | 39 ------- views/{faq.jsx => faq.js} | 6 +- views/foot.js | 18 ++++ views/foot.jsx | 20 ---- views/head.js | 37 +++++++ views/head.jsx | 38 ------- views/{home.jsx => home.js} | 77 +++++++------- views/share.js | 196 +++++++++++++++++++++++++++++++++++ views/share.jsx | 199 ------------------------------------ 17 files changed, 374 insertions(+), 383 deletions(-) rename views/{app.jsx => app.js} (53%) create mode 100644 views/error.js delete mode 100644 views/error.jsx rename views/{faq.jsx => faq.js} (95%) create mode 100644 views/foot.js delete mode 100644 views/foot.jsx create mode 100644 views/head.js delete mode 100644 views/head.jsx rename views/{home.jsx => home.js} (55%) create mode 100644 views/share.js delete mode 100644 views/share.jsx diff --git a/app.js b/app.js index 4dca616..667482a 100644 --- a/app.js +++ b/app.js @@ -4,7 +4,7 @@ import route from 'koa-route'; import logger from 'koa-logger'; import favicon from 'koa-favicon'; import compress from 'koa-compress'; -import staticHandler from 'koa-static'; +import staticHandler from 'koa-file-server'; import bodyparser from 'koa-bodyparser'; import React from 'react'; import co from 'co'; @@ -13,7 +13,7 @@ import index from './routes/index'; import search from './routes/search'; import share from './routes/share'; import itunesProxy from './routes/itunes-proxy'; -import {routes} from './views/app.jsx'; +import {routes} from './views/app'; import zlib from 'zlib'; import createHandler from './lib/react-handler'; @@ -46,7 +46,7 @@ app.use(bodyparser()); app.use(compress({flush: zlib.Z_SYNC_FLUSH })); app.use(favicon(path.join(__dirname, '/public/images/favicon.png'))); app.use(logger()); -app.use(staticHandler(path.join(__dirname, 'public'))); +app.use(staticHandler({root: 'public', maxage: 31536000000})); let mongo = {}; @@ -96,7 +96,17 @@ app.use(route.get('*', function* () { module.exports = app; if (!module.parent) { - app.listen(process.env.PORT || 3000, function() { - debug('Koa server listening on port ' + (process.env.PORT || 3000)); - }); + if (process.env.LOCALHOST_KEY) { + require('spdy').createServer({ + key: process.env.LOCALHOST_KEY, + cert: process.env.LOCALHOST_CRT + }, app.callback()).listen(3000, function() { + debug('Koa SPDY server listening on port ' + (process.env.PORT || 3000)); + }); + } else { + app.listen(process.env.PORT || 3000, function() { + debug('Koa HTTP server listening on port ' + (process.env.PORT || 3000)); + }); + } + } diff --git a/lib/react-handler.js b/lib/react-handler.js index 98a04b6..dda415a 100644 --- a/lib/react-handler.js +++ b/lib/react-handler.js @@ -1,4 +1,4 @@ -var Router = require('react-router'); +import Router from 'react-router'; export default function* (routes, url) { let router = Router.create({ diff --git a/package.json b/package.json index 719a6d7..bb1aa32 100644 --- a/package.json +++ b/package.json @@ -8,8 +8,7 @@ "build": "babel -d public/views views", "watch": "parallelshell \"npm run watch-js\" \"npm run watch-server\"", "watch-server": "nodemon -x \"node -r 'babel/register'\" -e js,jsx -i public/ app.js", - "watch-js": "babel -wd public/views views", - "clean": "rm -f ./public/scripts/bundle.js", + "watch-js": "babel --modules system -wd public/views views", "postinstall": "jspm install" }, "engines": { @@ -32,14 +31,14 @@ "browserify": "^10.1.3", "co": "^4.5.4", "debug": "^2.1.1", - "jspm": "^0.15.7", + "jspm": "^0.16.0-beta", "koa": "^0.21.0", "koa-bodyparser": "^2.0.0", "koa-compress": "^1.0.8", "koa-favicon": "^1.2.0", + "koa-file-server": "^2.3.1", "koa-logger": "^1.2.2", "koa-route": "^2.4.0", - "koa-static": "^1.4.9", "moment": "^2.10.3", "mongodb-promisified": "^1.0.2", "node-uuid": "^1.4.2", @@ -51,8 +50,7 @@ "reactify": "^1.1.1", "spotify": "^0.3.0", "superagent": "^1.2.0", - "superagent-bluebird-promise": "^2.0.2", - "uglifyjs": "^2.4.10" + "superagent-bluebird-promise": "^2.0.2" }, "devDependencies": { "co-mocha": "^1.1.0", @@ -61,19 +59,19 @@ "mocha": "^2.1.0", "nodemon": "^1.3.7", "parallelshell": "^1.1.1", - "should": "^6.0.3" + "should": "^6.0.3", + "spdy": "^1.32.0" }, "jspm": { "directories": { "baseURL": "public" }, "dependencies": { - "babel-core": "npm:babel-core@^5.5.8", "jsx": "github:floatdrop/plugin-jsx@^1.1.0", "react": "npm:react@0.13.3", "react-google-analytics": "npm:react-google-analytics@0.2.0", "react-router": "npm:react-router@0.13.3", - "superagent": "npm:superagent@^1.2.0" + "superagent": "npm:superagent@1.2.0" }, "devDependencies": { "babel": "npm:babel-core@^5.1.13", diff --git a/public/config.js b/public/config.js index c1b734f..89e9cfa 100644 --- a/public/config.js +++ b/public/config.js @@ -1,5 +1,6 @@ System.config({ "baseURL": "/", + "defaultJSExtensions": true, "transpiler": "babel", "babelOptions": { "optional": [ @@ -7,15 +8,15 @@ System.config({ ] }, "paths": { - "*": "*.js", - "*.jsx": "*.js", - "github:*": "jspm_packages/github/*.js", - "npm:*": "jspm_packages/npm/*.js" + "github:*": "jspm_packages/github/*", + "npm:*": "jspm_packages/npm/*" } }); System.config({ "map": { + "babel": "npm:babel-core@5.5.8", + "babel-runtime": "npm:babel-runtime@5.5.8", "core-js": "npm:core-js@0.9.17", "jsx": "github:floatdrop/plugin-jsx@1.1.0", "react": "npm:react@0.13.3", diff --git a/routes/index.js b/routes/index.js index 0a5b961..75df717 100644 --- a/routes/index.js +++ b/routes/index.js @@ -2,7 +2,7 @@ import React from 'react'; import createHandler from '../lib/react-handler'; -import {routes} from '../views/app.jsx'; +import {routes} from '../views/app'; module.exports = function* () { let recents = []; diff --git a/routes/share.js b/routes/share.js index 5806b71..8432779 100644 --- a/routes/share.js +++ b/routes/share.js @@ -1,6 +1,6 @@ import React from 'react'; import createHandler from '../lib/react-handler'; -import {routes} from '../views/app.jsx'; +import {routes} from '../views/app'; import services from '../lib/services'; let formatAndSort = function(matches, serviceId) { diff --git a/views/app.jsx b/views/app.js similarity index 53% rename from views/app.jsx rename to views/app.js index 8b99986..3a1c28c 100644 --- a/views/app.jsx +++ b/views/app.js @@ -1,18 +1,12 @@ -'use strict'; +import React from 'react'; +import Router, {Route, DefaultRoute, NotFoundRoute, RouteHandler} from 'react-router'; +import ga, {Initializer as GAInitiailizer} from 'react-google-analytics'; +import Home from './home'; +import Share from './share'; +import Head from './head'; +import ErrorView from './error'; -var React = require('react'); -var Router = require('react-router'); -var Route = require('react-router').Route; -var DefaultRoute = require('react-router').DefaultRoute; -var NotFoundRoute = require('react-router').NotFoundRoute; -var RouteHandler = require('react-router').RouteHandler; -var Home = require('./home.jsx'); -var Share = require('./share.jsx'); -var Head = require('./head.jsx'); -var ga = require('react-google-analytics'); -var GAInitiailizer = ga.Initializer; - -var App = React.createClass({ +let App = React.createClass({ render: function () { return ( @@ -20,25 +14,26 @@ var App = React.createClass({ - - - + + + ); } }); -var routes = ( + +let routes = ( - + ); if (typeof window !== 'undefined') { - console.log("HEREER") + console.info('Time since page started rendering: ' + (Date.now() - timerStart) + 'ms'); Router.run(routes, Router.HistoryLocation, function (Handler) { if (typeof window.recents !== 'undefined') { React.render(, document); @@ -50,4 +45,4 @@ if (typeof window !== 'undefined') { ga('send', 'pageview'); } -module.exports.routes = routes; +export {routes}; diff --git a/views/error.js b/views/error.js new file mode 100644 index 0000000..a9143e7 --- /dev/null +++ b/views/error.js @@ -0,0 +1,37 @@ +import React from 'react'; +import Head from './head'; +import Foot from './foot'; + +export default React.createClass({ + + render: function() { + return ( + + + +
+
+
+
+ +
+
+
+
+
+
+

{this.props.status}

+

{this.props.message}

+
{this.props.error.stack || ''}
+
+
+
+ +
+ + + ); + } +}); diff --git a/views/error.jsx b/views/error.jsx deleted file mode 100644 index 5105c84..0000000 --- a/views/error.jsx +++ /dev/null @@ -1,39 +0,0 @@ -"use strict"; - -var React = require("react"); -var Head = require("./head.jsx"); -var Foot = require("./foot.jsx"); - -module.exports = React.createClass({ - - render: function() { - return ( - - - -
-
-
-
- -
-
-
-
-
-
-

{this.props.status}

-

{this.props.message}

-
{this.props.error.stack || ""}
-
-
-
- -
- - - ); - } -}); diff --git a/views/faq.jsx b/views/faq.js similarity index 95% rename from views/faq.jsx rename to views/faq.js index 87b3b2d..a8ce555 100644 --- a/views/faq.jsx +++ b/views/faq.js @@ -1,8 +1,6 @@ -'use strict'; +import React from 'react'; -var React = require('react'); - -module.exports = React.createClass({ +export default React.createClass({ render: function() { return ( diff --git a/views/foot.js b/views/foot.js new file mode 100644 index 0000000..8d2d89a --- /dev/null +++ b/views/foot.js @@ -0,0 +1,18 @@ +import React from 'react'; + +export default React.createClass({ + + render: function() { + return ( + + ); + } +}); diff --git a/views/foot.jsx b/views/foot.jsx deleted file mode 100644 index 6dcdf10..0000000 --- a/views/foot.jsx +++ /dev/null @@ -1,20 +0,0 @@ -"use strict"; - -var React = require("react"); - -module.exports = React.createClass({ - - render: function() { - return ( - - ); - } -}); diff --git a/views/head.js b/views/head.js new file mode 100644 index 0000000..00275ca --- /dev/null +++ b/views/head.js @@ -0,0 +1,37 @@ +import React from 'react'; +import {State} from 'react-router'; + +export default React.createClass({ + + mixins: [ State ], + contextTypes: { + router: React.PropTypes.func.isRequired + }, + render: function() { + var image = this.props.shares ? this.props.shares[0].artwork.large : 'https://match.audio/images/logo-512.png'; + var title = this.props.shares ? this.props.shares[0].name + ' by ' + this.props.shares[0].artist.name : 'Match Audio'; + var shareUrl = 'https://match.audio/' + this.getParams().service + '/' + this.getParams().type + '/' + this.getParams().id; + return ( + + + + + {this.props.shares ? 'Listen to ' + this.props.shares[0].name + ' by ' + this.props.shares[0].artist.name + ' on Match Audio' : 'Match Audio'} + + + + + + + + + + + + + + + + ); + } +}); diff --git a/views/head.jsx b/views/head.jsx deleted file mode 100644 index fae63a7..0000000 --- a/views/head.jsx +++ /dev/null @@ -1,38 +0,0 @@ -"use strict"; - -var React = require("react"); -var Router = require("react-router"); - -module.exports = React.createClass({ - - mixins: [ Router.State ], - contextTypes: { - router: React.PropTypes.func.isRequired - }, - render: function() { - var image = this.props.shares ? this.props.shares[0].artwork.large : "https://match.audio/images/logo-512.png"; - var title = this.props.shares ? this.props.shares[0].name + " by " + this.props.shares[0].artist.name : "Match Audio"; - var shareUrl = "https://match.audio/" + this.getParams().service + "/" + this.getParams().type + "/" + this.getParams().id; - return ( - - - - {this.props.shares ? "Listen to " + this.props.shares[0].name + " by " + this.props.shares[0].artist.name + " on Match Audio" : "Match Audio"} - - - - - - - - - - - - - - - - ); - } -}); diff --git a/views/home.jsx b/views/home.js similarity index 55% rename from views/home.jsx rename to views/home.js index 316a94a..1e77319 100644 --- a/views/home.jsx +++ b/views/home.js @@ -1,19 +1,16 @@ -"use strict"; - -var React = require("react"); -var request = require("superagent"); -var Router = require("react-router"); -var Link = require("react-router").Link; -var Faq = require("./faq.jsx"); -var Foot = require("./foot.jsx"); +import React from 'react'; +import request from 'superagent'; +import {Navigation, State, Link} from 'react-router'; +import Faq from './faq'; +import Foot from './foot'; var Recent = React.createClass({ render: function() { - return (
-
+ return (
+

Recently Shared

-
+
{this.props.recents.map(function(item, i){ return (); })} @@ -31,9 +28,9 @@ var RecentItem = React.createClass({ return false; } return ( -
- -
+
+ +
); @@ -43,7 +40,7 @@ var RecentItem = React.createClass({ var SearchForm = React.createClass({ - mixins: [ Router.Navigation, Router.State ], + mixins: [ Navigation, State ], getInitialState: function () { return { @@ -65,14 +62,14 @@ var SearchForm = React.createClass({ }); return; } - request.post("/search").send({url: url}).end(function(req, res) { + request.post('/search').send({url: url}).end(function(req, res) { that.setState({ submitting: false }); if (res.body.error) { that.setState({error: res.body.error.message}); } - that.transitionTo("share", res.body); + that.transitionTo('share', res.body); }); }, @@ -85,14 +82,14 @@ var SearchForm = React.createClass({ render: function() { return ( -
-
- - - + +
+ + +
-
+
{this.state.error}
@@ -100,7 +97,7 @@ var SearchForm = React.createClass({ } }); -module.exports = React.createClass({ +export default React.createClass({ getInitialState: function () { // Use this only on first page load, refresh whenever we navigate back. @@ -118,7 +115,7 @@ module.exports = React.createClass({ componentDidMount: function () { if (!this.props.recents) { - request.get("/recent").set("Accept", "application/json").end(function(err, res) { + request.get('/recent').set('Accept', 'application/json').end(function(err, res) { this.setState({ recents: res.body.recents }); @@ -129,42 +126,42 @@ module.exports = React.createClass({ render: function() { return (
-
+
-

match.audio

+

match.audio

-
-
-
+
+
+
-
-
+
+

Match Audio makes sharing from music services better. - What happens when you share your favourite song on Spotify with a friend, but they don"t use Spotify? + What happens when you share your favourite song on Spotify with a friend, but they don't use Spotify?

We match album and track links from Youtube, Rdio, Spotify, Deezer, Google Music, Xbox Music, Beats Music, and iTunes and give you back one link with matches we find on all of them.

-
-
+
+

Tools

-
-
+
+

Download the Chrome Extension and get Match Audio links right from your address bar.

-
-

Download the Chrome Extension

+
+

Download the Chrome Extension

- +
); } diff --git a/views/share.js b/views/share.js new file mode 100644 index 0000000..8e05582 --- /dev/null +++ b/views/share.js @@ -0,0 +1,196 @@ +import React from 'react'; +import request from 'superagent'; +import {State, Link} from 'react-router'; +import Foot from './foot'; + +var MusicItem = React.createClass({ + + render: function() { + if (!this.props.item.matched_at) { + return ( +
+
+
+
+
+ +
+
+ +
+
+
+ ); + } else if (!this.props.item.id) { + return ( +
+
+
+
+ No Match +
+
+ +
+
+
+ ); + } else { + return ( + + ); + } + } + +}); + +export default React.createClass({ + + mixins: [ State ], + + getInitialState: function () { + if (this.props.shares && this.props.shares[0].id === this.getParams().id) { + return { + name: this.props.shares[0].name, + artist: this.props.shares[0].artist.name, + shares: this.props.shares, + shareUrl: 'https://match.audio/' + this.props.shares[0].service + '/' + this.props.shares[0].type + '/' + this.props.shares[0].id + }; + } + return { + name: '', + artist: '', + shares: [], + shareUrl: '' + }; + }, + + componentWillUnmount: function() { + if (this.state.interval) { + clearInterval(this.state.interval); + } + }, + + componentDidMount: function () { + var complete = this.state.shares.length > 0; + + this.state.shares.forEach(function(share) { + if (typeof share.matched_at === 'undefined') { + complete = false; + } + }); + + var getShares = function() { + request.get(this.getPathname() + '.json').end(function(err, res) { + var shares = res.body.shares; + complete = true; + shares.forEach(function(share) { + if (typeof share.matched_at === 'undefined') { + complete = false; + } + }); + + if (complete) { + clearInterval(this.state.interval); + } + + if (shares.length) { + this.setState({ + name: shares[0].name, + artist: shares[0].artist.name, + shares: shares, + shareUrl: 'https://match.audio/' + shares[0].service + '/' + shares[0].type + '/' + shares[0].id + }); + } + }.bind(this)); + }.bind(this); + + if (!this.state.shares.length) { + getShares(); + } + + // Temporary until websockets implementation + this.state.interval = setInterval(function() { + if (!complete) { + getShares(); + } + }, 2000); + + // Some hacks to pop open the Twitter/Facebook/Google Plus sharing dialogs without using their code. + Array.prototype.forEach.call(document.querySelectorAll('.share-dialog'), function(dialog){ + dialog.addEventListener('click', function(event) { + event.preventDefault(); + var w = 845; + var h = 670; + var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; + var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; + + var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; + var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; + + var left = ((width / 2) - (w / 2)) + dualScreenLeft; + var top = ((height / 2) - (h / 2)) + dualScreenTop; + var newWindow = window.open(dialog.href, 'Share Music', 'scrollbars=yes, width=' + w + ', height=' + h + ', top=' + top + ', left=' + left); + if (window.focus) { + newWindow.focus(); + } + }); + }); + }, + + render: function() { + return ( +
+
+
+
+
+
+

match.audio

+
+
+
+
+
+
+
+

Matched {this.state.shares[0] ? this.state.shares[0].type + 's' : ''} for

+

{this.state.name} - {this.state.artist}

+
+
+
    +
  • Share this
  • +
  • Twitter
  • +
  • Facebook
  • +
  • Google+
  • +
+
+
+
+ {this.state.shares.map(function(item, i){ + return (); + }.bind(this))} +
+
+
+ +
+ ); + } +}); diff --git a/views/share.jsx b/views/share.jsx deleted file mode 100644 index 8739b32..0000000 --- a/views/share.jsx +++ /dev/null @@ -1,199 +0,0 @@ -"use strict"; - -var React = require("react"); -var request = require("superagent"); -var Router = require("react-router"); -var Link = require("react-router").Link; -var Foot = require("./foot.jsx"); - -var MusicItem = React.createClass({ - - render: function() { - if (!this.props.item.matched_at) { - return ( -
-
-
-
-
- -
-
- -
-
-
- ); - } else if (!this.props.item.id) { - return ( -
-
-
-
- No Match -
-
- -
-
-
- ); - } else { - return ( - - ); - } - } - -}); - -module.exports = React.createClass({ - - mixins: [ Router.State ], - - getInitialState: function () { - if (this.props.shares && this.props.shares[0].id === this.getParams().id) { - return { - name: this.props.shares[0].name, - artist: this.props.shares[0].artist.name, - shares: this.props.shares, - shareUrl: "https://match.audio/" + this.props.shares[0].service + "/" + this.props.shares[0].type + "/" + this.props.shares[0].id - }; - } - return { - name: "", - artist: "", - shares: [], - shareUrl: "" - }; - }, - - componentWillUnmount: function() { - if (this.state.interval) { - clearInterval(this.state.interval); - } - }, - - componentDidMount: function () { - var complete = this.state.shares.length > 0; - - this.state.shares.forEach(function(share) { - if (typeof share.matched_at === "undefined") { - complete = false; - } - }); - - var getShares = function() { - request.get(this.getPathname() + ".json").end(function(err, res) { - var shares = res.body.shares; - complete = true; - shares.forEach(function(share) { - if (typeof share.matched_at === "undefined") { - complete = false; - } - }); - - if (complete) { - clearInterval(this.state.interval); - } - - if (shares.length) { - this.setState({ - name: shares[0].name, - artist: shares[0].artist.name, - shares: shares, - shareUrl: "https://match.audio/" + shares[0].service + "/" + shares[0].type + "/" + shares[0].id - }); - } - }.bind(this)); - }.bind(this); - - if (!this.state.shares.length) { - getShares(); - } - - // Temporary until websockets implementation - this.state.interval = setInterval(function() { - if (!complete) { - getShares(); - } - }, 2000); - - // Some hacks to pop open the Twitter/Facebook/Google Plus sharing dialogs without using their code. - Array.prototype.forEach.call(document.querySelectorAll(".share-dialog"), function(dialog){ - dialog.addEventListener("click", function(event) { - event.preventDefault(); - var w = 845; - var h = 670; - var dualScreenLeft = window.screenLeft !== undefined ? window.screenLeft : screen.left; - var dualScreenTop = window.screenTop !== undefined ? window.screenTop : screen.top; - - var width = window.innerWidth ? window.innerWidth : document.documentElement.clientWidth ? document.documentElement.clientWidth : screen.width; - var height = window.innerHeight ? window.innerHeight : document.documentElement.clientHeight ? document.documentElement.clientHeight : screen.height; - - var left = ((width / 2) - (w / 2)) + dualScreenLeft; - var top = ((height / 2) - (h / 2)) + dualScreenTop; - var newWindow = window.open(dialog.href, "Share Music", "scrollbars=yes, width=" + w + ", height=" + h + ", top=" + top + ", left=" + left); - if (window.focus) { - newWindow.focus(); - } - }); - }); - }, - - render: function() { - return ( -
-
-
-
-
-
-

match.audio

-
-
-
-
-
-
-
-

Matched {this.state.shares[0] ? this.state.shares[0].type + "s" : ""} for

-

{this.state.name} - {this.state.artist}

-
-
-
    -
  • Share this
  • -
  • Twitter
  • -
  • Facebook
  • -
  • Google+
  • -
-
-
-
- {this.state.shares.map(function(item, i){ - return (); - }.bind(this))} -
-
-
- -
- ); - } -});