Skip to content

Commit 4f323a6

Browse files
committed
Fix #314: Add streaming feature
1 parent bfee69c commit 4f323a6

File tree

8 files changed

+206
-9
lines changed

8 files changed

+206
-9
lines changed

WorldMoodTracker/index_bundle.js

+8-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
import {Modal} from 'react-overlays';
2+
import React from 'react';
3+
import {host} from '../index';
4+
import styles from '../../../css/bootstrap.min.css';
5+
6+
const modalStyle = {
7+
position: 'fixed',
8+
zIndex: 10,
9+
top: 0, bottom: 0, left: 0, right: 0
10+
};
11+
12+
const backdropStyle = {
13+
position: 'fixed',
14+
top: 0, bottom: 0, left: 0, right: 0,
15+
zIndex: 'auto',
16+
backgroundColor: '#000',
17+
opacity: 0.5
18+
};
19+
20+
const dialogStyle = function () {
21+
return {
22+
position: 'absolute',
23+
width: '100%',
24+
top: '5vh',
25+
height: '95vh',
26+
border: '1px solid #e5e5e5',
27+
backgroundColor: 'white',
28+
boxShadow: '0 5px 15px rgba(0,0,0,.5)',
29+
padding: 20
30+
};
31+
};
32+
33+
function getTweetHtml(json) {
34+
return (
35+
<div style={{padding: '5px', borderRadius: '3px', border: '1px solid black', margin: '10px'}}>
36+
<a href={json.link} target="_blank">
37+
<div style={{marginBottom: '5px'}}>
38+
<b>@{json['screen_name']}</b>
39+
</div>
40+
<div style={{overflowX: 'hidden'}}>{json['text']}</div>
41+
</a>
42+
</div>
43+
)
44+
}
45+
46+
export default class StreamOverlay extends React.Component {
47+
constructor (props) {
48+
super(props);
49+
this.state = {channel: this.props.channel, country: this.props.country, tweets: []};
50+
this.close = this.close.bind(this);
51+
this.eventSource = null;
52+
};
53+
54+
close() {
55+
if (this.eventSource) {
56+
this.eventSource.close();
57+
this.eventSource = null;
58+
}
59+
this.props.onClose();
60+
}
61+
62+
startEventSource(country) {
63+
let channel = 'twitter%2Fcountry%2F' + country;
64+
if (this.eventSource) {
65+
return;
66+
}
67+
this.eventSource = new EventSource(host + '/api/stream.json?channel=' + channel);
68+
this.eventSource.onmessage = (event) => {
69+
let json = JSON.parse(event.data);
70+
this.state.tweets.push(json);
71+
if (this.state.tweets.length > 250) {
72+
this.state.tweets.shift();
73+
}
74+
this.setState(this.state);
75+
};
76+
}
77+
78+
render () {
79+
this.startEventSource(this.props.channel);
80+
return (
81+
<div className={styles.container}>
82+
<Modal aria-labelledby='modal-label'
83+
style={modalStyle}
84+
backdropStyle={backdropStyle}
85+
show={true}
86+
onHide={this.close}>
87+
<div style={dialogStyle()}>
88+
<h2 id='modal-label'>Streaming Tweets from&nbsp;
89+
<span style={{background: '-webkit-linear-gradient(0deg, #D02010 2%, #FFFB20 98%)',
90+
'WebkitTextFillColor': 'transparent',
91+
'WebkitBackgroundClip': 'text',
92+
}}>{this.state.country}</span></h2>
93+
<div className={styles.container} style={{'height': '100%', 'overflowY': 'auto',
94+
'overflowX': 'hidden', maxWidth: '100%'}}>
95+
{this.state.tweets.reverse().map(getTweetHtml)}
96+
</div>
97+
</div>
98+
</Modal>
99+
</div>
100+
)
101+
}
102+
};

WorldMoodTracker/js/components/WorldMap.js

+9-2
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,11 @@ import {getScores} from '../utils/score';
77
import {getColor, getColorsForCountries} from '../utils/color';
88
import {host} from '../index';
99
import {getFromCache, updateCache} from '../utils/cache';
10+
import {reverseCountryCode} from "../utils";
1011

1112
export default class WorldMap extends React.Component {
12-
constructor() {
13-
super();
13+
constructor(props) {
14+
super(props);
1415
this.state = {
1516
map: null
1617
};
@@ -181,9 +182,15 @@ export default class WorldMap extends React.Component {
181182
}
182183

183184
componentDidMount() {
185+
let props = this.props; // Can't be accessed with Datamap's done callback
184186
this.setState({
185187
map: new Datamap({
186188
element: document.getElementById('map-container'),
189+
done: function(datamap) {
190+
datamap.svg.selectAll('.datamaps-subunit').on('click', function (geography) {
191+
props.showStream(geography.properties.name, reverseCountryCode(geography.id));
192+
})
193+
},
187194
responsive: true,
188195
fills: {defaultFill: 'rgba(200,200,200,0.8)'},
189196
geographyConfig: {

WorldMoodTracker/js/components/WorldMoodTracker.js

+33-2
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,47 @@ import Header from './Header';
33
import WorldMap from './WorldMap';
44
import Footer from './Footer';
55
import ScaleBar from './ScaleBar';
6+
import StreamOverlay from "./StreamOverlay";
67

78
export default class WorldMoodTracker extends React.Component {
9+
10+
constructor() {
11+
super();
12+
this.state = {enabled: false};
13+
this.showStream = this.showStream.bind(this);
14+
this.getStreamOverlay = this.getStreamOverlay.bind(this);
15+
this.onOverlayClose = this.onOverlayClose.bind(this);
16+
}
17+
18+
onOverlayClose() {
19+
this.state.enabled = false;
20+
this.setState(this.state);
21+
}
22+
23+
getStreamOverlay() {
24+
if (this.state.enabled) {
25+
return (<StreamOverlay
26+
show={true} channel={this.state.channel}
27+
country={this.state.country} onClose={this.onOverlayClose}/>);
28+
}
29+
}
30+
31+
showStream(countryName, countryCode) {
32+
this.state.channel = countryCode;
33+
this.state.country = countryName;
34+
this.state.enabled = true;
35+
this.setState(this.state);
36+
}
37+
838
render() {
939
return (
1040
<div>
1141
<Header/>
12-
<WorldMap/>
42+
{this.getStreamOverlay()}
43+
<WorldMap showStream={this.showStream}/>
1344
<ScaleBar/>
1445
<Footer/>
1546
</div>
1647
)
1748
}
18-
}
49+
}

WorldMoodTracker/js/index.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import ReactDOM from 'react-dom';
33
import WorldMoodTracker from './components/WorldMoodTracker.js';
44

5-
export const host = 'http://api.loklak.org';
5+
export const host = 'http://35.193.41.118:9000';
66

77
ReactDOM.render(
88
<WorldMoodTracker/>,

WorldMoodTracker/js/utils/index.js

+5
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ export function countryCodeConverter(code)
2626
}
2727
return code;
2828
}
29+
30+
export function reverseCountryCode(code)
31+
{
32+
return iso3166.codes[code];
33+
}

WorldMoodTracker/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
"path": "^0.12.7",
1717
"react": "^15.6.1",
1818
"react-dom": "^15.6.1",
19+
"react-overlays": "^0.8.0",
1920
"style-loader": "^0.18.2",
2021
"webpack": "^2.6.1",
2122
"webpack-dev-server": "^2.4.5"

WorldMoodTracker/yarn.lock

+47-1
Original file line numberDiff line numberDiff line change
@@ -885,6 +885,10 @@ center-align@^0.1.1:
885885
align-text "^0.1.3"
886886
lazy-cache "^1.0.3"
887887

888+
chain-function@^1.0.0:
889+
version "1.0.0"
890+
resolved "https://registry.yarnpkg.com/chain-function/-/chain-function-1.0.0.tgz#0d4ab37e7e18ead0bdc47b920764118ce58733dc"
891+
888892
chalk@^1.1.0, chalk@^1.1.3:
889893
version "1.1.3"
890894
resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
@@ -922,6 +926,10 @@ clap@^1.0.9:
922926
dependencies:
923927
chalk "^1.1.3"
924928

929+
classnames@^2.2.5:
930+
version "2.2.5"
931+
resolved "https://registry.yarnpkg.com/classnames/-/classnames-2.2.5.tgz#fb3801d453467649ef3603c7d61a02bd129bde6d"
932+
925933
926934
version "4.1.4"
927935
resolved "https://registry.yarnpkg.com/clean-css/-/clean-css-4.1.4.tgz#eec8811db27457e0078d8ca921fa81b72fa82bf4"
@@ -1353,6 +1361,10 @@ dom-converter@~0.1:
13531361
dependencies:
13541362
utila "~0.3"
13551363

1364+
dom-helpers@^3.2.0, dom-helpers@^3.2.1:
1365+
version "3.2.1"
1366+
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-3.2.1.tgz#3203e07fed217bd1f424b019735582fc37b2825a"
1367+
13561368
dom-serializer@0:
13571369
version "0.1.0"
13581370
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.1.0.tgz#073c697546ce0780ce23be4a28e293e40bc30c82"
@@ -3073,7 +3085,13 @@ promise@^7.1.1:
30733085
dependencies:
30743086
asap "~2.0.3"
30753087

3076-
prop-types@^15.5.10:
3088+
prop-types-extra@^1.0.1:
3089+
version "1.0.1"
3090+
resolved "https://registry.yarnpkg.com/prop-types-extra/-/prop-types-extra-1.0.1.tgz#a57bd4810e82d27a3ff4317ecc1b4ad005f79a82"
3091+
dependencies:
3092+
warning "^3.0.0"
3093+
3094+
prop-types@^15.5.10, prop-types@^15.5.8:
30773095
version "15.5.10"
30783096
resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.5.10.tgz#2797dfc3126182e3a95e3dfbb2e893ddd7456154"
30793097
dependencies:
@@ -3190,6 +3208,28 @@ react-dom@^15.6.1:
31903208
object-assign "^4.1.0"
31913209
prop-types "^15.5.10"
31923210

3211+
react-overlays@^0.8.0:
3212+
version "0.8.0"
3213+
resolved "https://registry.yarnpkg.com/react-overlays/-/react-overlays-0.8.0.tgz#55359421735da2c0d8e235f780c9f9f3b16bc2f2"
3214+
dependencies:
3215+
classnames "^2.2.5"
3216+
dom-helpers "^3.2.1"
3217+
prop-types "^15.5.10"
3218+
prop-types-extra "^1.0.1"
3219+
react-transition-group "^2.0.0-beta.0"
3220+
warning "^3.0.0"
3221+
3222+
react-transition-group@^2.0.0-beta.0:
3223+
version "2.2.0"
3224+
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-2.2.0.tgz#793bf8cb15bfe91b3101b24bce1c1d2891659575"
3225+
dependencies:
3226+
chain-function "^1.0.0"
3227+
classnames "^2.2.5"
3228+
dom-helpers "^3.2.0"
3229+
loose-envify "^1.3.1"
3230+
prop-types "^15.5.8"
3231+
warning "^3.0.0"
3232+
31933233
react@^15.6.1:
31943234
version "15.6.1"
31953235
resolved "https://registry.yarnpkg.com/react/-/react-15.6.1.tgz#baa8434ec6780bde997cdc380b79cd33b96393df"
@@ -3978,6 +4018,12 @@ [email protected]:
39784018
dependencies:
39794019
indexof "0.0.1"
39804020

4021+
warning@^3.0.0:
4022+
version "3.0.0"
4023+
resolved "https://registry.yarnpkg.com/warning/-/warning-3.0.0.tgz#32e5377cb572de4ab04753bdf8821c01ed605b7c"
4024+
dependencies:
4025+
loose-envify "^1.0.0"
4026+
39814027
watchpack@^1.3.1:
39824028
version "1.3.1"
39834029
resolved "https://registry.yarnpkg.com/watchpack/-/watchpack-1.3.1.tgz#7d8693907b28ce6013e7f3610aa2a1acf07dad87"

0 commit comments

Comments
 (0)