Add twitter follower button to board

This commit is contained in:
Berkeley Martinez
2015-10-19 14:19:04 -07:00
parent 2b6a84c1f5
commit 0a7f31a1d1
6 changed files with 143 additions and 9 deletions

View File

@ -959,7 +959,6 @@ code {
z-index: 20000 !important;
}
@import "jobs.less";
//uncomment this to see the dimensions of all elements outlined in red
//* {
@ -1070,3 +1069,4 @@ code {
}
@import "chat.less";
@import "jobs.less";

View File

@ -3,6 +3,7 @@ import { contain } from 'thundercats-react';
import { Button, Panel, Row, Col } from 'react-bootstrap';
import ListJobs from './List.jsx';
import TwitterBtn from './TwitterBtn.jsx';
export default contain(
{
@ -18,12 +19,18 @@ export default contain(
propTypes: {
children: PropTypes.element,
numOfFollowers: PropTypes.number,
appActions: PropTypes.object,
jobActions: PropTypes.object,
jobs: PropTypes.array,
showModal: PropTypes.bool
},
componentDidMount() {
const { jobActions } = this.props;
jobActions.getFollowers();
},
handleJobClick(id) {
const { appActions, jobActions } = this.props;
if (!id) {
@ -54,6 +61,7 @@ export default contain(
render() {
const {
children,
numOfFollowers,
jobs,
appActions
} = this.props;
@ -88,13 +96,7 @@ export default contain(
Post a job: $200 for 30 days + weekly tweets
</Button>
<div className='button-spacer' />
<a
className='twitter-follow-button'
data-show-count='false'
data-size='large'
href='https://twitter.com/CamperJobs'>
Follow @CamperJobs
</a>
<TwitterBtn count={ numOfFollowers || 0 } />
<div className='spacer' />
</Col>
</Row>

View File

@ -0,0 +1,33 @@
import React, { createClass, PropTypes } from 'react';
import { Button } from 'react-bootstrap';
const followLink = 'https://twitter.com/intent/follow?' +
'ref_src=twsrc%5Etfw&amp;region=follow_link&amp;screen_name=CamperJobs&' +
'amp;tw_p=followbutton';
function commify(count) {
return Number(count).toLocaleString('en');
}
export default createClass({
displayName: 'FollowButton',
propTypes: {
count: PropTypes.number
},
render() {
const { count } = this.props;
return (
<Button
block={ true }
bsSize='large'
bsStyle='primary'
href={ followLink }
target='__blank'>
Join { commify(count) } followers who see our job postings on Twitter.
</Button>
);
}
});

View File

@ -1,6 +1,7 @@
import { Actions } from 'thundercats';
import store from 'store';
import debugFactory from 'debug';
import { jsonp$ } from '../../../../utils/jsonp$';
const debug = debugFactory('freecc:jobs:actions');
const assign = Object.assign;
@ -59,6 +60,10 @@ export default Actions({
getSavedForm: null,
setForm(form) {
return { form };
},
getFollowers: null,
setFollowersCount(numOfFollowers) {
return { numOfFollowers };
}
})
.refs({ displayName: 'JobActions' })
@ -113,5 +118,20 @@ export default Actions({
appActions.goTo(goTo);
});
});
jobActions.getFollowers.subscribe(() => {
const url = 'https://cdn.syndication.twimg.com/widgets/followbutton/' +
'info.json?lang=en&screen_names=CamperJobs' +
'&callback=JSONPCallback';
jsonp$(url)
.map(({ response }) => {
return response[0]['followers_count'];
})
.subscribe(
count => jobActions.setFollowersCount(count),
err => jobActions.setError(err)
);
});
return jobActions;
});

View File

@ -19,7 +19,8 @@ export default Store({
openModal,
closeModal,
handleForm,
setForm
setForm,
setFollowersCount
} = cat.getActions('JobActions');
const register = createRegistrar(jobsStore);
register(setter(setJobs));
@ -27,6 +28,7 @@ export default Store({
register(setter(openModal));
register(setter(closeModal));
register(setter(setForm));
register(setter(setFollowersCount));
register(transformer(findJob));
register(handleForm);

77
common/utils/jsonp$.js Normal file
View File

@ -0,0 +1,77 @@
import { AnonymousObservable, Disposable } from 'rx';
const root = typeof window !== 'undefined' ? window : {};
const trash = 'document' in root && root.document.createElement('div');
function destroy(element) {
trash.appendChild(element);
trash.innerHTML = '';
}
export function jsonp$(options) {
let id = 0;
if (typeof options === 'string') {
options = { url: options };
}
return new AnonymousObservable(function(o) {
const settings = Object.assign(
{},
{
jsonp: 'JSONPCallback',
async: true,
jsonpCallback: 'rxjsjsonpCallbackscallback_' + (id++).toString(36)
},
options
);
let script = root.document.createElement('script');
script.type = 'text/javascript';
script.async = settings.async;
script.src = settings.url.replace(settings.jsonp, settings.jsonpCallback);
root[settings.jsonpCallback] = function(data) {
root[settings.jsonpCallback].called = true;
root[settings.jsonpCallback].data = data;
};
const handler = function(e) {
if (e.type === 'load' && !root[settings.jsonpCallback].called) {
e = { type: 'error' };
}
const status = e.type === 'error' ? 400 : 200;
const data = root[settings.jsonpCallback].data;
if (status === 200) {
o.onNext({
status: status,
responseType: 'jsonp',
response: data,
originalEvent: e
});
o.onCompleted();
} else {
o.onError({
type: 'error',
status: status,
originalEvent: e
});
}
};
script.onload = script.onreadystatechanged = script.onerror = handler;
const head = root.document.getElementsByTagName('head')[0] ||
root.document.documentElement;
head.insertBefore(script, head.firstChild);
return Disposable.create(() => {
script.onload = script.onreadystatechanged = script.onerror = null;
destroy(script);
script = null;
});
});
}