From 417ce19143b021e07d32740e5eca5a629a3d82be Mon Sep 17 00:00:00 2001 From: William Wong Date: Wed, 30 Oct 2019 00:13:16 -0700 Subject: [PATCH] Stop dictate if microphone is not available --- CHANGELOG.md | 1 + packages/component/src/Dictation.js | 42 +++++++++++++++++++++++++++-- 2 files changed, 41 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6d1f0f198b..3c156d7786 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. - Fixes [#2473](https://github.com/microsoft/BotFramework-WebChat/issues/2473). Fix samples 13 using wrong region for Speech Services credentials, by [@compulim](https://github.com/compulim) in PR [#2482](https://github.com/microsoft/BotFramework-WebChat/pull/2482) - Fixes [#2420](https://github.com/microsoft/BotFramework-WebChat/issues/2420). Fix saga error should not result in an unhandled exception, by [@compulim](https://github.com/compulim) in PR [#2421](https://github.com/microsoft/BotFramework-WebChat/pull/2421) - Fixes [#2513](https://github.com/microsoft/BotFramework-WebChat/issues/2513). Fix `core-js` not loading properly, by [@compulim](https://github.com/compulim) in PR [#2514](https://github.com/microsoft/BotFramework-WebChat/pull/2514) +- Fixes [#2516](https://github.com/microsoft/BotFramework-WebChat/issues/2516). Disable microphone input for `expecting` input hint on Safari, by [@compulim](https://github.com/compulim) in PR [#2517](https://github.com/microsoft/BotFramework-WebChat/pull/2517) ### Added diff --git a/packages/component/src/Dictation.js b/packages/component/src/Dictation.js index ac0dda946b..32ba29c46a 100644 --- a/packages/component/src/Dictation.js +++ b/packages/component/src/Dictation.js @@ -1,7 +1,7 @@ import { Composer as DictateComposer } from 'react-dictate-button'; import { Constants } from 'botframework-webchat-core'; import PropTypes from 'prop-types'; -import React, { useCallback } from 'react'; +import React, { useCallback, useMemo } from 'react'; import connectToWebChat from './connectToWebChat'; @@ -9,6 +9,26 @@ const { DictateState: { DICTATING, IDLE, STARTING } } = Constants; +const PrefixedAudioContext = window.AudioContext || window.webkitAudioContext; + +// The result of this check is asynchronous and it will fail on user interaction requirement. +async function canOpenMicrophone() { + const audioContext = new PrefixedAudioContext(); + + try { + if (audioContext.state === 'suspended') { + return await Promise.race([ + audioContext.resume().then(() => true), + new Promise(resolve => setImmediate(resolve)).then(() => false) + ]); + } + + return true; + } finally { + await audioContext.close(); + } +} + const Dictation = ({ dictateState, disabled, @@ -62,6 +82,24 @@ const Dictation = ({ onError && onError(event); }, [dictateState, onError, setDictateState, stopDictate]); + const shouldStart = !disabled && (dictateState === STARTING || dictateState === DICTATING) && !numSpeakingActivities; + + // We need to check if the browser allow us to do open microphone. + // In Safari, it block microphone access if the code was not executed based on user interaction. + + // Since the check call is asynchronous, the result will always fail the user interaction requirement. + // Thus, we can never open microphone after we receive the check result. + // Instead, we will both open microphone and check the result. If the result is negative, we will close the microphone. + + // TODO: [P3] Investigate if a resumed AudioContext instance is kept across multiple session, can we workaround Safari's restrictions. + useMemo(async () => { + if (shouldStart) { + const canStart = await canOpenMicrophone(); + + !canStart && stopDictate(); + } + }, [shouldStart, stopDictate]); + return ( ); };