Skip to content

Commit

Permalink
feat: new hook usePermission
Browse files Browse the repository at this point in the history
  • Loading branch information
xobotyi committed Jun 21, 2021
1 parent 681d972 commit 8069faa
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 20 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ import { useMountEffect } from "@react-hookz/web/esnext";

- [**`useNetworkState`**](https://react-hookz.github.io/web/?path=/docs/navigator-usenetwork)
— Tracks the state of browser's network connection.
- [**`usePermission`**](https://react-hookz.github.io/web/?path=/docs/navigator-usepermission)
— Tracks a permission state.

- #### Miscellaneous

Expand Down
22 changes: 19 additions & 3 deletions src/usePermission/__docs__/example.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,29 @@ import * as React from 'react';
import { usePermission } from '../..';

export const Example: React.FC = () => {
const [status, requestPermission] = usePermission({ name: 'geolocation' }, false);
const status = usePermission({ name: 'notifications' });

return (
<div>
<div>{status}</div>
<div>
<button onClick={requestPermission}>Request geolocation permission</button>
<em>
We do not use any notifications, notifications permission requested only for presentation
purposes.
</em>
</div>
<br />
<div>
Notifications status: <code>{status}</code>
</div>
<div>
{status === 'prompt' && (
<button
onClick={() => {
Notification.requestPermission();
}}>
Request notifications permission
</button>
)}
</div>
</div>
);
Expand Down
17 changes: 15 additions & 2 deletions src/usePermission/__docs__/story.mdx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks';
import { Example } from './example.stories';

<Meta title="New Hook/usePermission" component={Example} />
<Meta title="Navigator/usePermission" component={Example} />

# usePermission

Tracks a permission state.

- SSR-compatible
- Automatically updates on permission state change.

#### Example

<Canvas>
Expand All @@ -14,9 +19,17 @@ import { Example } from './example.stories';
## Reference

```ts

export function usePermission(descriptor: IAnyPermissionDescriptor): IUsePermissionState;
```

#### Arguments

#### Return

0. **state** - Permission state, can be one of the following strings:
- **`not-requested`** - State not requested yet, yielded only on mount, before permission
state requested.
- **`requested`** - State requested, but request promise is pending yet.
- **`prompt`** - Permission not requested from user, and can be requested via target API.
- **`denied`** - User denied permission.
- **`granted`** - User granted permission.
33 changes: 18 additions & 15 deletions src/usePermission/usePermission.ts
Original file line number Diff line number Diff line change
@@ -1,23 +1,23 @@
import { useCallback, useMemo, useRef } from 'react';
import { useCallback, useEffect, useMemo, useRef } from 'react';
import { useIsMounted } from '../useIsMounted/useIsMounted';
import { isBrowser, noop } from '../util/const';
import { useSafeState } from '../useSafeState/useSafeState';
import { off, on } from '../util/misc';
import { useUnmountEffect } from '../useUnmountEffect/useUnmountEffect';
import { useMountEffect } from '../useMountEffect/useMountEffect';

export type IAnyPermissionDescriptor =
| PermissionDescriptor
| DevicePermissionDescriptor
| MidiPermissionDescriptor
| PushPermissionDescriptor;

export type IUsePermissionState = PermissionState | 'not-requested';
export type IUsePermissionState = PermissionState | 'not-requested' | 'requested';

export function usePermission(
descriptor: IAnyPermissionDescriptor,
autoRequest = true
): [state: IUsePermissionState, requestState: () => void] {
/**
* Tracks a permission state.
*
* @param descriptor Permission request descriptor that passed to `navigator.permissions.query`
*/
export function usePermission(descriptor: IAnyPermissionDescriptor): IUsePermissionState {
const isMounted = useIsMounted();
const [state, setState] = useSafeState<IUsePermissionState>('not-requested');
const permissionStatus = useRef<PermissionStatus>();
Expand Down Expand Up @@ -47,17 +47,20 @@ export function usePermission(
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

const requestState = useCallback(() => {
// eslint-disable-next-line consistent-return
const requestState = useCallback(async (): Promise<PermissionStatus | void> => {
if (isBrowser) {
navigator.permissions.query(descriptor).then(processStatus).catch(noop);
setState('requested');
return navigator.permissions.query(descriptor).then(processStatus).catch(noop);
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [descriptor]);

useMountEffect(() => {
if (autoRequest) requestState();
});
useUnmountEffect(processStatus);
useEffect(() => {
requestState();

return [state, requestState];
return processStatus;
}, []);

return state;
}

0 comments on commit 8069faa

Please sign in to comment.