Skip to content

Instantly share code, notes, and snippets.

@honungsburk
Last active August 2, 2022 13:15
Show Gist options
  • Save honungsburk/f5897178af325141c00eef1216bb402f to your computer and use it in GitHub Desktop.
Save honungsburk/f5897178af325141c00eef1216bb402f to your computer and use it in GitHub Desktop.
import { useEffect, useState } from "react";
////////////////////////////////////////////////////////////////////////////////
// Permissions
////////////////////////////////////////////////////////////////////////////////
/**
* microphone and camera doesn't exist on all browsers!
*/
type AllPermissionName = "microphone" | "camera" | PermissionName;
type Permissions<Names extends AllPermissionName[]> = {
[key in Names[number]]: PermissionState;
};
function initPermissions<Names extends AllPermissionName[]>(
names: Names
): Permissions<Names> {
const obj: any = {};
for (let name of names) {
obj[name] = "prompt";
}
return obj as Permissions<Names>;
}
/**
* Keep track of all granted browser permissions. Automatically updates.
*
* NOTE: the "microphone" and "camera" permissions are not supported by all browsers!
*/
export default function useBrowserPermissions<
Names extends AllPermissionName[]
>(...permissionNames: Names): Permissions<Names> {
const [permissions, setPermissions] = useState<Permissions<Names>>(
initPermissions(permissionNames)
);
useEffect(() => {
const unsubs: (() => void)[] = [];
const exec = async () => {
let localPermissions: any = initPermissions(permissionNames);
for (let permissionName of permissionNames) {
const permissionStatus = await navigator.permissions.query({
name: permissionName as PermissionName,
});
localPermissions[permissionName] = permissionStatus.state;
setPermissions({ ...localPermissions });
const listener: (this: PermissionStatus) => void = function () {
console.log(permissionName, ":", this.state);
if (localPermissions[permissionName] !== this.state) {
localPermissions[permissionName] = this.state;
setPermissions({ ...localPermissions });
}
};
permissionStatus.addEventListener("change", listener);
unsubs.push(() =>
permissionStatus.removeEventListener("change", listener)
);
}
};
exec();
return () => {
unsubs.forEach((fn) => fn());
};
}, permissionNames);
// IMPORTANT: do not wrapp permissionNames in a list [permissionNames] that will trigger
// effect on each render!
// This happens because the list is a new refrence each time, but by not wrapping it will look at the values
// which are strings and are refer to the same ref each time!
return permissions;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment