diff --git a/.eslintrc.json b/.eslintrc.json index 993caf7..a8a1847 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -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" } \ No newline at end of file diff --git a/README.md b/README.md index 9463386..452db30 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/components/Page/index.js b/components/Page/index.js index c53823c..1c51636 100644 --- a/components/Page/index.js +++ b/components/Page/index.js @@ -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 ( @@ -102,9 +89,10 @@ export default function Page(props: Props) { :root { --progress: ${progress ? 100 - progress : 100}%; } - `} + `} + -
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {children} + + + ); +} diff --git a/cypress.json b/cypress.json index 6c86673..c91c3af 100644 --- a/cypress.json +++ b/cypress.json @@ -4,6 +4,6 @@ "defaultCommandTimeout": 20000, "video": false, "blacklistHosts": [ - "*.google-analytics.com" + "*.usefathom.com" ] } diff --git a/lib/gtag.js b/lib/gtag.js deleted file mode 100644 index 45a935d..0000000 --- a/lib/gtag.js +++ /dev/null @@ -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 - } -}; diff --git a/package-lock.json b/package-lock.json index 53feb06..bfbf38c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -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", diff --git a/package.json b/package.json index da4d22d..f885682 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/pages/_app.js b/pages/_app.js index deaf190..fa98c92 100644 --- a/pages/_app.js +++ b/pages/_app.js @@ -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
+} +class MyApp extends App { render() { const { Component, pageProps } = this.props; - return ( - <> - - - - - - - - - - - - - - - - - - - - - - - - - -