Last active
July 3, 2023 11:40
-
-
Save honungsburk/2ec136130d432698adb9a63f0e4e3117 to your computer and use it in GitHub Desktop.
Hook to query browser permissions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
// Some persmissions are not supported by all browsers, microphones are | |
// not supported by Safari for example. | |
export type PermissionNamePlus = PermissionName | 'microphone'; | |
export class PermissionError extends Error {} | |
// Cache the permissions so we don't have to show a loading state | |
// every time we check a permission. | |
type Permissions = Record<PermissionNamePlus, PermissionState | null>; | |
const permissionsCache: Permissions = { | |
microphone: null, | |
geolocation: null, | |
notifications: null, | |
'persistent-storage': null, | |
push: null, | |
'screen-wake-lock': null, | |
'xr-spatial-tracking': null, | |
}; | |
/** | |
* Note: Some permissions are not supported by all browsers, microphones for example. | |
* If the permission is not supported, the hook will return null. | |
* | |
* @param name - the name of the permission to check | |
* @returns | |
*/ | |
export default function usePermission( | |
name: PermissionNamePlus, | |
): [PermissionState | null, boolean, PermissionError | undefined] { | |
const [permissions, setPermissions] = React.useState<PermissionState | null>( | |
permissionsCache[name], | |
); | |
const [isLoading, setIsLoading] = React.useState<boolean>( | |
permissionsCache[name] === null, | |
); | |
const [error, setError] = React.useState<PermissionError | undefined>(); | |
React.useEffect(() => { | |
const set = (state: PermissionState | null) => { | |
setPermissions(state); | |
permissionsCache[name] = state; | |
}; | |
const onChange = function (this: PermissionStatus) { | |
set(this.state); | |
}; | |
let cleanup = () => {}; | |
navigator.permissions | |
.query({ name: name as PermissionName }) | |
.then((permission) => { | |
onChange.call(permission); | |
permission.onchange = onChange; | |
}) | |
.catch((err) => { | |
set(null); | |
setError(new PermissionError(err.message, { cause: err })); | |
}) | |
.finally(() => { | |
setIsLoading(false); | |
}); | |
return () => { | |
// We must wrap in a lambda so that the correct cleanup function is called | |
// when the component unmounts | |
cleanup(); | |
}; | |
}, []); | |
return [permissions, isLoading, error]; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment