Merge branch 'master' into master

This commit is contained in:
Brian Lovin 2019-12-26 23:24:23 -05:00 committed by GitHub
commit 2c14183ce5
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
29 changed files with 9129 additions and 11973 deletions

View file

@ -1,126 +1,31 @@
{ {
"extends": [ "extends": [
"airbnb", "eslint:recommended",
"prettier", "plugin:react/recommended",
"plugin:react/recommended" "plugin:@typescript-eslint/recommended",
"prettier/@typescript-eslint",
"plugin:prettier/recommended"
], ],
"parser": "babel-eslint", "plugins": ["react", "@typescript-eslint", "prettier"],
"parserOptions": {
"ecmaVersion": 8,
"ecmaFeatures": {
"experimentalObjectRestSpread": true,
"impliedStrict": true,
"classes": true
}
},
"env": { "env": {
"browser": true, "browser": true,
"node": true, "jasmine": true,
"jquery": true,
"jest": true "jest": true
}, },
"rules": { "rules": {
"no-debugger": 0, "prettier/prettier": ["error", { "singleQuote": true }],
"no-alert": 0, "react/display-name": "off",
"no-unused-vars": [ "react/react-in-jsx-scope": "off",
1, "@typescript-eslint/explicit-function-return-type": "off",
{ "@typescript-eslint/camelcase": "off",
"ignoreSiblings": true, "@typescript-eslint/ban-ts-ignore": "off",
"argsIgnorePattern": "res|next|^err" "@typescript-eslint/no-explicit-any": "off"
}
],
"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"
]
}
]
}, },
"plugins": [ "settings": {
"prettier" "react": {
] "pragma": "React",
"version": "detect"
}
},
"parser": "@typescript-eslint/parser"
} }

View file

@ -11,7 +11,7 @@ suppress_comment=.*\\$FlowFixMe
suppress_comment=.*\\$FlowIssue suppress_comment=.*\\$FlowIssue
esproposal.class_instance_fields=enable esproposal.class_instance_fields=enable
module.system.node.resolve_dirname=node_modules module.system.node.resolve_dirname=node_modules
module.system.node.resolve_dirname=. module.system.node.allow_root_relative=true
module.file_ext=.js module.file_ext=.js
module.file_ext=.jsx module.file_ext=.jsx
module.file_ext=.json module.file_ext=.json
@ -21,6 +21,3 @@ untyped-type-import=error
untyped-import=warn untyped-import=warn
unclear-type=warn unclear-type=warn
unsafe-getters-setters=error unsafe-getters-setters=error
[version]
0.95.1

4
.github/release-drafter.yml vendored Normal file
View file

@ -0,0 +1,4 @@
template: |
## Changes since last release
$CHANGES

22
.github/workflows/main.yml vendored Normal file
View file

@ -0,0 +1,22 @@
name: E2E on Chrome
on:
push:
branches:
- master
pull_request:
branches:
- master
jobs:
cypress-run:
runs-on: ubuntu-latest
# let's make sure our tests pass on Chrome browser
name: E2E on Chrome
steps:
- name: Checkout
uses: actions/checkout@v1
- name: Cypress run
uses: cypress-io/github-action@v1
with:
build: yarn run build
start: yarn run start
browser: chrome

3
.nowignore Normal file
View file

@ -0,0 +1,3 @@
cypress
README.md
package-lock.json

View file

@ -1,19 +0,0 @@
language: node_js
node_js:
- 10
cache:
directories:
- ~/.npm
- ~/.cache
install:
- npm install
before_script:
- npm run build
- npm run start &
script:
- node --version
- npm --version
- npm run flow
- npm run cypress:run
notifications:
email: false

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` 5. View the running app in your browser at `http://localhost:3000`
### Deploying ### 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 ### Feedback
Please open issues at any time for general feedback, or you can reach me directly at hi@brianlovin.com. Please open issues at any time for general feedback, or you can reach me directly at hi@brianlovin.com.

View file

@ -28,6 +28,7 @@ export default function CopyLinkButton(props: CopyLinkProps) {
isClicked={isClicked} isClicked={isClicked}
aria-label="Copy the websites address to your clipboard." aria-label="Copy the websites address to your clipboard."
type="button" type="button"
// $FlowIssue
{...props} {...props}
> >
<Icon glyph="link" size={24} /> <Icon glyph="link" size={24} />

View file

@ -64,7 +64,7 @@ export const Apps = ({ resource, handleAppsExpand }: Props) => {
aria-expanded={overflowExpanded} aria-expanded={overflowExpanded}
aria-controls={`apps_${resource.id}`} aria-controls={`apps_${resource.id}`}
> >
<Button type="button">Show {overflowExpanded ? "less" : "more"} choices</Button> <Button type="button">Show {overflowExpanded ? "fewer" : "more"} choices</Button>
</ExpandContainer> </ExpandContainer>
</React.Fragment> </React.Fragment>
)} )}

View file

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

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

@ -62,6 +62,7 @@ export default {
windows: 'https://vivaldi.com/download/', windows: 'https://vivaldi.com/download/',
macos: 'https://vivaldi.com/download/', macos: 'https://vivaldi.com/download/',
linux: 'https://vivaldi.com/download/', linux: 'https://vivaldi.com/download/',
android: 'https://vivaldi.com/android/',
}, },
}, },
{ {

View file

@ -8,7 +8,7 @@ export default {
{ {
name: 'Why you should encrypt your computer', name: 'Why you should encrypt your computer',
url: url:
'https://www.aheliotech.com/tutorials/why-you-should-encrypt-your-computer/', 'https://theintercept.com/2015/04/27/encrypting-laptop-like-mean/',
}, },
{ {
name: name:

View file

@ -4,6 +4,16 @@ export default {
description: `Over the years social media companies are able to gather staggering amounts of data about you, your interests, who you talk to, where you go, what you buy, and so much more. description: `Over the years social media companies are able to gather staggering amounts of data about you, your interests, who you talk to, where you go, what you buy, and so much more.
\n\nIf youre not ready to give up social media quite yet, you should take the time to review your security and privacy settings. Visualizing the amount of information that social media companies know about you may be enough to curb that unhealthy newsfeed obsession.`, \n\nIf youre not ready to give up social media quite yet, you should take the time to review your security and privacy settings. Visualizing the amount of information that social media companies know about you may be enough to curb that unhealthy newsfeed obsession.`,
apps: [
{
name: 'Jumbo',
image: '/static/img/jumbo.png',
url: 'https://www.jumboprivacy.com/',
sources: {
ios: 'https://apps.apple.com/us/app/jumbo-privacy/id1454039975'
}
}
],
resources: [ resources: [
{ {
name: 'Facebook privacy settings', name: 'Facebook privacy settings',

View file

@ -34,6 +34,10 @@ export default {
name: 'ExpressVPN', name: 'ExpressVPN',
image: '/static/img/expressvpn.jpg', image: '/static/img/expressvpn.jpg',
url: 'https://www.expressvpn.com/', url: 'https://www.expressvpn.com/',
offer: {
label: 'Get 30 days free',
url: 'https://www.expressrefer.com/refer-friend?referrer_id=44490458&utm_campaign=referrals&utm_medium=copy_link&utm_source=referral_dashboard',
},
sources: { sources: {
windows: 'https://www.expressvpn.com/vpn-software/vpn-windows', windows: 'https://www.expressvpn.com/vpn-software/vpn-windows',
macos: 'https://www.expressvpn.com/vpn-software/vpn-mac', macos: 'https://www.expressvpn.com/vpn-software/vpn-mac',
@ -96,7 +100,15 @@ export default {
ios: ' https://www.purevpn.com/download/ios-vpn', ios: ' https://www.purevpn.com/download/ios-vpn',
android: 'https://www.purevpn.com/download/android-vpn', android: 'https://www.purevpn.com/download/android-vpn',
linux:'https://www.purevpn.com/download/linux-vpn', linux:'https://www.purevpn.com/download/linux-vpn',
}, }
},
{
name: 'Guardian Firewall',
image: '/static/img/guardian.png',
url: 'https://guardianapp.com',
sources: {
ios: 'https://itunes.apple.com/us/app/guardian-firewall/id1363796315'
}
} }
], ],
resources: [ resources: [

View file

@ -4,6 +4,6 @@
"defaultCommandTimeout": 20000, "defaultCommandTimeout": 20000,
"video": false, "video": false,
"blacklistHosts": [ "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
}
};

View file

@ -1 +1,11 @@
module.exports = {}; module.exports = {
target: 'serverless',
webpack: config => {
// Fixes npm packages that depend on `fs` module
config.node = {
fs: 'empty',
};
return config;
},
};

View file

@ -1,12 +1,4 @@
{ {
"alias": ["securitycheckli.st"], "alias": ["securitycheckli.st"],
"files": [ "version": 2
"components",
"config",
"lib",
"pages",
"static",
"types",
".babelrc"
]
} }

11573
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -18,38 +18,38 @@
"lint:staged": "lint-staged" "lint:staged": "lint-staged"
}, },
"dependencies": { "dependencies": {
"@sentry/browser": "^5.4.0", "fathom-client": "^1.0.0",
"next": "^7.0.3", "next": "^9.1.6",
"next-seo": "^1.11.2", "next-seo": "^3.2.0",
"react": "^16.8.0-alpha.0", "react": "^16.12.0",
"react-clipboard.js": "^2.0.12", "react-clipboard.js": "^2.0.16",
"react-dom": "^16.8.0-alpha.0", "react-dom": "^16.12.0",
"react-markdown": "^4.0.8", "react-markdown": "^4.2.2",
"styled-components": "^4.3.1", "styled-components": "^4.4.1",
"throttle-debounce": "^2.1.0" "throttle-debounce": "^2.1.0"
}, },
"devDependencies": { "devDependencies": {
"@babel/core": "^7.4.5", "@babel/core": "^7.7.7",
"@babel/plugin-transform-flow-strip-types": "^7.4.4", "@babel/plugin-transform-flow-strip-types": "^7.7.4",
"@babel/preset-flow": "^7.0.0", "@babel/preset-flow": "^7.7.4",
"@cypress/browserify-preprocessor": "^2.1.1", "@cypress/browserify-preprocessor": "^2.1.1",
"babel-eslint": "^10.0.1", "babel-eslint": "^10.0.3",
"babel-plugin-styled-components": "^1.10.0", "babel-plugin-styled-components": "^1.10.6",
"babel-plugin-transform-define": "^1.3.1", "babel-plugin-transform-define": "^2.0.0",
"cypress": "^3.3.1", "cypress": "^3.8.0",
"eslint": "^5.16.0", "eslint": "^6.8.0",
"eslint-config-airbnb": "^17.1.0", "eslint-config-airbnb": "^18.0.1",
"eslint-config-prettier": "^4.3.0", "eslint-config-prettier": "^6.8.0",
"eslint-config-react-app": "^4.0.1", "eslint-config-react-app": "^5.0.1",
"eslint-plugin-flowtype": "^3.9.1", "eslint-plugin-flowtype": "^4.5.2",
"eslint-plugin-import": "^2.17.3", "eslint-plugin-import": "^2.19.1",
"eslint-plugin-jsx-a11y": "^6.2.1", "eslint-plugin-jsx-a11y": "^6.2.3",
"eslint-plugin-prettier": "^3.1.0", "eslint-plugin-prettier": "^3.1.2",
"eslint-plugin-react": "^7.13.0", "eslint-plugin-react": "^7.17.0",
"flow-bin": "^0.95.1", "flow-bin": "^0.114.0",
"lint-staged": "^8.2.0", "lint-staged": "^9.5.0",
"prettier": "^1.18.2", "prettier": "^1.19.1",
"prettier-eslint": "^8.8.2" "prettier-eslint": "^9.0.1"
}, },
"lint-staged": { "lint-staged": {
"*.js": [ "*.js": [

View file

@ -1,143 +1,35 @@
// @flow import * as React from 'react'
import App, { Container } from 'next/app'; import App from 'next/app';
import * as React from 'react'; import Fathom from 'fathom-client'
import Head from 'next/head'; import Router from 'next/router'
import * as Sentry from '@sentry/browser'; import Providers from '../components/Providers';
import NextSeo from 'next-seo';
import { GlobalStyles } from '../static/normalize';
import defaultSEO from '../config/next-seo.js';
const SENTRY_PUBLIC_DSN = Router.events.on('routeChangeComplete', () => {
'https://42334f0365364b63bc57f4245d111b87@sentry.io/1370339'; Fathom.trackPageview()
})
class SecurityChecklistApp extends App { function FathomWrapper(props) {
constructor() { React.useEffect(() => {
super(); if (process.env.NODE_ENV === 'production') {
Fathom.load();
Sentry.init({ dsn: SENTRY_PUBLIC_DSN }); Fathom.setSiteId('ESMHTGZE');
} Fathom.trackPageview();
}
// $FlowFixMe }, [])
componentDidCatch(error: mixed, errorInfo: any) { return <div {...props} />
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',
});
}
class MyApp extends App {
render() { render() {
const { Component, pageProps } = this.props; const { Component, pageProps } = this.props;
return ( return (
<Container> <FathomWrapper>
<GlobalStyles /> <Providers>
<Component {...pageProps} />
<Head> </Providers>
<meta name="theme-color" content="#FFF" key="theme-color" /> </FathomWrapper>
<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} />
</Container>
); );
} }
} }
export default SecurityChecklistApp; export default MyApp;

View file

@ -2,7 +2,6 @@
import * as React from 'react'; import * as React from 'react';
import Document, { Head, Main, NextScript } from 'next/document'; import Document, { Head, Main, NextScript } from 'next/document';
import { ServerStyleSheet } from 'styled-components'; import { ServerStyleSheet } from 'styled-components';
import { GA_TRACKING_ID } from '../lib/gtag';
export default class MyDocument extends Document { export default class MyDocument extends Document {
// $FlowFixMe // $FlowFixMe
@ -48,22 +47,6 @@ export default class MyDocument extends Document {
<body> <body>
<Main /> <Main />
<NextScript /> <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> </body>
</html> </html>
); );

View file

@ -1,18 +1,8 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import type { GetInitialProps } from '../types';
import Home from './index'; import Home from './index';
class Error extends React.Component<{}> { class Error extends React.Component<{}> {
static async getInitialProps({ res }: GetInitialProps) {
if (res && res.setHeader) {
const cacheAge = 60 * 60 * 24 * 30;
res.setHeader('Cache-Control', `public,s-maxage=${cacheAge}`);
}
return {};
}
render() { render() {
return <Home />; return <Home />;
} }

View file

@ -1,6 +1,6 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import NextSeo from 'next-seo'; import { NextSeo } from 'next-seo';
import Page, { SectionHeading, Heading, Subheading } from '../components/Page'; import Page, { SectionHeading, Heading, Subheading } from '../components/Page';
export default function About() { export default function About() {

View file

@ -1,25 +1,14 @@
// @flow // @flow
import * as React from 'react'; import * as React from 'react';
import Page, { SectionHeading, Heading, Subheading } from '../components/Page'; import Page, { SectionHeading, Heading, Subheading } from '../components/Page';
import type { GetInitialProps } from '../types';
import Checklist from '../components/Checklist'; import Checklist from '../components/Checklist';
import ShareButtons from '../components/ShareButtons'; import ShareButtons from '../components/ShareButtons';
import BottomShare from '../components/BottomShare'; import BottomShare from '../components/BottomShare';
class Index extends React.Component<{}> { class Index extends React.Component<{}> {
static async getInitialProps({ res }: GetInitialProps) {
if (res && res.setHeader) {
// cache podcasts for a month
const cacheAge = 60 * 60 * 24 * 30;
res.setHeader('Cache-Control', `public,s-maxage=${cacheAge}`);
}
return {};
}
render() { render() {
return ( return (
<Page displayProgress={true}> <Page displayProgress>
<SectionHeading> <SectionHeading>
<Heading>Be safe on the internet.</Heading> <Heading>Be safe on the internet.</Heading>
<Subheading> <Subheading>

BIN
static/img/guardian.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

BIN
static/img/jumbo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

8868
yarn.lock Normal file

File diff suppressed because it is too large Load diff