Skip to content

Commit 8e48dab

Browse files
Merge pull request #851 from basics/feature/entry-battery-level-check
fix(entry): added battery level check
2 parents 5ebf03e + df17692 commit 8e48dab

File tree

9 files changed

+117
-5
lines changed

9 files changed

+117
-5
lines changed

docs/src/guide/options.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,16 @@ These options can be used to define the initial checks to display the [`BoosterL
3939
````js
4040
{
4141
performance: true,
42-
browserSupport: true
42+
browserSupport: true,
43+
battery: true
4344
}
4445
````
4546

4647
| Key | Type | Required | Description | Default |
4748
| ---------------- | --------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------- |
4849
| `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` |
4950
| `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` |
51+
| `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` |
5052

5153
::: info
5254
For the browser support detection, the default [Browserslist](https://github.com/browserslist/browserslist) of the NuxtJS configuration is used.

playground/components/InfoLayer.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
<li id="nuxt-booster-message-unsupported-browser">outdated browser</li>
1010
<li id="nuxt-booster-message-reduced-bandwidth">reduced-bandwidth</li>
1111
<li id="nuxt-booster-message-weak-hardware">weak hardware</li>
12+
<li id="nuxt-booster-message-low-battery">low battery</li>
1213
</ul>
1314
<div class="info-layer-buttons">
1415
<base-button

src/media/video.mp4

1.46 KB
Binary file not shown.

src/module.js

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { optimizePreloads } from './utils/preload';
2424
import { getSupportedBrowserDetector } from './utils/browser';
2525
import { registerAppEntry as registerAppEntryWebpack } from './hookFunctions/webpack';
2626
import { registerAppEntry as registerAppEntryVite } from './hookFunctions/vite';
27+
import { getTemplate as getBlobFileTemplate } from './utils/blob';
2728

2829
import pluginTemplate from './tmpl/plugin.tmpl';
2930
import entryTemplate from './tmpl/entry.tmpl';
@@ -147,13 +148,24 @@ async function addBuildTemplates(nuxt, options) {
147148
entry: join(nuxt.options.appDir, 'entry'),
148149
runOptions: options.runOptions,
149150
ssr: nuxt.options.ssr,
150-
ignorePerformance: !options.detection.performance,
151+
ignore: {
152+
battery: !options.detection.battery,
153+
performance: !options.detection.performance
154+
},
151155
performanceMetrics: JSON.stringify(options.performanceMetrics || {}),
152156
supportedBrowserDetector
153157
});
154158
},
155159
write: true
156160
});
161+
162+
const files = [['video', resolver.resolve('media/video.mp4')]];
163+
const mediaContent = await getBlobFileTemplate(files);
164+
addTemplate({
165+
getContents: () => mediaContent,
166+
filename: MODULE_NAME + '/blobs.mjs',
167+
write: true
168+
});
157169
}
158170

159171
async function addModules(nuxt, moduleOptions) {

src/runtime/components/BoosterLayer.vue

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717
<li id="nuxt-booster-message-reduced-bandwidth">slow connection</li>
1818
<!-- Displayed when user hardware are not sufficient. -->
1919
<li id="nuxt-booster-message-weak-hardware">weak hardware</li>
20+
<!-- Displayed when the user batteries are not sufficient. -->
21+
<li id="nuxt-booster-message-low-battery">low battery</li>
2022
</ul>
2123

2224
<!-- Button to hide the layer with no javascript -->
@@ -96,5 +98,9 @@ useHead({
9698
display: none;
9799
}
98100
101+
#nuxt-speedkit-message-low-battery {
102+
display: none;
103+
}
104+
99105
/*! purgecss end ignore */
100106
</style>

src/runtime/utils/entry.js

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,44 @@ export function initReducedView() {
5555
tmp.remove();
5656
});
5757
}
58+
59+
export async function hasBatteryPerformanceIssue(videoBlob) {
60+
try {
61+
if (await isBatteryLow()) {
62+
throw new Error('Battery is low.');
63+
}
64+
} catch (error) {
65+
if (error.message === 'Battery is low.') {
66+
throw error;
67+
}
68+
await canVideoPlay(videoBlob);
69+
}
70+
}
71+
72+
/**
73+
* Checks if battery still has enough energy.
74+
* This check is for Chrome and all other browsers that support this setting.
75+
*
76+
* Condition is: The device is not charging and Battery is below <= 20%.
77+
* @see https://blog.google/products/chrome/new-chrome-features-to-save-battery-and-make-browsing-smoother/
78+
* @see https://developer.chrome.com/blog/memory-and-energy-saver-mode/
79+
**/
80+
async function isBatteryLow() {
81+
const MIN_BATTERY_LEVEL = 0.2;
82+
const battery = await window.navigator.getBattery();
83+
return !battery.charging && battery.level <= MIN_BATTERY_LEVEL;
84+
}
85+
86+
/**
87+
* Checking whether a video can be played.
88+
* This check is for IOS and checks if the power saving mode is enabled.
89+
*
90+
* In this case no video will be played automatically and play throws an error.
91+
*/
92+
export function canVideoPlay(blob) {
93+
const video = document.createElement('video');
94+
video.muted = true;
95+
video.playsinline = true;
96+
video.src = URL.createObjectURL(blob);
97+
return video.play();
98+
}

src/tmpl/entry.tmpl.js

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,10 @@ export default options => {
22
let code = `import { ${
33
options.performanceCheck ? `run, ` : ``
44
}hasSufficientPerformance, setup } from '#booster/utils/performance';
5-
import { triggerRunCallback, observeBoosterButton, setupBoosterLayer, updateBoosterLayerMessage, initReducedView } from '#booster/utils/entry';
5+
import { triggerRunCallback, observeBoosterButton, setupBoosterLayer, updateBoosterLayerMessage, initReducedView, hasBatteryPerformanceIssue } from '#booster/utils/entry';
66
import Deferred from '#booster/classes/Deferred';
77
import { isSupportedBrowser } from '#booster/utils/browser';
8+
import {video as videoBlob} from './blobs.mjs';
89
910
`;
1011

@@ -46,15 +47,38 @@ function client () {
4647
const forceInit = ('__NUXT_BOOSTER_FORCE_INIT__' in window && window.__NUXT_BOOSTER_FORCE_INIT__);
4748
4849
async function initApp(force) {
50+
4951
if (initialized) {
5052
deferred.resolve();
5153
}
5254
5355
document.documentElement.classList.remove('nuxt-booster-reduced-view');
5456
57+
`;
58+
59+
if (!options.ignore.battery) {
60+
code += `
5561
try {
62+
if (!force) {
63+
await hasBatteryPerformanceIssue(videoBlob)
64+
}
65+
} catch (error) {
66+
67+
console.warn(error)
68+
69+
triggerRunCallback(false);
70+
71+
if (!!layerEl) {
72+
// User must interact via the layer.
73+
updateSpeedkitLayerMessage(layerEl, 'nuxt-speedkit-message-low-battery');
74+
return null;
75+
}
76+
}
77+
`;
78+
}
5679

57-
`;
80+
code += `
81+
try {`;
5882

5983
if (options.performanceCheck) {
6084
code += `
@@ -75,6 +99,9 @@ if (!force) {
7599
deferred.resolve();
76100
77101
} catch (error) {
102+
103+
console.warn(error)
104+
78105
triggerRunCallback(false);
79106
80107
if (!!layerEl) {

src/utils/blob.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { promises as fsPromises } from 'fs';
2+
import mime from 'mime-types';
3+
4+
async function getFileInfo(name, file) {
5+
return {
6+
name,
7+
// eslint-disable-next-line security/detect-non-literal-fs-filename
8+
file: await fsPromises.readFile(file),
9+
mimeType: mime.lookup(file)
10+
};
11+
}
12+
13+
export async function getTemplate(files) {
14+
return (await Promise.all(files.map(file => getFileInfo(...file))))
15+
.map(
16+
({ name, file, mimeType }) =>
17+
`export const ${name} = new Blob([new Uint8Array([${[...file].join(
18+
', '
19+
)}])], {type: '${mimeType}'});`
20+
)
21+
.join('\n');
22+
}

src/utils/options.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,8 @@ export function getDefaultOptions() {
1313

1414
detection: {
1515
performance: true,
16-
browserSupport: true
16+
browserSupport: true,
17+
battery: true
1718
},
1819

1920
performanceMetrics: {

0 commit comments

Comments
 (0)