Adding PWA install prompt
This commit is contained in:
parent
589b1aa9e0
commit
a2b9808613
@ -10,6 +10,7 @@ import style from './style.css';
|
|||||||
|
|
||||||
import Form from 'components/Form';
|
import Form from 'components/Form';
|
||||||
import Message from 'components/Message';
|
import Message from 'components/Message';
|
||||||
|
import InstallPrompt from 'components/InstallPrompt';
|
||||||
import { demoImage } from 'devel';
|
import { demoImage } from 'devel';
|
||||||
|
|
||||||
const syntaxes = {
|
const syntaxes = {
|
||||||
@ -24,11 +25,13 @@ class App extends React.PureComponent {
|
|||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
window.addEventListener('hashchange', this.handleHashChange);
|
window.addEventListener('hashchange', this.handleHashChange);
|
||||||
|
window.addEventListener('beforeinstallprompt', this.handleInstallPrompt);
|
||||||
this.handleHashChange();
|
this.handleHashChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentWillUnmount() {
|
componentWillUnmount() {
|
||||||
window.removeEventListener('hashchange', this.handleHashChange);
|
window.removeEventListener('hashchange', this.handleHashChange);
|
||||||
|
window.removeEventListener('beforeinstallprompt', this.handleInstallPrompt);
|
||||||
}
|
}
|
||||||
|
|
||||||
async setSvgUrl() {
|
async setSvgUrl() {
|
||||||
@ -97,6 +100,14 @@ class App extends React.PureComponent {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleInstallPrompt = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
installPrompt: event
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
handleSubmit = ({expr, syntax}) => {
|
handleSubmit = ({expr, syntax}) => {
|
||||||
if (expr) {
|
if (expr) {
|
||||||
const params = new URLSearchParams({ syntax, expr });
|
const params = new URLSearchParams({ syntax, expr });
|
||||||
@ -150,8 +161,30 @@ class App extends React.PureComponent {
|
|||||||
this.handleHashChange();
|
this.handleHashChange();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleInstallReject = () => {
|
||||||
|
this.setState({ installPrompt: null });
|
||||||
|
}
|
||||||
|
|
||||||
|
handleInstallAccept = async () => {
|
||||||
|
const { installPrompt } = this.state;
|
||||||
|
|
||||||
|
this.setState({ installPrompt: null });
|
||||||
|
installPrompt.prompt();
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
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 = [
|
const downloadUrls = [
|
||||||
svgUrl,
|
svgUrl,
|
||||||
pngUrl
|
pngUrl
|
||||||
@ -181,6 +214,9 @@ class App extends React.PureComponent {
|
|||||||
<SVG data={ image } ref={ this.image }/>
|
<SVG data={ image } ref={ this.image }/>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
{
|
||||||
|
installPrompt && <InstallPrompt onAccept={ this.handleInstallAccept } onReject={ this.handleInstallReject } />
|
||||||
|
}
|
||||||
</React.Fragment>;
|
</React.Fragment>;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
31
src/components/InstallPrompt/__snapshots__/test.js.snap
Normal file
31
src/components/InstallPrompt/__snapshots__/test.js.snap
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||||
|
|
||||||
|
exports[`InstallPrompt rendering 1`] = `
|
||||||
|
<div
|
||||||
|
className="install"
|
||||||
|
>
|
||||||
|
<p
|
||||||
|
className="cta"
|
||||||
|
>
|
||||||
|
<Trans>
|
||||||
|
Add Regexper to your home screen?
|
||||||
|
</Trans>
|
||||||
|
</p>
|
||||||
|
<div
|
||||||
|
className="actions"
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
className="primary"
|
||||||
|
>
|
||||||
|
<Trans>
|
||||||
|
Add It
|
||||||
|
</Trans>
|
||||||
|
</button>
|
||||||
|
<button>
|
||||||
|
<Trans>
|
||||||
|
No Thanks
|
||||||
|
</Trans>
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`;
|
23
src/components/InstallPrompt/index.js
Normal file
23
src/components/InstallPrompt/index.js
Normal file
@ -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 }) => (
|
||||||
|
<div className={ style.install }>
|
||||||
|
<p className={ style.cta }><Trans>Add Regexper to your home screen?</Trans></p>
|
||||||
|
<div className={ style.actions }>
|
||||||
|
<button className={ style.primary } onClick={ onAccept }><Trans>Add It</Trans></button>
|
||||||
|
<button onClick={ onReject }><Trans>No Thanks</Trans></button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
|
||||||
|
InstallPrompt.propTypes = {
|
||||||
|
onAccept: PropTypes.func.isRequired,
|
||||||
|
onReject: PropTypes.func.isRequired
|
||||||
|
};
|
||||||
|
|
||||||
|
export default translate()(InstallPrompt);
|
||||||
|
export { InstallPrompt };
|
38
src/components/InstallPrompt/style.css
Normal file
38
src/components/InstallPrompt/style.css
Normal file
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
14
src/components/InstallPrompt/test.js
Normal file
14
src/components/InstallPrompt/test.js
Normal file
@ -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(
|
||||||
|
<InstallPrompt t={ translate }/>
|
||||||
|
);
|
||||||
|
expect(component).toMatchSnapshot();
|
||||||
|
});
|
||||||
|
});
|
@ -1,4 +1,6 @@
|
|||||||
404 Page Not Found: '404: Page Not Found'
|
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
|
An error has occurred: An error has occurred
|
||||||
Created by <1>Jeff Avallone</1>: Created by <1>Jeff Avallone</1>
|
Created by <1>Jeff Avallone</1>: Created by <1>Jeff Avallone</1>
|
||||||
Display: Display
|
Display: Display
|
||||||
@ -6,6 +8,7 @@ Download PNG: Download PNG
|
|||||||
Download SVG: Download SVG
|
Download SVG: Download SVG
|
||||||
Enter regular expression to display: Enter regular expression to display
|
Enter regular expression to display: Enter regular expression to display
|
||||||
Generated images licensed: 'Generated images licensed: <1><0></0></1>'
|
Generated images licensed: 'Generated images licensed: <1><0></0></1>'
|
||||||
|
No Thanks: No Thanks
|
||||||
Permalink: Permalink
|
Permalink: Permalink
|
||||||
Privacy Policy: Privacy Policy
|
Privacy Policy: Privacy Policy
|
||||||
Privacy Policy Content: >
|
Privacy Policy Content: >
|
||||||
|
Loading…
Reference in New Issue
Block a user