Skip to content

Commit 77f91be

Browse files
committed
[IMPL] useScreenWakeLock hook
1 parent 1aaa612 commit 77f91be

8 files changed

Lines changed: 161 additions & 8 deletions

File tree

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { useScreenWakeLock } from "../../../../../../packages/react-tools/src"
2+
3+
/**
4+
The component uses _useScreenWakeLock_ hook to detect if WakeLock API is available and renders informations about it. It has a button also, to acquire and release a WakeLock.
5+
*/
6+
export const UseScreenWakeLock = () => {
7+
const [info, acquire, release] = useScreenWakeLock();
8+
9+
return <>
10+
<p>WakeLock API supported: {info.isSupported ? "Yes" : "No"}</p>
11+
<p>WakeLock type: {info.type ? info.type : "WakeLock not found."}</p>
12+
<p>WakeLock active: {info.isActive ? "Yes" : "No"}</p>
13+
<button
14+
type="button"
15+
onClick={() => {
16+
info.isActive ? release() : acquire();
17+
}}
18+
disabled={!info.isSupported}
19+
>
20+
{info.isActive ? "UnLock": "Lock"}
21+
</button>
22+
</>
23+
}

apps/react-tools-demo/src/constants/components.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,8 @@ export const COMPONENTS = [
9696
"useDeviceMotion",
9797
"useDeviceOrientation",
9898
"useVibrate",
99-
"useBluetooth"
99+
"useBluetooth",
100+
"useScreenWakeLock"
100101
]
101102
],
102103
//UTILS

apps/react-tools-demo/src/markdown/useDerivedState.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,7 @@ const WithUseDerivedState = memo(({ user }: { user: string }) => {
109109
```
110110

111111
> The component has _three internal string states_ and renders three input fields and three components that receive one state each. These three components have an object as internal state with two properties _loading_, initially set to __true__, and _friends_ which is an initially empty array.
112-
> Based on the _user_ prop they receive, they set the _loading_ property of the internal state to __true__ and invoke a _serverAPI_ function that simulates a backend call and returns a list of names filtered by the passed _prop_. This list values ​​the _friends_ property of the internal state and this list together with the passed _user_ prop are rendered:
112+
> Based on the _user_ prop they receive, they set the _loading_ property of the internal state to __true__ and invoke a _serverAPI_ function that simulates a backend call and returns a list of names filtered by the passed _prop_. This list values the _friends_ property of the internal state and this list together with the passed _user_ prop are rendered:
113113
> - The _Without useDerivedState_ component uses the _useState_ and _useEffect_ hooks to implement this logic.
114114
> - The _With useDerivedState_ component uses the _useDerivedState_ hook and the _useEffect_.
115115
> - The _With useDerivedStateAndCompute_ component uses the _useDerivedState_ hook and the optional third parameter to implement all logic.
@@ -118,7 +118,7 @@ const WithUseDerivedState = memo(({ user }: { user: string }) => {
118118
>
119119
> The component without _useDerivedState_ hook is rendered one more time every time its _prop_ changes while the other two have the same number of renders.
120120
>
121-
> Furthermore, if you debug the code you can see how in the first component there is no synchronization in the updating of the values ​​since in a first render the rendered _prop_ user is updated and in a second render the writing `loading` is rendered instead of the list of names.
121+
> Furthermore, if you debug the code you can see how in the first component there is no synchronization in the updating of the values since in a first render the rendered _prop_ user is updated and in a second render the writing `loading` is rendered instead of the list of names.
122122
123123

124124
## API
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# useScreenWakeLock
2+
Hook to use [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API).
3+
4+
## Usage
5+
6+
```tsx
7+
export const UseScreenWakeLock = () => {
8+
const [info, acquire, release] = useScreenWakeLock();
9+
10+
return <>
11+
<p>WakeLock API supported: {info.isSupported ? "Yes" : "No"}</p>
12+
<p>WakeLock type: {info.type ? info.type : "WakeLock not found."}</p>
13+
<p>WakeLock active: {info.isActive ? "Yes" : "No"}</p>
14+
<button
15+
type="button"
16+
onClick={() => {
17+
info.isActive ? release() : acquire();
18+
}}
19+
disabled={!info.isSupported}
20+
>
21+
{info.isActive ? "UnLock": "Lock"}
22+
</button>
23+
</>
24+
}
25+
```
26+
27+
> The component uses _useScreenWakeLock_ hook to detect if WakeLock API is available and renders informations about it. It has a button also, to acquire and release a WakeLock.
28+
29+
30+
## API
31+
32+
```tsx
33+
useScreenWakeLock(onRelease?: (evt?: Event) =>void): [{isSupported: boolean, type: "screen"|null, isActive: boolean|null}, ()=>Promise<void>, ()=>Promise<void>]
34+
```
35+
36+
> ### Params
37+
>
38+
> - __onRelease?__: _(evt?:Event)=>void_
39+
function that will be executed on release event.
40+
>
41+
42+
> ### Returns
43+
>
44+
> __result__: __Array__:
45+
- __Object__:
46+
- __isSupported__ : _boolean_
47+
- __type__ : _"screen"|null_
48+
- __isActive__ : _boolean|null_
49+
- _()=>Promise<void>_
50+
- _()=>Promise<void>_
51+
> - 1. __info__: object with these properties:
52+
> - _isSupported_: returns a boolean to know if API is available.
53+
> - _type_: return a string representation of the currently acquired WakeLock type.
54+
> - _isActive_: returns a boolean indicating whether the WakeLockSentinel has been activated.
55+
> - 2. __acquire__: function to request a WakeLock.
56+
> - 3. __release__: function to release a WakeLock.
57+
>

packages/react-tools/README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
- [x] useSyncExternalStore
1717
- [x] useDerivedState
1818
- [ ] useStateValidator (???)
19+
- [ ] useSignal (https://medium.com/@personal.david.kohen/the-quest-for-signals-in-react-usestate-on-steroids-71eb9fc87c14)
1920
- [ ] useStore
2021
- [ ] createStore (example: https://github.com/streamich/react-use/blob/master/src/factory/createGlobalState.ts)
2122
- [ ] usePubSubStore (with pusSub model)
@@ -97,9 +98,7 @@
9798
- [x] useDeviceOrientation
9899
- [x] useVibrate (??? mobile)
99100
- [x] useBluetooth
100-
- [ ] useFileSystem (https://vueuse.org/core/useFileSystemAccess/).
101-
- [ ] useGamePad (https://vueuse.org/core/useGamepad/)
102-
- [ ] useWakeLock (https://vueuse.org/core/useWakeLock/)
101+
- [x] useScreenWakeLock
103102
- [ ] useSpeech (https://developer.mozilla.org/en-US/docs/Web/API/SpeechSynthesisUtterance#examples)
104103
- [ ] useDevicesList (https://vueuse.org/core/useDevicesList/)
105104
- [ ] useFPS (https://vueuse.org/core/useFps/)

packages/react-tools/src/hooks/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,4 +76,5 @@ export { useDeviceMotion } from './useDeviceMotion';
7676
export { useDeviceOrientation } from './useDeviceOrientation';
7777
export { useVibrate } from './useVibrate';
7878
export { useDerivedState } from './useDerivedState';
79-
export { useBluetooth } from './useBluetooth';
79+
export { useBluetooth } from './useBluetooth';
80+
export { useScreenWakeLock } from './useScreenWakeLock';
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
import { useCallback, useMemo, useRef } from "react"
2+
import { useSyncExternalStore } from ".";
3+
4+
/**
5+
* **`useScreenWakeLock`**: Hook to use [Screen Wake Lock API](https://developer.mozilla.org/en-US/docs/Web/API/Screen_Wake_Lock_API).
6+
* @param {(evt?:Event)=>void} [onRelease] - function that will be executed on release event.
7+
* @returns {[{isSupported: boolean, type: "screen"|null, isActive: boolean|null}, ()=>Promise<void>, ()=>Promise<void>]} result - An array with three element:
8+
* - 1. __info__: object with these properties:
9+
* - _isSupported_: returns a boolean to know if API is available.
10+
* - _type_: return a string representation of the currently acquired WakeLock type.
11+
* - _isActive_: returns a boolean indicating whether the WakeLockSentinel has been activated.
12+
* - 2. __acquire__: function to request a WakeLock.
13+
* - 3. __release__: function to release a WakeLock.
14+
*/
15+
export const useScreenWakeLock = (onRelease?: (evt?: Event) =>void): [{isSupported: boolean, type: "screen"|null, isActive: boolean|null}, ()=>Promise<void>, ()=>Promise<void>] => {
16+
const notifyRef = useRef<() => void>();
17+
const internalWakeLock = useRef<WakeLockSentinel>();
18+
const listener = useRef<(ev: Event)=>any>((evt: Event) => {
19+
onRelease!(evt);
20+
notifyRef.current && notifyRef.current();
21+
})
22+
const acquire = useRef(async () => {
23+
if (!!navigator && "wakeLock" in navigator) {
24+
internalWakeLock.current = await navigator.wakeLock.request("screen");
25+
onRelease && internalWakeLock.current.addEventListener("release", listener.current);
26+
notifyRef.current && notifyRef.current();
27+
}
28+
});
29+
const release = useRef(async () => {
30+
if (internalWakeLock.current) {
31+
await internalWakeLock.current.release();
32+
internalWakeLock.current.removeEventListener("release", listener.current);
33+
internalWakeLock.current = undefined;
34+
notifyRef.current && notifyRef.current();
35+
}
36+
});
37+
38+
const value = useSyncExternalStore(
39+
useCallback(notif => {
40+
notifyRef.current = notif;
41+
return () => {
42+
notifyRef.current = undefined;
43+
}
44+
}, []),
45+
useMemo(() => {
46+
let info: {isSupported: boolean, type: "screen" | null, isActive: boolean | null} = {
47+
isSupported: !!navigator && "wakeLock" in navigator,
48+
type: null,
49+
isActive: null,
50+
};
51+
return () => {
52+
const isSupported = !!navigator && "wakeLock" in navigator;
53+
let type=null, isActive=null;
54+
if (internalWakeLock.current) {
55+
type = internalWakeLock.current.type;
56+
isActive = !internalWakeLock.current.released;
57+
}
58+
if (isSupported !== info.isSupported || type !== info.type || isActive !== info.isActive) {
59+
info = {
60+
isSupported,
61+
type,
62+
isActive: isActive
63+
};
64+
}
65+
return info;
66+
}
67+
}, [])
68+
);
69+
70+
return [value, acquire.current, release.current];
71+
}

packages/react-tools/src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,7 +109,8 @@ export {
109109
useDeviceOrientation,
110110
useVibrate,
111111
useDerivedState,
112-
useBluetooth
112+
useBluetooth,
113+
useScreenWakeLock
113114
} from './hooks'
114115

115116
export {

0 commit comments

Comments
 (0)