Skip to content

Instantly share code, notes, and snippets.

@sungwoncho
Last active November 17, 2016 00:32
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sungwoncho/7f0302d3dc01170da008c63aea64063c to your computer and use it in GitHub Desktop.
Save sungwoncho/7f0302d3dc01170da008c63aea64063c to your computer and use it in GitHub Desktop.
import React from 'react';
import ReactDOM from 'react-dom';
import { Router, browserHistory } from 'react-router';
import { Provider } from 'react-redux';
import ReactGA from 'react-ga';
import getRoutes from './routes';
import ApiClient from './libs/api_client';
import configureStore from './configureStore';
import { ReduxAsyncConnect } from 'redux-connect';
import { updateLocation } from './actions/router';
const store = configureStore(window.__data);
const client = new ApiClient();
ReactGA.initialize('UA-55773666-10');
// logPageview records the current page in the Google Analytics
function logPageview() {
ReactGA.set({ page: window.location.pathname });
ReactGA.pageview(window.location.pathname);
}
// hashLinkScroll scrolls the page to appropriate hash location in the page
function hashLinkScroll() {
const { hash } = window.location;
if (hash !== '') {
// Push onto callback queue so it runs after the DOM is updated,
// this is required when navigating from a different page so that
// the element is rendered on the page before trying to getElementById.
setTimeout(() => {
const id = hash.replace('#', '');
const element = document.getElementById(id);
if (element) element.scrollIntoView();
}, 0);
}
}
function handleUpdate() {
hashLinkScroll();
// __PRODUCTION__ is a global defined in webpack config
if (__PRODUCTION__) {
logPageview();
}
}
function renderRoutes(history) {
// store the current router information inside the redux store
history.listen((location) => store.dispatch(updateLocation(location)));
return (
<Router
render={(props) => <ReduxAsyncConnect {...props} helpers={{ client }} />}
history={history}
onUpdate={handleUpdate}
>
{getRoutes()}
</Router>
);
}
ReactDOM.render((
<Provider store={store}>
{renderRoutes(browserHistory)}
</Provider>
), document.getElementById('react-root'));
package main
import (
//...
"github.com/mssola/user_agent"
//...
)
//...
func (i *Impl) CreateCompanyView(w http.ResponseWriter, r *http.Request) {
db := i.DB
vars := mux.Vars(r)
slug := vars["companySlug"]
var company models.Company
var companyView models.CompanyView
// If somehow bot triggered this, then do not increment the view count
ua := user_agent.New(r.Header.Get("User-Agent"))
if ua.Bot() {
return
}
err := db.Where("slug = ?", slug).First(&company).Error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
err = db.Where(models.CompanyView{CompanyID: company.ID, Date: time.Now()}).
FirstOrCreate(&companyView).
Error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
// Increment
companyView.Count = companyView.Count + 1
err = db.Save(&companyView).Error
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
}
//...
import React from 'react';
import { asyncConnect } from 'redux-connect';
import { withRouter } from 'react-router';
import ApiClient from '../../libs/api_client';
const client = new ApiClient();
import { fetchCompany, isCompanyLoaded as loaded } from '../../actions/company_item';
import ProfileContent from './profile_content.jsx';
import SignInDialogue from './sign_in_dialogue.jsx';
import { retrieveSingleEntity, retrieveEntities } from '../../libs/state_helpers';
import { areCompanyJobsLoaded, fetchCompanyJobs } from '../../actions/company_jobs';
@asyncConnect([{
promise: ({ store: { getState, dispatch }, params: { companySlug } }) => {
const state = getState();
const promises = [];
if (!loaded(state, companySlug)) {
promises.push(dispatch(fetchCompany(companySlug)));
}
if (!areCompanyJobsLoaded(state, companySlug)) {
promises.push(dispatch(fetchCompanyJobs(companySlug)));
}
return Promise.all(promises);
},
// asyncConnect makes the result of the promise available in redux store under
// the given key, but we don't rely on that behavior. Rather, key is provided
// to track errors.
key: 'companyPromise'
}],
(state, ownProps) => {
const { params: { companySlug } } = ownProps;
return {
company: retrieveSingleEntity(state, 'companies', companySlug),
jobs: retrieveEntities(state, state.companyJobs[companySlug].items, 'jobs'),
filterSelection: state.companyFilters.selection,
isFetching: state.companyItem.isFetching,
user: state.auth.user,
followingCompanies: state.followingList.items
};
}
)
class CompanyLayout extends React.Component {
constructor(props) {
super(props);
this.state = {
showSignInDialogue: false,
currentTab: 'overview'
};
}
componentDidMount() {
const { params: { companySlug } } = this.props;
client.post(`/companies/${companySlug}/company_views`)
.catch(err => console.log('Error while incrementing view', err));
}
toggleSignInDialogue(display) {
this.setState({ showSignInDialogue: display });
}
handleSwitchTab(tabName) {
this.setState({ currentTab: tabName });
}
handleModalClose() {
const { router } = this.props;
router.goBack();
}
render() {
const { company, filterSelection, isFetching, user, followingCompanies, isModal, location, jobs, router } = this.props;
const { showSignInDialogue } = this.state;
const isFollowing = followingCompanies.indexOf(company.slug) > -1;
return (
<div className="company-profile-container">
<ProfileContent
company={company}
filterSelection={filterSelection}
user={user}
showSignInDialogue={this.toggleSignInDialogue.bind(this, true)}
isModal={isModal}
location={location}
isFollowing={isFollowing}
jobs={jobs}
handleSwitchTab={this.handleSwitchTab.bind(this)}
handleModalClose= {this.handleModalClose.bind(this)}
/>
{showSignInDialogue ?
<SignInDialogue
company={company}
closeSignInDialogue={this.toggleSignInDialogue.bind(this, false)}
/> : null}
</div>
);
}
}
export default withRouter(CompanyLayout);
import React, { PropTypes } from 'react';
import { renderToString } from 'react-dom/server';
import Helmet from 'react-helmet';
Html.propTypes = {
component: PropTypes.node,
assets: PropTypes.object
};
export default class Html extends React.Component {
render() {
const { component, assets, store } = this.props;
const content = component ? renderToString(component) : '';
const head = Helmet.rewind();
return (
<html lang="en-us">
<head>
{head.title.toComponent()}
{head.meta.toComponent()}
{head.link.toComponent()}
<link rel="shortcut icon" href="/favicon.ico" />
<meta name="fragment" content="!" />
<link rel="stylesheet" href="//maxcdn.bootstrapcdn.com/font-awesome/4.5.0/css/font-awesome.min.css" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/css/bootstrap.min.css" integrity="sha384-y3tfxAZXuh4HwSYylfB+J125MxIs6mR5FOHamPBG064zB+AFeWH94NdvaCBm8qnd" crossOrigin="anonymous" />
<script src="https://code.jquery.com/jquery-2.2.2.min.js" integrity="sha256-36cp2Co+/62rEAAYHLmRCPIych47CvdM+uTBJwSzWjI=" crossOrigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/tether/1.2.0/js/tether.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.2/js/bootstrap.min.js"></script>
<link href='https://fonts.googleapis.com/css?family=Lato:300,400,500,700,900' rel='stylesheet' type='text/css' />
<meta name="viewport" content="width=device-width, initial-scale=1" />
{/* styles (will be present only in production with webpack extract text plugin) */}
{Object.keys(assets.styles).map((style, key) =>
<link href={assets.styles[style]} key={key} media="screen, projection"
rel="stylesheet" type="text/css" charSet="UTF-8"/>
)}
<script dangerouslySetInnerHTML={{__html:
`if (document.location.hostname.search("localhost") === -1) {
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-55773666-10', 'auto');
ga('send', 'pageview');
}`
}} />
{/* (will be present only in development mode) */}
{/* outputs a <style/> tag with all bootstrap styles + App.scss + it could be CurrentPage.scss. */}
{/* can smoothen the initial style flash (flicker) on page load in development mode. */}
{/* ideally one could also include here the style for the current page (Home.scss, About.scss, etc) */}
{ Object.keys(assets.styles).length === 0 ? <style dangerouslySetInnerHTML={{__html: require('../styles/main.scss')._style}}/> : null }
</head>
<body>
<div id="react-root" dangerouslySetInnerHTML={{ __html: content }} />
<script dangerouslySetInnerHTML={{__html: `window.__data=${JSON.stringify(store.getState())};`}} charSet="UTF-8"/>
<script dangerouslySetInnerHTML={{__html: `(function(){var qs,js,q,s,d=document,gi=d.getElementById,ce=d.createElement,gt=d.getElementsByTagName,id='typef_orm',b='https://s3-eu-west-1.amazonaws.com/share.typeform.com/';if(!gi.call(d,id)){js=ce.call(d,'script');js.id=id;js.src=b+'share.js';q=gt.call(d,'script')[0];q.parentNode.insertBefore(js,q)}id=id+'_';if(!gi.call(d,id)){qs=ce.call(d,'link');qs.rel='stylesheet';qs.id=id;qs.href=b+'share-button.css';s=gt.call(d,'head')[0];s.appendChild(qs,s)}})()`}} />
<script dangerouslySetInnerHTML={{__html: `!function(d,s,id){var js,fjs=d.getElementsByTagName(s)[0],p=/^http:/.test(d.location)?'http':'https';if(!d.getElementById(id)){js=d.createElement(s);js.id=id;js.src=p+'://platform.twitter.com/widgets.js';fjs.parentNode.insertBefore(js,fjs);}}(document, 'script', 'twitter-wjs');`}} />
<script src={assets.javascript.main} charSet="UTF-8"/>
</body>
</html>
);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment