diff --git a/docs/src/guide/options.md b/docs/src/guide/options.md
index dd17145019..8615491f8b 100644
--- a/docs/src/guide/options.md
+++ b/docs/src/guide/options.md
@@ -39,7 +39,8 @@ These options can be used to define the initial checks to display the [`BoosterL
````js
{
performance: true,
- browserSupport: true
+ browserSupport: true,
+ battery: true
}
````
@@ -47,6 +48,7 @@ These options can be used to define the initial checks to display the [`BoosterL
| ---------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
| `performance` | `Boolean` | yes | Checking whether the [minimum characteristic values](/guide/options#performancemetrics) have been reached. If the test is negative, the [`BoosterLayer`](/components/booster-layer) will be displayed. | `true` |
| `browserSupport` | `Boolean` | yes | Check if the current browser on client side is supported. If the test is negative, the [`BoosterLayer`](/components/booster-layer) will be displayed. | `true` |
+ | `battery` | `Boolean` | yes | Check if the current user save power in browser. If the test is negative, the [`BoosterLayer`](/components/booster-layer) will be displayed. | `true` |
::: info
For the browser support detection, the default [Browserslist](https://github.com/browserslist/browserslist) of the NuxtJS configuration is used.
diff --git a/playground/components/InfoLayer.vue b/playground/components/InfoLayer.vue
index 38bd812f57..54a998c39d 100644
--- a/playground/components/InfoLayer.vue
+++ b/playground/components/InfoLayer.vue
@@ -9,6 +9,7 @@
outdated browser
reduced-bandwidth
weak hardware
+ low battery
mediaContent,
+ filename: MODULE_NAME + '/blobs.mjs',
+ write: true
+ });
}
async function addModules(nuxt, moduleOptions) {
diff --git a/src/runtime/components/BoosterLayer.vue b/src/runtime/components/BoosterLayer.vue
index 083f07f811..38e04d5db5 100644
--- a/src/runtime/components/BoosterLayer.vue
+++ b/src/runtime/components/BoosterLayer.vue
@@ -17,6 +17,8 @@
slow connection
weak hardware
+
+ low battery
@@ -96,5 +98,9 @@ useHead({
display: none;
}
+#nuxt-speedkit-message-low-battery {
+ display: none;
+}
+
/*! purgecss end ignore */
diff --git a/src/runtime/utils/entry.js b/src/runtime/utils/entry.js
index 26184fedc0..fcac110d2f 100644
--- a/src/runtime/utils/entry.js
+++ b/src/runtime/utils/entry.js
@@ -55,3 +55,44 @@ export function initReducedView() {
tmp.remove();
});
}
+
+export async function hasBatteryPerformanceIssue(videoBlob) {
+ try {
+ if (await isBatteryLow()) {
+ throw new Error('Battery is low.');
+ }
+ } catch (error) {
+ if (error.message === 'Battery is low.') {
+ throw error;
+ }
+ await canVideoPlay(videoBlob);
+ }
+}
+
+/**
+ * Checks if battery still has enough energy.
+ * This check is for Chrome and all other browsers that support this setting.
+ *
+ * Condition is: The device is not charging and Battery is below <= 20%.
+ * @see https://blog.google/products/chrome/new-chrome-features-to-save-battery-and-make-browsing-smoother/
+ * @see https://developer.chrome.com/blog/memory-and-energy-saver-mode/
+ **/
+async function isBatteryLow() {
+ const MIN_BATTERY_LEVEL = 0.2;
+ const battery = await window.navigator.getBattery();
+ return !battery.charging && battery.level <= MIN_BATTERY_LEVEL;
+}
+
+/**
+ * Checking whether a video can be played.
+ * This check is for IOS and checks if the power saving mode is enabled.
+ *
+ * In this case no video will be played automatically and play throws an error.
+ */
+export function canVideoPlay(blob) {
+ const video = document.createElement('video');
+ video.muted = true;
+ video.playsinline = true;
+ video.src = URL.createObjectURL(blob);
+ return video.play();
+}
diff --git a/src/tmpl/entry.tmpl.js b/src/tmpl/entry.tmpl.js
index b346c789e3..77982356d3 100644
--- a/src/tmpl/entry.tmpl.js
+++ b/src/tmpl/entry.tmpl.js
@@ -2,9 +2,10 @@ export default options => {
let code = `import { ${
options.performanceCheck ? `run, ` : ``
}hasSufficientPerformance, setup } from '#booster/utils/performance';
-import { triggerRunCallback, observeBoosterButton, setupBoosterLayer, updateBoosterLayerMessage, initReducedView } from '#booster/utils/entry';
+import { triggerRunCallback, observeBoosterButton, setupBoosterLayer, updateBoosterLayerMessage, initReducedView, hasBatteryPerformanceIssue } from '#booster/utils/entry';
import Deferred from '#booster/classes/Deferred';
import { isSupportedBrowser } from '#booster/utils/browser';
+import {video as videoBlob} from './blobs.mjs';
`;
@@ -46,15 +47,38 @@ function client () {
const forceInit = ('__NUXT_BOOSTER_FORCE_INIT__' in window && window.__NUXT_BOOSTER_FORCE_INIT__);
async function initApp(force) {
+
if (initialized) {
deferred.resolve();
}
document.documentElement.classList.remove('nuxt-booster-reduced-view');
+ `;
+
+ if (!options.ignore.battery) {
+ code += `
try {
+ if (!force) {
+ await hasBatteryPerformanceIssue(videoBlob)
+ }
+ } catch (error) {
+
+ console.warn(error)
+
+ triggerRunCallback(false);
+
+ if (!!layerEl) {
+ // User must interact via the layer.
+ updateSpeedkitLayerMessage(layerEl, 'nuxt-speedkit-message-low-battery');
+ return null;
+ }
+ }
+ `;
+ }
- `;
+ code += `
+ try {`;
if (options.performanceCheck) {
code += `
@@ -75,6 +99,9 @@ if (!force) {
deferred.resolve();
} catch (error) {
+
+ console.warn(error)
+
triggerRunCallback(false);
if (!!layerEl) {
diff --git a/src/utils/blob.js b/src/utils/blob.js
new file mode 100644
index 0000000000..7c0663f006
--- /dev/null
+++ b/src/utils/blob.js
@@ -0,0 +1,22 @@
+import { promises as fsPromises } from 'fs';
+import mime from 'mime-types';
+
+async function getFileInfo(name, file) {
+ return {
+ name,
+ // eslint-disable-next-line security/detect-non-literal-fs-filename
+ file: await fsPromises.readFile(file),
+ mimeType: mime.lookup(file)
+ };
+}
+
+export async function getTemplate(files) {
+ return (await Promise.all(files.map(file => getFileInfo(...file))))
+ .map(
+ ({ name, file, mimeType }) =>
+ `export const ${name} = new Blob([new Uint8Array([${[...file].join(
+ ', '
+ )}])], {type: '${mimeType}'});`
+ )
+ .join('\n');
+}
diff --git a/src/utils/options.js b/src/utils/options.js
index e9ab1b6bc7..736a07867d 100644
--- a/src/utils/options.js
+++ b/src/utils/options.js
@@ -13,7 +13,8 @@ export function getDefaultOptions() {
detection: {
performance: true,
- browserSupport: true
+ browserSupport: true,
+ battery: true
},
performanceMetrics: {