Merge pull request #764 from petele/install-prompt

This commit is contained in:
Surma
2020-06-24 16:12:48 +01:00
committed by GitHub
4 changed files with 127 additions and 2 deletions

View File

@ -10,6 +10,7 @@ Google Analytics is used to record the following:
* [Basic visit data](https://support.google.com/analytics/answer/6004245?ref_topic=2919631).
* Before and after image size once an image is downloaded. These values are rounded to the nearest
kilobyte.
* If install is available, when Squoosh is installed, and what method was used to install Squoosh.
Image compression is handled locally; no additional data is sent to the server.

View File

@ -41,17 +41,31 @@ const demos = [
},
];
const installButtonSource = 'introInstallButton';
interface Props {
onFile: (file: File | Fileish) => void;
showSnack: SnackBarElement['showSnackbar'];
}
interface State {
fetchingDemoIndex?: number;
beforeInstallEvent?: BeforeInstallPromptEvent;
}
export default class Intro extends Component<Props, State> {
state: State = {};
private fileInput?: HTMLInputElement;
private installingViaButton = false;
constructor() {
super();
// Listen for beforeinstallprompt events, indicating Squoosh is installable.
window.addEventListener('beforeinstallprompt', this.onBeforeInstallPromptEvent);
// Listen for the appinstalled event, indicating Squoosh has been installed.
window.addEventListener('appinstalled', this.onAppInstalled);
}
@bind
private resetFileInput() {
@ -90,7 +104,52 @@ export default class Intro extends Component<Props, State> {
}
}
render({ }: Props, { fetchingDemoIndex }: State) {
@bind
private onBeforeInstallPromptEvent(event: BeforeInstallPromptEvent) {
// Don't show the mini-infobar on mobile
event.preventDefault();
// Save the beforeinstallprompt event so it can be called later.
this.setState({ beforeInstallEvent: event });
// Log the event.
ga('send', 'event', 'pwa-install', 'available');
}
@bind
private async onInstallClick(event: Event) {
// Get the deferred beforeinstallprompt event
const beforeInstallEvent = this.state.beforeInstallEvent;
// If there's no deferred prompt, bail.
if (!beforeInstallEvent) return;
this.installingViaButton = true;
// Show the browser install prompt
beforeInstallEvent.prompt();
// Wait for the user to accept or dismiss the install prompt
const { outcome } = await beforeInstallEvent.userChoice;
ga('send', 'event', 'pwa-install', installButtonSource, outcome);
// If the prompt was dismissed, we aren't going to install via the button.
if (outcome === 'dismissed') {
this.installingViaButton = false;
}
}
@bind
private onAppInstalled() {
// Try to get the install, if it's not set, use 'browser'
const source = this.installingViaButton ? installButtonSource : 'browser';
ga('send', 'event', 'pwa-install', 'installed', source);
this.installingViaButton = false;
// We don't need the install button, if it's shown
this.setState({ beforeInstallEvent: undefined });
}
render({ }: Props, { fetchingDemoIndex, beforeInstallEvent }: State) {
return (
<div class={style.intro}>
<div>
@ -120,7 +179,7 @@ export default class Intro extends Component<Props, State> {
<img class={style.demoIcon} src={demo.iconUrl} alt="" decoding="async" />
{fetchingDemoIndex === i &&
<div class={style.demoLoading}>
<loading-spinner class={style.demoLoadingSpinner}/>
<loading-spinner class={style.demoLoadingSpinner} />
</div>
}
</div>
@ -132,6 +191,15 @@ export default class Intro extends Component<Props, State> {
)}
</ul>
</div>
{beforeInstallEvent &&
<button
type="button"
class={style.installButton}
onClick={this.onInstallClick}
>
Install
</button>
}
<ul class={style.relatedLinks}>
<li><a href="https://github.com/GoogleChromeLabs/squoosh/">View the code</a></li>
<li><a href="https://github.com/GoogleChromeLabs/squoosh/issues">Report a bug</a></li>

32
src/components/intro/missing-types.d.ts vendored Normal file
View File

@ -0,0 +1,32 @@
/**
* The BeforeInstallPromptEvent is fired at the Window.onbeforeinstallprompt handler
* before a user is prompted to "install" a web site to a home screen on mobile.
*/
interface BeforeInstallPromptEvent extends Event {
/**
* Returns an array of DOMString items containing the platforms on which the event was dispatched.
* This is provided for user agents that want to present a choice of versions to the user such as,
* for example, "web" or "play" which would allow the user to chose between a web version or
* an Android version.
*/
readonly platforms: Array<string>;
/**
* Returns a Promise that resolves to a DOMString containing either "accepted" or "dismissed".
*/
readonly userChoice: Promise<{
outcome: 'accepted' | 'dismissed',
platform: string
}>;
/**
* Allows a developer to show the install prompt at a time of their own choosing.
* This method returns a Promise.
*/
prompt(): Promise<void>;
}
interface WindowEventMap {
"beforeinstallprompt": BeforeInstallPromptEvent;
}

View File

@ -170,6 +170,30 @@
--color: #fff;
}
.install-button {
composes: unbutton from '../../lib/util.scss';
&:hover,
&:focus {
background: #f5f5f5;
}
background: #fff;
border: 1px solid #e8e8e8;
padding: 14px;
font-size: 1.3rem;
position: absolute;
top: 1rem;
right: 1rem;
animation: fade-in .3s ease-in-out;
}
@keyframes fade-in {
from { opacity: 0; }
}
.related-links {
display: flex;
padding: 0;