Refactor app and add fathom

This commit is contained in:
Brian Lovin 2019-11-24 19:51:03 -05:00
parent 071cc65444
commit 2b6e90dd77
10 changed files with 167 additions and 370 deletions

View file

@ -1,126 +1,31 @@
{
"extends": [
"airbnb",
"prettier",
"plugin:react/recommended"
"eslint:recommended",
"plugin:react/recommended",
"plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
],
"parser": "babel-eslint",
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"impliedStrict": true,
"classes": true
}
},
"plugins": ["react", "@typescript-eslint", "prettier"],
"env": {
"browser": true,
"node": true,
"jquery": true,
"jasmine": true,
"jest": true
},
"rules": {
"no-debugger": 0,
"no-alert": 0,
"no-unused-vars": [
1,
{
"ignoreSiblings": true,
"argsIgnorePattern": "res|next|^err"
}
],
"prefer-const": [
"error",
{
"destructuring": "all"
}
],
"arrow-body-style": [
2,
"as-needed"
],
"no-unused-expressions": [
2,
{
"allowTaggedTemplates": true
}
],
"no-param-reassign": [
2,
{
"props": false
}
],
"no-console": 0,
"import/prefer-default-export": 0,
"import": 0,
"func-names": 0,
"space-before-function-paren": 0,
"comma-dangle": 0,
"max-len": 0,
"import/extensions": 0,
"no-underscore-dangle": 0,
"consistent-return": 0,
"react/display-name": 1,
"react/no-array-index-key": 0,
"react/react-in-jsx-scope": 0,
"react/prefer-stateless-function": 0,
"react/forbid-prop-types": 0,
"react/no-unescaped-entities": 0,
"jsx-a11y/accessible-emoji": 0,
"react/jsx-one-expression-per-line": 0,
"react/require-default-props": 0,
"react/jsx-filename-extension": [
1,
{
"extensions": [
".js",
".jsx"
]
}
],
"radix": 0,
"no-shadow": [
2,
{
"hoist": "all",
"allow": [
"resolve",
"reject",
"done",
"next",
"err",
"error"
]
}
],
"quotes": [
2,
"single",
{
"avoidEscape": true,
"allowTemplateLiterals": true
}
],
"prettier/prettier": [
"error",
{
"trailingComma": "es5",
"singleQuote": true,
"printWidth": 80
}
],
"jsx-a11y/href-no-hash": "off",
"jsx-a11y/anchor-is-valid": [
"warn",
{
"aspects": [
"invalidHref"
]
}
]
"prettier/prettier": ["error", { "singleQuote": true }],
"react/display-name": "off",
"react/react-in-jsx-scope": "off",
"@typescript-eslint/explicit-function-return-type": "off",
"@typescript-eslint/camelcase": "off",
"@typescript-eslint/ban-ts-ignore": "off",
"@typescript-eslint/no-explicit-any": "off"
},
"plugins": [
"prettier"
]
"settings": {
"react": {
"pragma": "React",
"version": "detect"
}
},
"parser": "@typescript-eslint/parser"
}

View file

@ -23,7 +23,7 @@ If you would like to create a new category of security and privacy resources, pl
5. View the running app in your browser at `http://localhost:3000`
### Deploying
You can deploy this project yourself with ZEIT + Now by configuring `now.json` and running `$ now`. You should change the Google Analytics tag in `lib/gtag.js` and the Sentry DSN url in `pages/_app.js`.
You can deploy this project yourself with ZEIT + Now by configuring `now.json` and running `$ now`.
### Feedback
Please open issues at any time for general feedback, or you can reach me directly at hi@brianlovin.com.

View file

@ -16,7 +16,6 @@ import {
InnerContainer,
ScrollToTop,
} from './style';
import * as gtag from '../../lib/gtag';
import { getLocalStorageLength } from '../../lib/localStorage';
import data from '../../config/data';
@ -31,7 +30,6 @@ const totalItemsCount = Object.keys(data).length;
export default function Page(props: Props) {
const { children, displayProgress } = props;
const [lastTrackedPageview, setLastTrackedPageview] = useState(null);
const [showHeaderShadow, setHeaderShadow] = useState(false);
const [scrollToTopVisible, setScrollToTopVisible] = useState(false);
const [progress, setProgress] = useState(0);
@ -48,10 +46,10 @@ export default function Page(props: Props) {
function updateProgress() {
const checkedItemsCount = getLocalStorageLength();
const progressPercentage = checkedItemsCount * 100 / totalItemsCount;
const progressPercentage = (checkedItemsCount * 100) / totalItemsCount;
setProgress(progressPercentage);
setCurrentCount(checkedItemsCount);
};
}
const scrollToTop = () => {
if (window) {
@ -69,32 +67,21 @@ export default function Page(props: Props) {
return () => {
if (window) {
window.removeEventListener('scroll', throttledScroll);
setLastTrackedPageview(null);
}
};
}, [progress]);
useEffect(() => {
if (window && displayProgress) {
window.addEventListener('storage:updated', updateProgress );
window.addEventListener('storage:updated', updateProgress);
}
return () => {
if (window && displayProgress) {
window.removeEventListener('storage:updated', updateProgress );
window.removeEventListener('storage:updated', updateProgress);
}
};
});
useEffect(() => {
if (document) {
const newLocation = document.location.pathname;
if (newLocation !== lastTrackedPageview) {
gtag.pageview(document.location.pathname);
setLastTrackedPageview(newLocation);
}
}
});
return (
<ThemeProvider theme={theme}>
<Container>
@ -102,9 +89,10 @@ export default function Page(props: Props) {
:root {
--progress: ${progress ? 100 - progress : 100}%;
}
`}</style>
`}
</style>
<Header
<Header
showHeaderShadow={showHeaderShadow}
displayProgress={displayProgress}
totalItemsCount={totalItemsCount}

View file

@ -0,0 +1,107 @@
import { ThemeProvider } from 'styled-components';
import Head from 'next/head';
import { DefaultSeo } from 'next-seo';
import { GlobalStyles } from '../../static/normalize';
import SEO from '../../config/next-seo';
import { theme } from '../theme';
interface Props {
children?: any;
}
export default function Providers({ children }: Props) {
return (
<>
<DefaultSeo {...SEO} />
<Head>
<meta name="theme-color" content="#FFF" key="theme-color" />
<meta
name="description"
content="A checklist for staying safe on the internet"
/>
<link
rel="apple-touch-icon"
sizes="57x57"
href="/static/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="/static/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="/static/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="/static/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="/static/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="/static/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="/static/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="/static/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/static/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="/static/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/static/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="/static/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/static/favicon-16x16.png"
/>
<link rel="manifest" href="/static/manifest.json" />
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
<link rel="mask-icon" href="/static/pinned-tab.svg" color="#FFF" />
<meta name="msapplication-TileColor" content="#ffffff" />
</Head>
<GlobalStyles />
<ThemeProvider theme={theme}>
{children}
</ThemeProvider>
</>
);
}

View file

@ -4,6 +4,6 @@
"defaultCommandTimeout": 20000,
"video": false,
"blacklistHosts": [
"*.google-analytics.com"
"*.usefathom.com"
]
}

View file

@ -1,26 +0,0 @@
// @flow
export const GA_TRACKING_ID = 'UA-31162386-5';
// https://developers.google.com/analytics/devguides/collection/gtagjs/pages
export const pageview = (url: string) => {
try {
window.gtag('config', GA_TRACKING_ID, {
page_location: url,
});
} catch (err) {
// dont crash the page if ga fails
}
};
// https://developers.google.com/analytics/devguides/collection/gtagjs/events
export const event = ({ action, category, label, value }: any) => {
try {
window.gtag('event', action, {
event_category: category,
event_label: label,
value,
});
} catch (err) {
// dont crash the page if ga fails
}
};

62
package-lock.json generated
View file

@ -2965,63 +2965,6 @@
"any-observable": "^0.3.0"
}
},
"@sentry/browser": {
"version": "5.9.1",
"resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-5.9.1.tgz",
"integrity": "sha512-7AOabwp9yAH9h6Xe6TfDwlLxHbUSWs+SPWHI7bPlht2yDSAqkXYGSzRr5X0XQJX9oBQdx2cEPMqHyJrbNaP/og==",
"requires": {
"@sentry/core": "5.8.0",
"@sentry/types": "5.7.1",
"@sentry/utils": "5.8.0",
"tslib": "^1.9.3"
}
},
"@sentry/core": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.8.0.tgz",
"integrity": "sha512-aAh2KLidIXJVGrxmHSVq2eVKbu7tZiYn5ylW6yzJXFetS5z4MA+JYaSBaG2inVYDEEqqMIkb17TyWxxziUDieg==",
"requires": {
"@sentry/hub": "5.8.0",
"@sentry/minimal": "5.8.0",
"@sentry/types": "5.7.1",
"@sentry/utils": "5.8.0",
"tslib": "^1.9.3"
}
},
"@sentry/hub": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.8.0.tgz",
"integrity": "sha512-VdApn1ZCNwH1wwQwoO6pu53PM/qgHG+DQege0hbByluImpLBhAj9w50nXnF/8KzV4UoMIVbzCb6jXzMRmqqp9A==",
"requires": {
"@sentry/types": "5.7.1",
"@sentry/utils": "5.8.0",
"tslib": "^1.9.3"
}
},
"@sentry/minimal": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.8.0.tgz",
"integrity": "sha512-MIlFOgd+JvAUrBBmq7vr9ovRH1HvckhnwzHdoUPpKRBN+rQgTyZy1o6+kA2fASCbrRqFCP+Zk7EHMACKg8DpIw==",
"requires": {
"@sentry/hub": "5.8.0",
"@sentry/types": "5.7.1",
"tslib": "^1.9.3"
}
},
"@sentry/types": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.7.1.tgz",
"integrity": "sha512-tbUnTYlSliXvnou5D4C8Zr+7/wJrHLbpYX1YkLXuIJRU0NSi81bHMroAuHWILcQKWhVjaV/HZzr7Y/hhWtbXVQ=="
},
"@sentry/utils": {
"version": "5.8.0",
"resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.8.0.tgz",
"integrity": "sha512-KDxUvBSYi0/dHMdunbxAxD3389pcQioLtcO6CI6zt/nJXeVFolix66cRraeQvqupdLhvOk/el649W4fCPayTHw==",
"requires": {
"@sentry/types": "5.7.1",
"tslib": "^1.9.3"
}
},
"@types/clipboard": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@types/clipboard/-/clipboard-2.0.1.tgz",
@ -7087,6 +7030,11 @@
"reusify": "^1.0.0"
}
},
"fathom-client": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fathom-client/-/fathom-client-1.0.0.tgz",
"integrity": "sha512-YUJJHGOm9KOIjoZDnQeCCPOKu28t6k+hPD49rGAV1/HTknAIDb8J2u5foPg2JgsWcB4ORvJjN/7xSTUPvRK4/Q=="
},
"fd-slicer": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz",

View file

@ -21,7 +21,7 @@
"lint:staged": "lint-staged"
},
"dependencies": {
"@sentry/browser": "^5.9.1",
"fathom-client": "^1.0.0",
"next": "^9.1.4",
"next-seo": "^3.0.0",
"react": "^16.12.0",

View file

@ -1,143 +1,35 @@
// @flow
import * as React from 'react'
import App from 'next/app';
import * as React from 'react';
import Head from 'next/head';
import * as Sentry from '@sentry/browser';
import { NextSeo } from 'next-seo';
import { GlobalStyles } from '../static/normalize';
import defaultSEO from '../config/next-seo.js';
import Fathom from 'fathom-client'
import Router from 'next/router'
import Providers from '../components/Providers';
const SENTRY_PUBLIC_DSN =
'https://42334f0365364b63bc57f4245d111b87@sentry.io/1370339';
Router.events.on('routeChangeComplete', () => {
Fathom.trackPageview()
})
class SecurityChecklistApp extends App {
constructor() {
super();
Sentry.init({ dsn: SENTRY_PUBLIC_DSN });
}
// $FlowFixMe
componentDidCatch(error: mixed, errorInfo: any) {
Sentry.configureScope(scope => {
Object.keys(errorInfo).forEach(key => {
scope.setExtra(key, errorInfo[key]);
});
});
Sentry.captureException(error);
// This is needed to render errors correctly in development / production
super.componentDidCatch(error, errorInfo);
}
componentDidMount() {
Sentry.init({
dsn: 'https://b92b29b696884e5798e161962eac36de@sentry.io/1318151',
});
}
function FathomWrapper(props) {
React.useEffect(() => {
if (process.env.NODE_ENV === 'production') {
Fathom.load();
Fathom.setSiteId('ESMHTGZE');
Fathom.trackPageview();
}
}, [])
return <div {...props} />
}
class MyApp extends App {
render() {
const { Component, pageProps } = this.props;
return (
<>
<GlobalStyles />
<Head>
<meta name="theme-color" content="#FFF" key="theme-color" />
<meta
name="description"
content="A checklist for staying safe on the internet"
/>
<link
rel="apple-touch-icon"
sizes="57x57"
href="/static/apple-icon-57x57.png"
/>
<link
rel="apple-touch-icon"
sizes="60x60"
href="/static/apple-icon-60x60.png"
/>
<link
rel="apple-touch-icon"
sizes="72x72"
href="/static/apple-icon-72x72.png"
/>
<link
rel="apple-touch-icon"
sizes="76x76"
href="/static/apple-icon-76x76.png"
/>
<link
rel="apple-touch-icon"
sizes="114x114"
href="/static/apple-icon-114x114.png"
/>
<link
rel="apple-touch-icon"
sizes="120x120"
href="/static/apple-icon-120x120.png"
/>
<link
rel="apple-touch-icon"
sizes="144x144"
href="/static/apple-icon-144x144.png"
/>
<link
rel="apple-touch-icon"
sizes="152x152"
href="/static/apple-icon-152x152.png"
/>
<link
rel="apple-touch-icon"
sizes="180x180"
href="/static/apple-icon-180x180.png"
/>
<link
rel="icon"
type="image/png"
sizes="192x192"
href="/static/android-icon-192x192.png"
/>
<link
rel="icon"
type="image/png"
sizes="32x32"
href="/static/favicon-32x32.png"
/>
<link
rel="icon"
type="image/png"
sizes="96x96"
href="/static/favicon-96x96.png"
/>
<link
rel="icon"
type="image/png"
sizes="16x16"
href="/static/favicon-16x16.png"
/>
<link rel="manifest" href="/static/manifest.json" />
<meta name="msapplication-TileImage" content="/ms-icon-144x144.png" />
<link rel="mask-icon" href="/static/pinned-tab.svg" color="#FFF" />
<meta name="msapplication-TileColor" content="#ffffff" />
<script
src="https://browser.sentry-cdn.com/4.2.4/bundle.min.js"
crossOrigin="anonymous"
/>
{this.props.styleTags}
</Head>
<NextSeo config={defaultSEO} />
<Component {...pageProps} />
</>
<FathomWrapper>
<Providers>
<Component {...pageProps} />
</Providers>
</FathomWrapper>
);
}
}
export default SecurityChecklistApp;
export default MyApp;

View file

@ -2,7 +2,6 @@
import * as React from 'react';
import Document, { Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components';
import { GA_TRACKING_ID } from '../lib/gtag';
export default class MyDocument extends Document {
// $FlowFixMe
@ -48,22 +47,6 @@ export default class MyDocument extends Document {
<body>
<Main />
<NextScript />
{/* Global Site Tag (gtag.js) - Google Analytics */}
<script
async
src={`https://www.googletagmanager.com/gtag/js?id=${GA_TRACKING_ID}`}
/>
<script
// eslint-disable-next-line
dangerouslySetInnerHTML={{
__html: `
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', '${GA_TRACKING_ID}');
`,
}}
/>
</body>
</html>
);