diff --git a/README.md b/README.md index 1f32797f..72e6ed2a 100644 --- a/README.md +++ b/README.md @@ -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. diff --git a/src/components/intro/index.tsx b/src/components/intro/index.tsx index c9ca9536..f8c95ad7 100644 --- a/src/components/intro/index.tsx +++ b/src/components/intro/index.tsx @@ -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 { 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 { } } - 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 (
@@ -120,7 +179,7 @@ export default class Intro extends Component { {fetchingDemoIndex === i &&
- +
}
@@ -132,6 +191,15 @@ export default class Intro extends Component { )}
+ {beforeInstallEvent && + + }