-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Move
use-notification
to monorepo (#120)
- Loading branch information
1 parent
f62df20
commit cde7145
Showing
11 changed files
with
447 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
'use-notification': patch | ||
--- | ||
|
||
Moved `use-notification` to monorepo |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
{ | ||
"presets": [ | ||
[ | ||
"@aacc/babel-preset", | ||
{ | ||
"typescript": true, | ||
"react": true | ||
} | ||
] | ||
] | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
/** | ||
* @type {import('eslint').Linter.Config} | ||
*/ | ||
module.exports = { | ||
root: true, | ||
extends: ['@aacc/eslint-config/typescript'], | ||
parserOptions: { | ||
tsconfigRootDir: __dirname, | ||
project: 'tsconfig.eslint.json', | ||
}, | ||
ignorePatterns: ['node_modules', 'dist'], | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,83 @@ | ||
# use-notification | ||
|
||
React hook wrapping the Web Notification API. | ||
|
||
## Usage | ||
|
||
```bash | ||
npm install use-notification | ||
``` | ||
|
||
```tsx | ||
import { useNotification } from 'use-notification' | ||
``` | ||
|
||
## useNotification ReturnType | ||
|
||
```tsx | ||
interface UseNotificationReturnType { | ||
/** | ||
* Represents the permission status for displaying web notifications on | ||
* the current origin. | ||
* | ||
* @example 'default' | 'denied' | 'granted' | ||
*/ | ||
permission: NotificationPermission | null | ||
/** | ||
* Any error thrown while initializing the Notification constructor. | ||
*/ | ||
error: Error | null | ||
/** | ||
* Requests permission to display web notifications. | ||
*/ | ||
requestPermission: () => Promise<void> | ||
/** | ||
* Triggers a web notification. | ||
* | ||
* Note: The function signature matches the web Notification constructor one | ||
* to one. See the MDN documentation for more information: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/Notification | ||
*/ | ||
notify: ( | ||
title: Notification['title'], | ||
options?: NotificationOptions, | ||
) => Notification | null | ||
} | ||
``` | ||
|
||
## Examples | ||
|
||
Note: The API is intentionally minimal and unopinionated to ensure the hook is | ||
flexible enough to handle multiple use cases. The following examples demonstrate | ||
some common configurations. | ||
|
||
### Example: Simple Notification | ||
|
||
[Demo](https://1pb5m.csb.app/) • | ||
[Code Sandbox](https://codesandbox.io/s/simple-notification-1pb5m) | ||
|
||
```tsx | ||
import { useNotification } from 'use-notification' | ||
|
||
function App() { | ||
const { permission, error, requestPermission, notify } = useNotification() | ||
|
||
if (error) { | ||
return ( | ||
<div> | ||
Notification Error: | ||
<pre>{error.message}</pre> | ||
</div> | ||
) | ||
} | ||
|
||
return ( | ||
<div> | ||
{permission === 'default' && ( | ||
<button onClick={requestPermission}>Enable Notifications</button> | ||
)} | ||
<button onClick={() => notify('Hi')}>Notify</button> | ||
</div> | ||
) | ||
} | ||
``` |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,63 @@ | ||
{ | ||
"name": "use-notification", | ||
"version": "0.2.2", | ||
"description": "", | ||
"author": "Aaron Casanova <aaronccasanova@gmail.com>", | ||
"license": "MIT", | ||
"main": "dist/cjs/index.js", | ||
"module": "dist/esm/index.mjs", | ||
"types": "dist/types/index.d.ts", | ||
"exports": { | ||
".": { | ||
"types": "./dist/types/index.d.ts", | ||
"import": "./dist/esm/index.mjs", | ||
"require": "./dist/cjs/index.js" | ||
} | ||
}, | ||
"scripts": { | ||
"dev": "npm-run-all --parallel 'build:* -- --watch'", | ||
"build": "npm-run-all --parallel build:*", | ||
"build:js": "rollup -c", | ||
"build:types": "tsc --emitDeclarationOnly", | ||
"type-check": "tsc --noEmit", | ||
"type-check:watch": "npm run type-check -- --watch", | ||
"lint": "TIMING=1 eslint . --ext .js,.ts --cache", | ||
"prepublishOnly": "npm run build" | ||
}, | ||
"files": [ | ||
"dist" | ||
], | ||
"peerDependencies": { | ||
"react": "^17.0.0" | ||
}, | ||
"dependencies": {}, | ||
"devDependencies": { | ||
"@aacc/babel-preset": "*", | ||
"@aacc/browserslist-config": "*", | ||
"@aacc/eslint-config": "*", | ||
"@aacc/tsconfigs": "*", | ||
"@rollup/plugin-babel": "^5.3.1", | ||
"@rollup/plugin-commonjs": "^21.1.0", | ||
"@rollup/plugin-node-resolve": "^13.2.1", | ||
"@types/react": "^17.0.0", | ||
"react": "^17.0.0", | ||
"rollup": "^2.70.2", | ||
"typescript": "^4.7.3" | ||
}, | ||
"browserslist": [ | ||
"extends @aacc/browserslist-config" | ||
], | ||
"publishConfig": { | ||
"access": "public", | ||
"@aacc:registry": "https://registry.npmjs.org" | ||
}, | ||
"repository": { | ||
"type": "git", | ||
"url": "git+https://github.com/aaronccasanova/aacc.git", | ||
"directory": "hooks/use-notification" | ||
}, | ||
"bugs": { | ||
"url": "https://github.com/aaronccasanova/aacc/issues" | ||
}, | ||
"homepage": "https://github.com/aaronccasanova/aacc/blob/main/hooks/use-notification/README.md" | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import path from 'path' | ||
|
||
import nodeResolve from '@rollup/plugin-node-resolve' | ||
import commonjs from '@rollup/plugin-commonjs' | ||
import babel from '@rollup/plugin-babel' | ||
|
||
import pkg from './package.json' | ||
|
||
const extensions = ['.js', '.jsx', '.ts', '.tsx'] | ||
|
||
/** | ||
* @type {import('rollup').RollupOptions} | ||
*/ | ||
export default { | ||
input: 'src/index.tsx', | ||
output: [ | ||
{ | ||
format: /** @type {const} */ ('cjs'), | ||
entryFileNames: '[name][assetExtname].js', | ||
dir: path.dirname(pkg.main), | ||
preserveModules: true, | ||
}, | ||
{ | ||
format: /** @type {const} */ ('es'), | ||
entryFileNames: '[name][assetExtname].mjs', | ||
dir: path.dirname(pkg.module), | ||
preserveModules: true, | ||
}, | ||
], | ||
plugins: [ | ||
// Allows node_modules resolution | ||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call | ||
nodeResolve({ extensions }), | ||
// Allow bundling cjs modules. Rollup doesn't understand cjs | ||
commonjs(), | ||
// Compile TypeScript/JavaScript files | ||
babel({ | ||
extensions, | ||
babelHelpers: 'bundled', | ||
include: ['src/**/*'], | ||
}), | ||
], | ||
external: [ | ||
...Object.keys(pkg.dependencies ?? {}), | ||
...Object.keys(pkg.peerDependencies ?? {}), | ||
], | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,143 @@ | ||
/** | ||
* Notification API Docs: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/Notification | ||
*/ | ||
import React from 'react' | ||
|
||
import { isSupported, useIsomorphicLayoutEffect } from './utils' | ||
|
||
interface NotificationState { | ||
permission: NotificationPermission | null | ||
error: Error | null | ||
} | ||
|
||
type NotificationAction = Partial<NotificationState> | ||
|
||
type NotificationReducer = ( | ||
prevState: NotificationState, | ||
action: NotificationAction, | ||
) => NotificationState | ||
|
||
/** Prevents updating state on an unmounted component. */ | ||
function useSafeDispatch( | ||
dispatch: React.Dispatch<React.ReducerAction<NotificationReducer>>, | ||
) { | ||
const mounted = React.useRef(false) | ||
|
||
useIsomorphicLayoutEffect(() => { | ||
mounted.current = true | ||
|
||
return () => { | ||
mounted.current = false | ||
} | ||
}, []) | ||
|
||
return React.useCallback( | ||
(action: NotificationAction) => { | ||
if (mounted.current) dispatch(action) | ||
}, | ||
[dispatch], | ||
) | ||
} | ||
|
||
interface UseNotificationReturnType { | ||
/** | ||
* Represents the permission status for displaying web notifications on | ||
* the current origin. | ||
* | ||
* @example 'default' | 'denied' | 'granted' | ||
*/ | ||
permission: NotificationPermission | null | ||
/** | ||
* Any error thrown while initializing the Notification constructor. | ||
*/ | ||
error: Error | null | ||
/** | ||
* Requests permission to display web notifications. | ||
*/ | ||
requestPermission: () => Promise<void> | ||
/** | ||
* Triggers a web notification. | ||
* | ||
* Note: The function signature matches the web Notification constructor one | ||
* to one. See the MDN documentation for more information: | ||
* https://developer.mozilla.org/en-US/docs/Web/API/Notification | ||
*/ | ||
notify: ( | ||
title: Notification['title'], | ||
options?: NotificationOptions, | ||
) => Notification | null | ||
} | ||
|
||
export function useNotification(): UseNotificationReturnType { | ||
const supported = isSupported() | ||
|
||
const [{ permission, error }, setState] = | ||
React.useReducer<NotificationReducer>( | ||
(prevState, action) => ({ ...prevState, ...action }), | ||
{ | ||
permission: supported ? Notification.permission : null, | ||
error: !supported | ||
? new Error('This browser does not support web notifications.') | ||
: null, | ||
}, | ||
) | ||
|
||
const safeSetState = useSafeDispatch(setState) | ||
|
||
const requestPermission = React.useCallback(async () => { | ||
try { | ||
if (!supported || permission !== 'default') return | ||
|
||
const notificationPermission = await Notification.requestPermission() | ||
|
||
// Update permission status and clear out any errors. | ||
safeSetState({ | ||
permission: notificationPermission, | ||
error: null, | ||
}) | ||
} catch { | ||
// Fallback to the deprecated callback API. | ||
Notification.requestPermission((notificationPermission) => { | ||
// Update permission status and clear out any errors. | ||
safeSetState({ | ||
permission: notificationPermission, | ||
error: null, | ||
}) | ||
}).catch((deprecatedCallbackError) => { | ||
if (deprecatedCallbackError instanceof Error) { | ||
safeSetState({ error: deprecatedCallbackError }) | ||
} | ||
}) | ||
} | ||
}, [permission, supported]) | ||
|
||
const notify = React.useCallback( | ||
(title: Notification['title'], options?: NotificationOptions) => { | ||
if (!supported || permission !== 'granted') return null | ||
|
||
try { | ||
const notification = new Notification(title, options) | ||
|
||
// Clear out possible errors in state. | ||
safeSetState({ error: null }) | ||
|
||
return notification | ||
} catch (notificationError) { | ||
if (notificationError instanceof Error) { | ||
safeSetState({ error: notificationError }) | ||
} | ||
|
||
return null | ||
} | ||
}, | ||
[permission, supported], | ||
) | ||
|
||
return { | ||
permission, | ||
error, | ||
requestPermission, | ||
notify, | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
import React from 'react' | ||
|
||
export function isServer() { | ||
const isDOM = | ||
typeof window !== 'undefined' && | ||
window.document && | ||
window.document.documentElement | ||
|
||
return !isDOM | ||
} | ||
|
||
/** Checks if Web Notification API supported. */ | ||
export function isSupported() { | ||
return !isServer() && 'Notification' in window | ||
} | ||
|
||
export const useIsomorphicLayoutEffect = isServer() | ||
? React.useEffect | ||
: React.useLayoutEffect |
Oops, something went wrong.
cde7145
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
aacc-next-ts-styled-comps – ./recipes/next-ts-styled-comps
aacc-next-ts-styled-comps.vercel.app
aacc-next-ts-styled-comps-aaronccasanova.vercel.app
aacc-next-ts-styled-comps-git-main-aaronccasanova.vercel.app
cde7145
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
aacc-scales – ./apps/scales
aacc-scales-aaronccasanova.vercel.app
aacc-scales.vercel.app
aacc-scales-git-main-aaronccasanova.vercel.app
cde7145
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Successfully deployed to the following URLs:
aacc-next-ts – ./recipes/next-ts
aacc-next-ts.vercel.app
aacc-next-ts-aaronccasanova.vercel.app
aacc-next-ts-git-main-aaronccasanova.vercel.app