From a2b980861336a388afffbef5623057dd2ef9844b Mon Sep 17 00:00:00 2001 From: Jeff Avallone Date: Tue, 29 May 2018 19:01:19 -0400 Subject: [PATCH] Adding PWA install prompt --- src/components/App/index.js | 38 ++++++++++++++++++- .../InstallPrompt/__snapshots__/test.js.snap | 31 +++++++++++++++ src/components/InstallPrompt/index.js | 23 +++++++++++ src/components/InstallPrompt/style.css | 38 +++++++++++++++++++ src/components/InstallPrompt/test.js | 14 +++++++ src/locales/en/translation.yaml | 3 ++ 6 files changed, 146 insertions(+), 1 deletion(-) create mode 100644 src/components/InstallPrompt/__snapshots__/test.js.snap create mode 100644 src/components/InstallPrompt/index.js create mode 100644 src/components/InstallPrompt/style.css create mode 100644 src/components/InstallPrompt/test.js diff --git a/src/components/App/index.js b/src/components/App/index.js index 1fb1766..3c59572 100644 --- a/src/components/App/index.js +++ b/src/components/App/index.js @@ -10,6 +10,7 @@ import style from './style.css'; import Form from 'components/Form'; import Message from 'components/Message'; +import InstallPrompt from 'components/InstallPrompt'; import { demoImage } from 'devel'; const syntaxes = { @@ -24,11 +25,13 @@ class App extends React.PureComponent { componentDidMount() { window.addEventListener('hashchange', this.handleHashChange); + window.addEventListener('beforeinstallprompt', this.handleInstallPrompt); this.handleHashChange(); } componentWillUnmount() { window.removeEventListener('hashchange', this.handleHashChange); + window.removeEventListener('beforeinstallprompt', this.handleInstallPrompt); } async setSvgUrl() { @@ -97,6 +100,14 @@ class App extends React.PureComponent { } } + handleInstallPrompt = event => { + event.preventDefault(); + + this.setState({ + installPrompt: event + }); + } + handleSubmit = ({expr, syntax}) => { if (expr) { const params = new URLSearchParams({ syntax, expr }); @@ -150,8 +161,30 @@ class App extends React.PureComponent { this.handleHashChange(); } + handleInstallReject = () => { + this.setState({ installPrompt: null }); + } + + handleInstallAccept = async () => { + const { installPrompt } = this.state; + + this.setState({ installPrompt: null }); + installPrompt.prompt(); + } + render() { - const { SVG, loading, loadingFailed, svgUrl, pngUrl, permalinkUrl, syntax, expr, image } = this.state; + const { + SVG, + loading, + loadingFailed, + svgUrl, + pngUrl, + permalinkUrl, + syntax, + expr, + image, + installPrompt + } = this.state; const downloadUrls = [ svgUrl, pngUrl @@ -181,6 +214,9 @@ class App extends React.PureComponent { } + { + installPrompt && + } ; } } diff --git a/src/components/InstallPrompt/__snapshots__/test.js.snap b/src/components/InstallPrompt/__snapshots__/test.js.snap new file mode 100644 index 0000000..363f014 --- /dev/null +++ b/src/components/InstallPrompt/__snapshots__/test.js.snap @@ -0,0 +1,31 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`InstallPrompt rendering 1`] = ` +
+

+ + Add Regexper to your home screen? + +

+
+ + +
+
+`; diff --git a/src/components/InstallPrompt/index.js b/src/components/InstallPrompt/index.js new file mode 100644 index 0000000..69e1b14 --- /dev/null +++ b/src/components/InstallPrompt/index.js @@ -0,0 +1,23 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { translate, Trans } from 'react-i18next'; + +import style from './style.css'; + +const InstallPrompt = ({ onAccept, onReject }) => ( +
+

Add Regexper to your home screen?

+
+ + +
+
+); + +InstallPrompt.propTypes = { + onAccept: PropTypes.func.isRequired, + onReject: PropTypes.func.isRequired +}; + +export default translate()(InstallPrompt); +export { InstallPrompt }; diff --git a/src/components/InstallPrompt/style.css b/src/components/InstallPrompt/style.css new file mode 100644 index 0000000..7c9affb --- /dev/null +++ b/src/components/InstallPrompt/style.css @@ -0,0 +1,38 @@ +@import url('../../globals.css'); + +.install { + position: fixed; + bottom: 0; + left: 0; + right: 0; + background: var(--color-tan); + color: var(--color-black); +} + +.cta { + margin: 0; + padding: var(--spacing-margin); + text-align: center; +} + +.actions { + display: flex; + flex-wrap: nowrap; + justify-content: space-evenly; + padding: var(--spacing-margin); + + & button { + font-size: inherit; + line-height: 2.8rem; + border: 0 none; + background: var(--color-green) var(--gradient-green); + color: var(--color-black); + cursor: pointer; + padding: 0; + width: 40vw; + + &.primary { + font-weight: bold; + } + } +} diff --git a/src/components/InstallPrompt/test.js b/src/components/InstallPrompt/test.js new file mode 100644 index 0000000..f0249ee --- /dev/null +++ b/src/components/InstallPrompt/test.js @@ -0,0 +1,14 @@ +import React from 'react'; +import { shallow } from 'enzyme'; + +import { InstallPrompt } from 'components/InstallPrompt'; +import { translate } from '__mocks__/i18n'; + +describe('InstallPrompt', () => { + test('rendering', () => { + const component = shallow( + + ); + expect(component).toMatchSnapshot(); + }); +}); diff --git a/src/locales/en/translation.yaml b/src/locales/en/translation.yaml index c0d5472..1258708 100644 --- a/src/locales/en/translation.yaml +++ b/src/locales/en/translation.yaml @@ -1,4 +1,6 @@ 404 Page Not Found: '404: Page Not Found' +Add It: Add It +Add Regexper to your home screen?: Add Regexper to your home screen? An error has occurred: An error has occurred Created by <1>Jeff Avallone: Created by <1>Jeff Avallone Display: Display @@ -6,6 +8,7 @@ Download PNG: Download PNG Download SVG: Download SVG Enter regular expression to display: Enter regular expression to display Generated images licensed: 'Generated images licensed: <1><0>' +No Thanks: No Thanks Permalink: Permalink Privacy Policy: Privacy Policy Privacy Policy Content: >