Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# IntelliJ
.idea
*.iml

# Node
node_modules
npm-debug.log

#Project
dist
48 changes: 48 additions & 0 deletions Gulpfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
var gulp = require('gulp');
var reactify = require('reactify');
var browserify = require('browserify');
var source = require('vinyl-source-stream');
var del = require('del');
var pkg = require('./package.json');

var paths = {
main: "./" + pkg.main,
js: ['app/js/**/*.js'],
statics: ['app/images', 'app/styles/**/*.*', 'app/index.html'],
dist: './dist'
};

var config = {
browserify: {
entries: [paths.main],
debug: true,
standalone: pkg.name,
extensions: ['.jsx', '.js']
}
};

gulp.task('clean', function(done) {
del(['dist'], done);
});

gulp.task('copy', function() {
gulp.src(paths.statics)
.pipe(gulp.dest(paths.dist))
});

gulp.task('js', function() {
browserify(config.browserify)
.transform(reactify)
.bundle()
.pipe(source('bundle.js'))
.pipe(gulp.dest(paths.dist + "/js/"));
});

gulp.task('watch', function() {
gulp.watch(paths.js, ['js']);
gulp.watch(paths.statics, ['copy']);
});

gulp.task('dist', ['js', 'copy']);

gulp.task('default', ['watch', 'dist']);
29 changes: 29 additions & 0 deletions app/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<!doctype html>
<html>
<head>
<title>React Hackathon</title>

<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1">
<meta name="description" content="">
<meta name="viewport" content="initial-scale=1, maximum-scale=1, user-scalable=no">
<meta http-equiv="PRAGMA" content="NO-CACHE">

<!-- iOS Webapp Initializiation -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">

<link rel="stylesheet" href="main.css" />

</head>

<body>

<div id="app"></div>
<script type="text/javascript"
src="https://maps.googleapis.com/maps/api/js?key=AIzaSyDeMc1Q4LENnb0VEiLV1Tlb7UlMGA-adwc">
</script>
<script src="js/bundle.js"></script>

</body>
</html>
53 changes: 53 additions & 0 deletions app/js/app.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use strict";

var React = require("react");
var Http = require("./http");

var Search = require("./search");
var ResultList = require("./results");
var Map = require("./map");
var Members = require("./members");

module.exports = React.createClass({
displayName: 'App',

getInitialState() {
return {};
},

updateResults(results) {
this.setState({ results: results });
},

resultSelected(result) {
this.setState({
showResult: true,
result: result
});
},

render() {
var comp;
if (this.state.showResult) {
comp = (
<div>
<Map data={this.state.result} />
<Members groupId={this.state.result.get("id", 0)} />
</div>
);

} else if (this.state.results) {

comp = <ResultList data={this.state.results}
resultSelected={this.resultSelected}/>;
}

return (
<div className="main">
<div className="head">ReactJS Meetups</div>
<Search onSearchResultsReceived={this.updateResults}/>
{comp}
</div>
);
}
});
8 changes: 8 additions & 0 deletions app/js/config.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
"use strict";

module.exports = {
meetup: {
searchUrl: "http://api.meetup.com/find/groups?key=f2f14303864524413516c4327452b2c",
membersUrl: "https://api.meetup.com/2/members?key=f2f14303864524413516c4327452b2c"
}
};
36 changes: 36 additions & 0 deletions app/js/http.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
"use strict";

var request = require('superagent');
var Immutable = require('immutable');

var Http = {

/**
* Gets JSON data from a given URL.
*/
get(url, params, callback) {
if (!callback) {
callback = params;
params = {};
}

request
.get(url)
.query(params)
.end(function(result) {
if (result.ok) {
var pageData;
if (result.body) {
pageData = Immutable.fromJS(result.body);
} else {
pageData = Immutable.fromJS(JSON.parse(result.text));
}
}
if (pageData) {
callback(pageData);
}
});
}
};

module.exports = Http;
11 changes: 11 additions & 0 deletions app/js/index.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
"use strict";

var React = require("react");
var App = require("./app");

React.initializeTouchEvents(true);

React.render(
<App />,
document.getElementById("app")
);
64 changes: 64 additions & 0 deletions app/js/map.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use strict";

var React = require('react');

module.exports = React.createClass({
displayName: 'GoogleMapsChart',

getInitialState: function() {
return {
map: undefined
};
},

componentDidMount: function () {
this._initMap();
this._insertMarkers(this.props.data);
},

_initMap() {
var mapOptions = {
center: {lat: 48.8721388, lng: 2.3411542}, // Mozilla Paris HQ
zoom: 10
};
var map = new google.maps.Map(
React.findDOMNode(this),
mapOptions
);

this.setState({
map: map
});
},

_insertMarkers(data) {
var map = this.state.map;
if (map && data) {
data.forEach((member) => {
this._addMarkerToMap(member);
});
}
},

/**
* required properties in markerData
* lat: marker latitude
* lon: marker longitude
* name: onHover string for the marker
*/
_addMarkerToMap(markerData, map) {
new google.maps.Marker({
position: new google.maps.LatLng(markerData.get('lat'), markerData.get('lon')),
map: map,
title: markerData.get('name')
});
},

render() {

return (
<div id="google-maps" className="google-maps-chart">
</div>
)
}
});
53 changes: 53 additions & 0 deletions app/js/members.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
"use strict";

var React = require("react");
var Http = require("./http");
var config = require("./config");

module.exports = React.createClass({
displayName: 'App',

propTypes: {
groupId: React.PropTypes.object.isRequired
},

getInitialState() {
return {};
},


componentDidMount() {
Http.get(config.meetup.membersUrl, { "group_id": this.props.groupId }, response => {

this.setState({
cities: response
.get('results')
.groupBy((member) => member.get('city'))
.map((m, c) => {
return {
city: c,
total: m.count()
}
})
})
})
},

render(){
var cities = this.state.cities;
if (cities) {
var stats = cities.map((c, i) =>
<div className="member" key={i}>
<div className="city">{c.city}</div>
<div className="total">{c.total}</div>
</div>
);
}

return (
<div className="members">
{stats}
</div>
);
}
});
64 changes: 64 additions & 0 deletions app/js/results.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
"use strict";

var React = require("react");

var Result = React.createClass({

displayName: "Result",

propTypes: {
data: React.PropTypes.object.isRequired,
resultSelected: React.PropTypes.func
},

onClick() {
var callback = this.props.resultSelected;
callback && callback(this.props.data);
},

render() {
var data = this.props.data;
return (
<div className="result" onClick={this.onClick}>
<div className="thumb">
<img src={data.getIn(['photos', 0, 'thumb_link'])}/>
</div>
<div className="details">
<div className="name">{data.get("name")}</div>
<div className="location">
<span className="city">{data.get("city")}</span>
<span className="country">{data.get("country")}</span>
</div>
</div>
</div>
);
}
});

var ResultList = React.createClass({

displayName: "ResultList",

propTypes: {
data: React.PropTypes.object.isRequired,
resultSelected: React.PropTypes.func
},


render() {
if (this.props.data) {
var results = this.props.data.map(
(result, index) => <Result data={result} key={index}
resultSelected={this.props.resultSelected}/>
);
}

return (
<div className="results">
{results}
</div>
);
}
});

module.exports = ResultList;
Loading