Skip to content
This repository was archived by the owner on Sep 15, 2020. It is now read-only.

Commit e7f7ef6

Browse files
Add progress circle
1 parent fdd7356 commit e7f7ef6

File tree

4 files changed

+138
-22
lines changed

4 files changed

+138
-22
lines changed

src/redux/modules/news.js

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,22 +12,29 @@ export const NEWS_ERROR = 'NEWS_ERROR'
1212
// Actions
1313
// ------------------------------------
1414
export const newsStart = (): Action => ({
15-
type: NEWS_START
15+
type: NEWS_START,
16+
payload: {
17+
loading: true
18+
}
1619
})
1720

1821
export const newsComplete = (news: Array): Action => ({
1922
type: NEWS_COMPLETE,
2023
payload: {
24+
loading: false,
2125
news
2226
}
2327
})
2428

2529
export const newsError = (error: string): Action => ({
2630
type: NEWS_ERROR,
31+
payload: {
32+
loading: true
33+
},
2734
error
2835
})
2936

30-
export const getNews = (): Function => {
37+
export const getNews = (topTerms: Array): Function => {
3138
return (dispatch: Function, getState: Function): Promise => {
3239
dispatch(newsStart())
3340

@@ -46,14 +53,15 @@ export const actions = {
4653
// ------------------------------------
4754
const ACTION_HANDLERS = {
4855
[NEWS_COMPLETE]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
49-
[NEWS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error),
56+
[NEWS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error)
5057
}
5158

5259
// ------------------------------------
5360
// Reducer
5461
// ------------------------------------
5562
const initialState = {
5663
news: [],
64+
loading: false,
5765
error: null
5866
}
5967
export default function newsReducer (state: Object = initialState, action: Action): Object {

src/redux/modules/twitter.js

Lines changed: 88 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import fetch from 'isomorphic-fetch'
22
import { getAccessToken } from '../utils/auth'
3-
import { getTopTerms } from '../utils/terms'
3+
4+
import common from 'common-words'
5+
import twitter from 'twitter-text'
46

57
/* @flow */
68
// ------------------------------------
@@ -12,6 +14,9 @@ export const CREDENTIALS_ERROR = 'CREDENTIALS_ERROR'
1214
export const TWEETS_START = 'TWEETS_START'
1315
export const TWEETS_COMPLETE = 'TWEETS_COMPLETE'
1416
export const TWEETS_ERROR = 'TWEETS_ERROR'
17+
export const TOP_TERMS_START = 'TOP_TERMS_START'
18+
export const TOP_TERMS_COMPLETE = 'TOP_TERMS_COMPLETE'
19+
export const TOP_TERMS_ERROR = 'TOP_TERMS_ERROR'
1520

1621
// ------------------------------------
1722
// Actions
@@ -63,14 +68,17 @@ export const getCredentials = (): Function => {
6368
}
6469

6570
export const tweetsStart = (): Action => ({
66-
type: TWEETS_START
71+
type: TWEETS_START,
72+
payload: {
73+
tweetsLoading: true
74+
}
6775
})
6876

69-
export const tweetsComplete = (tweets: Array, topTerms: Array): Action => ({
77+
export const tweetsComplete = (tweets: Array): Action => ({
7078
type: TWEETS_COMPLETE,
7179
payload: {
72-
tweets: tweets.map((item) => item.id_str),
73-
topTerms: topTerms
80+
tweetsLoading: false,
81+
tweets
7482
}
7583
})
7684

@@ -99,7 +107,7 @@ export const getTweets = (): Function => {
99107
.then((json) => {
100108
console.log('@-->response json', json)
101109

102-
dispatch(tweetsComplete(json, getTopTerms(json)))
110+
dispatch(tweetsComplete(json.map((tweet) => tweet.id_str)))
103111

104112
return json
105113
})
@@ -110,9 +118,75 @@ export const getTweets = (): Function => {
110118
}
111119
}
112120

121+
export const topTermsStart = (): Action => ({
122+
type: TOP_TERMS_START,
123+
payload: {
124+
topTermsLoading: true
125+
}
126+
})
127+
128+
export const topTermsComplete = (topTerms: Array): Action => ({
129+
type: TOP_TERMS_COMPLETE,
130+
payload: {
131+
topTermsLoading: false,
132+
topTerms
133+
}
134+
})
135+
136+
export const topTermsError = (error: string): Action => ({
137+
type: TOP_TERMS_ERROR,
138+
error
139+
})
140+
141+
export const getTopTerms = (tweets: Array): Function => {
142+
return (dispatch: Function, getState: Function): Promise => {
143+
dispatch(topTermsStart())
144+
// Ignore common words
145+
const ignoreWords = common.map((item) => item.word)
146+
ignoreWords.push('is', 're', 'are', 'where', 'https', 'don', 'sure')
147+
148+
// Map of word counts
149+
const counts = new Map()
150+
151+
tweets
152+
.map((tweet) => tweet.text)
153+
// Remove Twitter Entities from Tweet i.e. hashtags, mentions, URLs
154+
.map((text) => {
155+
const entities = twitter.extractEntitiesWithIndices(text)
156+
157+
// Iterate the list of entities in reverse
158+
// in order to correctly splice out the indices
159+
for (let i = entities.length - 1; i >= 0; i--) {
160+
let [start, end] = entities[i].indices
161+
// Splice out text
162+
text = text.slice(0, start) + '' + text.slice(end)
163+
}
164+
return text
165+
})
166+
// Concat text
167+
.reduce((a, b) => a + ' ' + b)
168+
// Match whole words
169+
.match(/[a-z]+|\d+/igm)
170+
.filter((word) => word.length > 1 && word !== 'RT' && !ignoreWords.includes(word.toLowerCase()))
171+
// Do word count
172+
.forEach((word) => {
173+
let count = counts.get(word) || 0
174+
counts.set(word, count + 1)
175+
})
176+
// Sort words by counts, descending
177+
let ret = Array.from(counts.entries()).map(([word, count]) => ({word, count}))
178+
ret.sort((a, b) => b.count - a.count)
179+
// Return only top 10 terms
180+
ret = ret.length > 10 ? ret.slice(0, 10) : ret
181+
dispatch(topTermsComplete(ret))
182+
return Promise.resolve(ret)
183+
}
184+
}
185+
113186
export const actions = {
114187
getCredentials,
115-
getTweets
188+
getTweets,
189+
getTopTerms
116190
}
117191

118192
// ------------------------------------
@@ -121,8 +195,12 @@ export const actions = {
121195
const ACTION_HANDLERS = {
122196
[CREDENTIALS_COMPLETE]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
123197
[CREDENTIALS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error),
198+
[TWEETS_START]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
124199
[TWEETS_COMPLETE]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
125-
[TWEETS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error)
200+
[TWEETS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error),
201+
[TOP_TERMS_START]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
202+
[TOP_TERMS_COMPLETE]: (state: Object, action: {payload: Object}): Object => Object.assign({}, state, action.payload),
203+
[TOP_TERMS_ERROR]: (state: Object, action: {error: string}): Object => Object.assign({}, state, action.error)
126204
}
127205

128206
// ------------------------------------
@@ -132,7 +210,9 @@ const initialState = {
132210
id: null,
133211
screenName: null,
134212
tweets: [],
213+
tweetsLoading: true,
135214
topTerms: [],
215+
topTermsLoading: true,
136216
error: null
137217
}
138218
export default function twitterReducer (state: Object = initialState, action: Action): Object {

src/views/TwitterView/TwitterView.js

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,15 @@ import { actions as newsActions } from '../../redux/modules/news'
66
import { bindActionCreators } from 'redux'
77
import Timeline from '../../components/Timeline'
88
import Terms from '../../components/Terms'
9+
import CircularProgress from 'material-ui/lib/circular-progress'
10+
import classes from './TwitterView.scss'
911

1012
const mapStateToProps = (state) => ({
1113
screenName: state.twitter.screenName,
1214
tweets: state.twitter.tweets,
13-
topTerms: state.twitter.topTerms
15+
tweetsLoading: state.twitter.tweetsLoading,
16+
topTerms: state.twitter.topTerms,
17+
topTermsLoading: state.twitter.topTermsLoading
1418
})
1519
const mapDispatchToProps = (dispatch) => bindActionCreators({
1620
dispatch,
@@ -22,33 +26,44 @@ export class TwitterView extends React.Component {
2226
static propTypes = {
2327
requestAccessToken: PropTypes.func.isRequired,
2428
getCredentials: PropTypes.func.isRequired,
29+
screenName: PropTypes.string,
2530
getTweets: PropTypes.func.isRequired,
2631
tweets: PropTypes.arrayOf(PropTypes.string).isRequired,
27-
screenName: PropTypes.string,
32+
tweetsLoading: PropTypes.bool.isRequired,
33+
getTopTerms: PropTypes.func.isRequired,
2834
topTerms: PropTypes.arrayOf(PropTypes.object).isRequired,
35+
topTermsLoading: PropTypes.bool.isRequired,
2936
getNews: PropTypes.func.isRequired
3037
}
3138

3239
componentWillMount () {
3340
this.props.requestAccessToken()
3441
.then(() => this.props.getCredentials())
3542
.then(() => this.props.getTweets())
36-
.then(() => this.props.getNews())
43+
.then((tweets) => this.props.getTopTerms(tweets))
44+
.then((topTerms) => this.props.getNews(topTerms))
3745
}
3846

3947
render () {
48+
let content
49+
if (this.props.tweetsLoading || this.props.topTermsLoading) {
50+
content = (<div className={classes.progress}><CircularProgress /></div>)
51+
} else {
52+
content = (<div className='row'>
53+
<div className='col-md-6'>
54+
<Terms terms={this.props.topTerms}/>
55+
</div>
56+
<div className='col-md-6'>
57+
<Timeline tweets={this.props.tweets} screenName={this.props.screenName} />
58+
</div>
59+
</div>)
60+
}
61+
4062
return (
4163
<div className='container'>
4264
<h1 className='text-center'>Your Twitter Lexicon</h1>
4365
<br />
44-
<div className='row'>
45-
<div className='col-md-6'>
46-
<Terms terms={this.props.topTerms}/>
47-
</div>
48-
<div className='col-md-6'>
49-
<Timeline tweets={this.props.tweets} screenName={this.props.screenName} />
50-
</div>
51-
</div>
66+
{content}
5267
<br />
5368
<hr />
5469
<br />
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
.progress {
2+
// flexbox container
3+
display: -webkit-flex;
4+
display: flex;
5+
-webkit-flex-direction: column;
6+
flex-direction: column;
7+
8+
// center everything
9+
-webkit-align-items: center;
10+
align-items: center;
11+
-webkit-justify-content: center;
12+
justify-content: center;
13+
}

0 commit comments

Comments
 (0)