-
-
Notifications
You must be signed in to change notification settings - Fork 270
/
_app.js
160 lines (136 loc) · 4.5 KB
/
_app.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
/* eslint-disable max-classes-per-file */
import App from 'next/app';
import Router from 'next/router';
import PropTypes from 'prop-types';
import FontFaceObserver from 'fontfaceobserver';
import LogRocket from 'logrocket';
import ReactGA from 'react-ga';
import ScrollUpButton from 'react-scroll-up-button';
import setupLogRocketReact from 'logrocket-react';
import * as Sentry from '@sentry/browser';
import { clientTokens } from 'common/config/environment';
import Nav from 'components/Nav/Nav';
import Footer from 'components/Footer/Footer';
import Modal from 'components/Modal/Modal';
import Fingerprint2 from 'fingerprintjs2';
import hash from 'object-hash';
import { version } from '../package.json';
import 'common/styles/globalStyles.css';
const isProduction = process.env.NODE_ENV === 'production';
const fonts = [
{
fontFamily: 'Encode Sans',
url: 'https://fonts.googleapis.com/css?family=Encode+Sans:400,700',
},
{
fontFamily: 'DIN Condensed Bold',
// loading of this font is being handled by the @font-face rule on
// the global style sheet.
url: null,
},
];
Layout.propTypes = {
children: PropTypes.node.isRequired,
};
function Layout({ children }) {
return (
<>
<Nav />
<main>{children}</main>
<Footer />
<ScrollUpButton />
</>
);
}
// Same test used by EFF for identifying users
// https://panopticlick.eff.org/
const setLogRocketFingerprint = () => {
Fingerprint2.get(components => {
const fingerprint = hash(components);
LogRocket.identify(fingerprint);
});
};
class OperationCodeApp extends App {
componentDidMount() {
/* Analytics */
// Temporary method until we do dynamic now configs
if (isProduction && window.location.host.includes('operationcode.org')) {
Sentry.init({ dsn: clientTokens.SENTRY_DSN, release: `front-end@${version}` });
LogRocket.init(`${clientTokens.LOGROCKET}/operation-code`);
ReactGA.initialize(clientTokens.GOOGLE_ANALYTICS);
// Every crash report will have a LogRocket session URL.
LogRocket.getSessionURL(sessionURL => {
Sentry.configureScope(scope => {
scope.setExtra('sessionURL', sessionURL);
});
});
setupLogRocketReact(LogRocket);
// Per library docs, Fingerprint2 should not run immediately
if (window.requestIdleCallback) {
requestIdleCallback(setLogRocketFingerprint);
} else {
setTimeout(setLogRocketFingerprint, 500);
}
ReactGA.set({ page: window.location.pathname });
}
/* Non-render blocking font load */
const observers = fonts.map(font => {
if (font.url) {
const link = document.createElement('link');
link.href = font.url;
link.rel = 'stylesheet'; // eslint-disable-line unicorn/prevent-abbreviations
document.head.append(link);
}
const observer = new FontFaceObserver(font.fontFamily);
return observer.load(null, 10000); // increase the max timeout from default 3s to 10s
});
Promise.all(observers)
.then(() => {
document.documentElement.classList.add('fonts-loaded');
})
.catch(() =>
Sentry.captureException('FontFaceObserver took too long to resolve. Ignore this.'),
);
/* Modal anchor set */
if (Modal.setAppElement) {
Modal.setAppElement('body');
}
}
componentWillUnmount() {
window.removeEventListener('resize', this.debouncedHandleScreenResize);
}
componentDidCatch(error, errorInfo) {
Sentry.withScope(scope => {
Object.keys(errorInfo).forEach(key => {
scope.setExtra(key, errorInfo[key]);
});
Sentry.captureException(error);
});
super.componentDidCatch(error, errorInfo);
}
render() {
// eslint-disable-next-line unicorn/prevent-abbreviations
const { Component, pageProps } = this.props;
return (
<Layout>
<Component {...pageProps} />
</Layout>
);
}
}
if (isProduction) {
Router.events.on('routeChangeComplete', url => ReactGA.pageview(url));
}
// Fixes Next CSS route change bug: https://github.com/zeit/next-plugins/issues/282
if (!isProduction) {
Router.events.on('routeChangeComplete', () => {
const path = '/_next/static/chunks/styles.chunk.css';
const chunksSelector = `link[href*="${path}"]:not([rel=preload])`;
const chunksNodes = document.querySelectorAll(chunksSelector);
if (chunksNodes.length) {
const timestamp = new Date().valueOf();
chunksNodes[0].href = `${path}?ts=${timestamp}`;
}
});
}
export default OperationCodeApp;