@@ -65,6 +65,7 @@ const SearchContent = () => {
6565 const [ audioStream , setAudioStream ] = useState ( null ) ;
6666 const [ isTranscriptLoading , setIsTranscriptLoading ] = useState ( false ) ;
6767 const [ searchPending , setSearchPending ] = useState ( true ) ;
68+ const [ microphonePermissionStatus , setMicrophonePermissionStatus ] = useState ( 'unknown' ) ;
6869
6970 const sourcesObj = banidb . SOURCE_TEXTS ;
7071 const writersObj = banidb . WRITER_TEXTS ;
@@ -199,6 +200,17 @@ const SearchContent = () => {
199200 } ) ;
200201 } , [ ] ) ;
201202
203+ const checkMicrophonePermission = async ( ) => {
204+ try {
205+ ipcRenderer . send ( 'get-media-access-status' , 'microphone' ) ;
206+ const permission = await navigator . permissions . query ( { name : 'microphone' } ) ;
207+ return permission . state ;
208+ } catch ( error ) {
209+ console . error ( 'Error checking microphone permission:' , error ) ;
210+ return 'unknown' ;
211+ }
212+ } ;
213+
202214 useEffect ( ( ) => {
203215 const checkOnlineStatus = async ( ) => {
204216 try {
@@ -210,8 +222,20 @@ const SearchContent = () => {
210222 } ;
211223
212224 checkOnlineStatus ( ) ;
225+ checkMicrophonePermission ( ) ;
213226 } , [ ] ) ;
214227
228+ const requestMicrophonePermission = ( ) =>
229+ new Promise ( ( resolve ) => {
230+ const handlePermissionResponse = ( event , status ) => {
231+ ipcRenderer . removeListener ( 'media-access-status' , handlePermissionResponse ) ;
232+ resolve ( status ) ;
233+ } ;
234+
235+ ipcRenderer . on ( 'media-access-status' , handlePermissionResponse ) ;
236+ ipcRenderer . send ( 'get-media-access-status' , 'microphone' ) ;
237+ } ) ;
238+
215239 const handleMicClick = async ( ) => {
216240 if ( isRecording ) {
217241 if ( mediaRecorder && mediaRecorder . state !== 'inactive' ) {
@@ -226,7 +250,16 @@ const SearchContent = () => {
226250 } ) ;
227251 } else {
228252 try {
229- const stream = await navigator . mediaDevices . getUserMedia ( { audio : true } ) ;
253+ const permissionStatus = await requestMicrophonePermission ( ) ;
254+ setMicrophonePermissionStatus ( permissionStatus ) ;
255+
256+ const stream = await navigator . mediaDevices . getUserMedia ( {
257+ audio : {
258+ echoCancellation : true ,
259+ noiseSuppression : true ,
260+ autoGainControl : true ,
261+ } ,
262+ } ) ;
230263 const recorder = new MediaRecorder ( stream ) ;
231264 const chunks = [ ] ;
232265
@@ -314,15 +347,18 @@ const SearchContent = () => {
314347 label : 'start-recording' ,
315348 } ) ;
316349 } catch ( error ) {
317- console . error ( 'Error accessing microphone:' , error ) ;
350+ const errorMessage =
351+ i18n . t ( `MICROPHONE_ERROR.${ error . name } ` ) || i18n . t ( 'MICROPHONE_ERROR.default' ) ;
352+
318353 analytics . trackEvent ( {
319354 category : 'search' ,
320355 action : 'voice-search' ,
321- label : 'microphone- error' ,
356+ label : error . name ,
322357 value : error . message ,
323358 } ) ;
324359
325- alert ( 'Unable to access microphone. Please check permissions and try again.' ) ;
360+ // eslint-disable-next-line no-alert
361+ alert ( errorMessage ) ;
326362 }
327363 }
328364 } ;
@@ -373,6 +409,10 @@ const SearchContent = () => {
373409 < IconButton
374410 icon = { isRecording ? 'fa fa-stop' : 'fa fa-microphone' }
375411 onClick = { handleMicClick }
412+ style = { {
413+ opacity : microphonePermissionStatus === 'denied' ? 0.5 : 1 ,
414+ cursor : microphonePermissionStatus === 'denied' ? 'not-allowed' : 'pointer' ,
415+ } }
376416 />
377417 ) }
378418 { currentLanguage !== 'en' && (
0 commit comments