Add twitter follower button to board
This commit is contained in:
@ -959,7 +959,6 @@ code {
|
|||||||
z-index: 20000 !important;
|
z-index: 20000 !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@import "jobs.less";
|
|
||||||
|
|
||||||
//uncomment this to see the dimensions of all elements outlined in red
|
//uncomment this to see the dimensions of all elements outlined in red
|
||||||
//* {
|
//* {
|
||||||
@ -1070,3 +1069,4 @@ code {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@import "chat.less";
|
@import "chat.less";
|
||||||
|
@import "jobs.less";
|
||||||
|
@ -3,6 +3,7 @@ import { contain } from 'thundercats-react';
|
|||||||
import { Button, Panel, Row, Col } from 'react-bootstrap';
|
import { Button, Panel, Row, Col } from 'react-bootstrap';
|
||||||
|
|
||||||
import ListJobs from './List.jsx';
|
import ListJobs from './List.jsx';
|
||||||
|
import TwitterBtn from './TwitterBtn.jsx';
|
||||||
|
|
||||||
export default contain(
|
export default contain(
|
||||||
{
|
{
|
||||||
@ -18,12 +19,18 @@ export default contain(
|
|||||||
|
|
||||||
propTypes: {
|
propTypes: {
|
||||||
children: PropTypes.element,
|
children: PropTypes.element,
|
||||||
|
numOfFollowers: PropTypes.number,
|
||||||
appActions: PropTypes.object,
|
appActions: PropTypes.object,
|
||||||
jobActions: PropTypes.object,
|
jobActions: PropTypes.object,
|
||||||
jobs: PropTypes.array,
|
jobs: PropTypes.array,
|
||||||
showModal: PropTypes.bool
|
showModal: PropTypes.bool
|
||||||
},
|
},
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
const { jobActions } = this.props;
|
||||||
|
jobActions.getFollowers();
|
||||||
|
},
|
||||||
|
|
||||||
handleJobClick(id) {
|
handleJobClick(id) {
|
||||||
const { appActions, jobActions } = this.props;
|
const { appActions, jobActions } = this.props;
|
||||||
if (!id) {
|
if (!id) {
|
||||||
@ -54,6 +61,7 @@ export default contain(
|
|||||||
render() {
|
render() {
|
||||||
const {
|
const {
|
||||||
children,
|
children,
|
||||||
|
numOfFollowers,
|
||||||
jobs,
|
jobs,
|
||||||
appActions
|
appActions
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -88,13 +96,7 @@ export default contain(
|
|||||||
Post a job: $200 for 30 days + weekly tweets
|
Post a job: $200 for 30 days + weekly tweets
|
||||||
</Button>
|
</Button>
|
||||||
<div className='button-spacer' />
|
<div className='button-spacer' />
|
||||||
<a
|
<TwitterBtn count={ numOfFollowers || 0 } />
|
||||||
className='twitter-follow-button'
|
|
||||||
data-show-count='false'
|
|
||||||
data-size='large'
|
|
||||||
href='https://twitter.com/CamperJobs'>
|
|
||||||
Follow @CamperJobs
|
|
||||||
</a>
|
|
||||||
<div className='spacer' />
|
<div className='spacer' />
|
||||||
</Col>
|
</Col>
|
||||||
</Row>
|
</Row>
|
||||||
|
33
common/app/routes/Jobs/components/TwitterBtn.jsx
Normal file
33
common/app/routes/Jobs/components/TwitterBtn.jsx
Normal 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&region=follow_link&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>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
@ -1,6 +1,7 @@
|
|||||||
import { Actions } from 'thundercats';
|
import { Actions } from 'thundercats';
|
||||||
import store from 'store';
|
import store from 'store';
|
||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
import { jsonp$ } from '../../../../utils/jsonp$';
|
||||||
|
|
||||||
const debug = debugFactory('freecc:jobs:actions');
|
const debug = debugFactory('freecc:jobs:actions');
|
||||||
const assign = Object.assign;
|
const assign = Object.assign;
|
||||||
@ -59,6 +60,10 @@ export default Actions({
|
|||||||
getSavedForm: null,
|
getSavedForm: null,
|
||||||
setForm(form) {
|
setForm(form) {
|
||||||
return { form };
|
return { form };
|
||||||
|
},
|
||||||
|
getFollowers: null,
|
||||||
|
setFollowersCount(numOfFollowers) {
|
||||||
|
return { numOfFollowers };
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.refs({ displayName: 'JobActions' })
|
.refs({ displayName: 'JobActions' })
|
||||||
@ -113,5 +118,20 @@ export default Actions({
|
|||||||
appActions.goTo(goTo);
|
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;
|
return jobActions;
|
||||||
});
|
});
|
||||||
|
@ -19,7 +19,8 @@ export default Store({
|
|||||||
openModal,
|
openModal,
|
||||||
closeModal,
|
closeModal,
|
||||||
handleForm,
|
handleForm,
|
||||||
setForm
|
setForm,
|
||||||
|
setFollowersCount
|
||||||
} = cat.getActions('JobActions');
|
} = cat.getActions('JobActions');
|
||||||
const register = createRegistrar(jobsStore);
|
const register = createRegistrar(jobsStore);
|
||||||
register(setter(setJobs));
|
register(setter(setJobs));
|
||||||
@ -27,6 +28,7 @@ export default Store({
|
|||||||
register(setter(openModal));
|
register(setter(openModal));
|
||||||
register(setter(closeModal));
|
register(setter(closeModal));
|
||||||
register(setter(setForm));
|
register(setter(setForm));
|
||||||
|
register(setter(setFollowersCount));
|
||||||
|
|
||||||
register(transformer(findJob));
|
register(transformer(findJob));
|
||||||
register(handleForm);
|
register(handleForm);
|
||||||
|
77
common/utils/jsonp$.js
Normal file
77
common/utils/jsonp$.js
Normal 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;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
Reference in New Issue
Block a user