From 3b971a2297ef5bcc17f9dd6c8b3a65bf6e308ebe Mon Sep 17 00:00:00 2001 From: streamich Date: Sun, 28 Oct 2018 11:26:23 +0100 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20useSpeech=20hook?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 1 + docs/useSpeech.md | 18 +++++++++++++ src/__stories__/useSpeech.story.tsx | 16 ++++++++++++ src/index.ts | 2 ++ src/react.ts | 3 +++ src/useLifecycles.ts | 2 +- src/useMount.ts | 6 +---- src/useSpeech.ts | 40 +++++++++++++++++++++++++++++ 8 files changed, 82 insertions(+), 6 deletions(-) create mode 100644 docs/useSpeech.md create mode 100644 src/__stories__/useSpeech.story.tsx create mode 100644 src/useSpeech.ts diff --git a/README.md b/README.md index 3d54b8d1..6bbddad0 100644 --- a/README.md +++ b/README.md @@ -47,6 +47,7 @@
- [__UI__](./docs/UI.md) - [`useAudio`](./docs/useAudio.md) — plays audio and exposes its controls. + - [`useSpeech`](./docs/useSpeech.md) — synthesizes speech from text string.

- [__Animations__](./docs/Animations.md) diff --git a/docs/useSpeech.md b/docs/useSpeech.md new file mode 100644 index 00000000..4af79c63 --- /dev/null +++ b/docs/useSpeech.md @@ -0,0 +1,18 @@ +# `useSpeech` + +React UI hook that synthesizes human voice that speaks a given string. + + +## Usage + +```jsx +import {useSpeech} from 'react-use'; + +const Demo = () => { + const state = useSpeech('Hello world!'); + + return ( +
{JSON.stringify(state, null, 2)}
+ ); +}; +``` diff --git a/src/__stories__/useSpeech.story.tsx b/src/__stories__/useSpeech.story.tsx new file mode 100644 index 00000000..7433cfad --- /dev/null +++ b/src/__stories__/useSpeech.story.tsx @@ -0,0 +1,16 @@ +import {storiesOf} from '@storybook/react'; +import * as React from 'react'; +import {useSpeech} from '..'; + +const Demo = () => { + const state = useSpeech('Hello world!'); + + return ( +
{JSON.stringify(state, null, 2)}
+ ); +}; + +storiesOf('useSpeech', module) + .add('Example', () => + + ) diff --git a/src/index.ts b/src/index.ts index cd2a00b0..e9cf92a5 100644 --- a/src/index.ts +++ b/src/index.ts @@ -22,6 +22,7 @@ import useOrientation from './useOrientation'; import useRaf from './useRaf'; import useSetState from './useSetState'; import useSize from './useSize'; +import useSpeech from './useSpeech'; import useSpring from './useSpring'; import useTimeout from './useTimeout'; import useTitle from './useTitle'; @@ -55,6 +56,7 @@ export { useRaf, useSetState, useSize, + useSpeech, useSpring, useTimeout, useTitle, diff --git a/src/react.ts b/src/react.ts index 69defde9..de817490 100644 --- a/src/react.ts +++ b/src/react.ts @@ -12,3 +12,6 @@ export const useRef: UseRef = (React as any).useRef; export type UseCallback = any)>(callback: T, args: any[]) => T; export const useCallback: UseCallback = (React as any).useCallback; + +export type UseMemo = (fn: Function, args: any[]) => T; +export const useMemo: UseMemo = (React as any).useMemo; diff --git a/src/useLifecycles.ts b/src/useLifecycles.ts index b5c7e69b..841ccd47 100644 --- a/src/useLifecycles.ts +++ b/src/useLifecycles.ts @@ -1,6 +1,6 @@ import {useEffect} from './react'; -const useLifecycles = (mount, unmount) => { +const useLifecycles = (mount, unmount?) => { useEffect(() => { if (mount) mount(); return () => { diff --git a/src/useMount.ts b/src/useMount.ts index 24e72fe5..7ff2edc4 100644 --- a/src/useMount.ts +++ b/src/useMount.ts @@ -1,9 +1,5 @@ import {useEffect} from './react'; -const useMount = (mount) => { - useEffect(() => { - if (mount) mount(); - }, []); -}; +const useMount = (mount) => useEffect(mount, []); export default useMount; diff --git a/src/useSpeech.ts b/src/useSpeech.ts new file mode 100644 index 00000000..a7c8a7c3 --- /dev/null +++ b/src/useSpeech.ts @@ -0,0 +1,40 @@ +import {useRef} from './react'; +import useSetState from './useSetState'; +import useMount from './useMount'; + +export interface SpeechState { + isPlaying: boolean; + volume: number; +} + +export interface SpeechOptions { + lang?: any; + pitch?: number; + rate?: number; + voice?: any; + volume?: number; +} + +const useSpeech = (text: string, opts: SpeechOptions = {}): SpeechState => { + const [state, setState] = useSetState({ + isPlaying: false, + volume: opts.volume || 1, + }); + + const uterranceRef = useRef(null); + + useMount(() => { + const utterance = new SpeechSynthesisUtterance(text); + utterance.volume = opts.volume || 1; + utterance.onstart = () => setState({isPlaying: true}); + utterance.onresume = () => setState({isPlaying: true}); + utterance.onend = () => setState({isPlaying: false}); + utterance.onpause = () => setState({isPlaying: false}); + uterranceRef.current = utterance; + window.speechSynthesis.speak(uterranceRef.current); + }); + + return state; +}; + +export default useSpeech;