2021-11-01 06:40:04 +09:00
import styles from "./Panes.module.scss" ;
2021-08-20 02:11:02 +10:00
import { Text } from "preact-i18n" ;
2021-09-09 23:02:47 +01:00
import { useEffect , useState } from "preact/hooks" ;
2021-08-20 02:11:02 +10:00
2022-05-30 14:42:09 +01:00
import { Button , Category , ComboBox , Tip } from "@revoltchat/ui" ;
2022-05-23 21:21:29 +01:00
2021-09-09 23:02:47 +01:00
import { stopPropagation } from "../../../lib/stopPropagation" ;
import { voiceState } from "../../../lib/vortex/VoiceState" ;
2022-05-30 14:42:09 +01:00
import { I18nError } from "../../../context/Locale" ;
2022-05-29 16:38:09 +01:00
2022-05-30 14:42:09 +01:00
import opusSVG from "../assets/opus_logo.svg" ;
2021-12-20 20:48:37 +00:00
{
/*import OpusSVG from "../assets/opus_logo.svg";*/
}
2021-09-08 14:12:15 +02:00
2021-09-09 23:02:47 +01:00
const constraints = { audio : true } ;
2021-08-20 02:11:02 +10:00
2021-12-15 18:23:05 +00:00
// TODO: do not rewrite this code until voice is rewritten!
2021-12-23 21:43:11 +00:00
export function Audio() {
2021-09-09 23:02:47 +01:00
const [ mediaStream , setMediaStream ] = useState < MediaStream | undefined > (
undefined ,
) ;
const [ mediaDevices , setMediaDevices ] = useState <
MediaDeviceInfo [ ] | undefined
> ( undefined ) ;
const [ permission , setPermission ] = useState < PermissionState | undefined > (
undefined ,
) ;
const [ error , setError ] = useState < DOMException | undefined > ( undefined ) ;
2021-09-08 14:12:15 +02:00
const askOrGetPermission = async ( ) = > {
try {
2021-10-31 22:52:20 +01:00
const result = await navigator . mediaDevices . getUserMedia (
2021-09-09 23:02:47 +01:00
constraints ,
) ;
2021-10-31 22:52:20 +01:00
setMediaStream ( result ) ;
2021-09-08 14:12:15 +02:00
} catch ( err ) {
// The user has blocked the permission
2021-09-09 23:02:47 +01:00
setError ( err as DOMException ) ;
2021-09-08 14:12:15 +02:00
}
try {
2021-09-09 23:02:47 +01:00
const { state } = await navigator . permissions . query ( {
// eslint-disable-next-line
2021-10-31 22:52:20 +01:00
// @ts-ignore: very few browsers accept this `PermissionName`, but it has been drafted in https://www.w3.org/TR/permissions/#powerful-features-registry
2021-09-09 23:02:47 +01:00
name : "microphone" ,
} ) ;
2021-09-08 14:12:15 +02:00
2021-09-09 23:02:47 +01:00
setPermission ( state ) ;
2021-09-08 14:12:15 +02:00
} catch ( err ) {
2021-10-31 22:52:20 +01:00
// the browser might not support `query` functionnality or `PermissionName`
// nothing to do
2021-09-08 14:12:15 +02:00
}
2021-09-09 23:02:47 +01:00
} ;
2021-08-20 02:11:02 +10:00
2021-10-31 22:52:20 +01:00
useEffect ( ( ) = > {
return ( ) = > {
if ( mediaStream ) {
// close microphone access on unmount
2021-12-20 13:37:21 +00:00
mediaStream . getTracks ( ) . forEach ( ( track ) = > {
track . stop ( ) ;
} ) ;
2021-10-31 22:52:20 +01:00
}
2021-12-20 13:37:21 +00:00
} ;
2021-10-31 22:52:20 +01:00
} , [ mediaStream ] ) ;
2021-09-08 14:12:15 +02:00
useEffect ( ( ) = > {
if ( ! mediaStream ) {
return ;
}
2021-09-09 23:02:47 +01:00
navigator . mediaDevices . enumerateDevices ( ) . then (
( devices ) = > {
setMediaDevices ( devices ) ;
} ,
( err ) = > {
setError ( err as DOMException ) ;
} ,
) ;
} , [ mediaStream ] ) ;
const handleAskForPermission = (
2021-11-01 06:40:04 +09:00
ev : JSX.TargetedMouseEvent < HTMLElement > ,
2021-09-09 23:02:47 +01:00
) = > {
stopPropagation ( ev ) ;
setError ( undefined ) ;
askOrGetPermission ( ) ;
} ;
2021-08-20 02:11:02 +10:00
return (
< >
2021-11-01 06:40:04 +09:00
< div class = { styles . audio } >
{ ! permission && (
2022-05-30 12:47:13 +01:00
< Tip palette = "error" >
2021-12-20 13:37:21 +00:00
< Text id = "app.settings.pages.audio.tip_grant_permission" / >
< / Tip >
2021-09-09 23:02:47 +01:00
) }
{ error && permission === "prompt" && (
2022-05-30 12:47:13 +01:00
< Tip palette = "error" >
2021-12-20 13:37:21 +00:00
< Text id = "app.settings.pages.audio.tip_retry" / >
< a onClick = { handleAskForPermission } >
< Text id = "app.settings.pages.audio.button_retry" / >
< / a >
.
2021-09-09 23:02:47 +01:00
< / Tip >
) }
2021-12-20 13:37:21 +00:00
< div className = { styles . audioRow } >
< div className = { styles . select } >
< h3 >
< Text id = "app.settings.pages.audio.input_device" / >
< / h3 >
< div class = { styles . audioBox } >
< ComboBox
value = {
window . localStorage . getItem (
"audioInputDevice" ,
) ? ? 0
}
onChange = { ( e ) = >
changeAudioDevice (
e . currentTarget . value ,
"input" ,
)
} >
{ mediaDevices
? . filter (
( device ) = >
device . kind === "audioinput" ,
)
. map ( ( device ) = > {
return (
< option
value = { device . deviceId }
key = { device . deviceId } >
{ device . label || (
< Text id = "app.settings.pages.audio.device_label_NA" / >
) }
< / option >
) ;
} ) }
< / ComboBox >
2021-12-20 20:48:37 +00:00
{ /*TOFIX: add logic to sound notches*/ }
{ / * < d i v c l a s s N a m e = { s t y l e s . n o t c h e s } >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< / div > * / }
2021-12-20 13:37:21 +00:00
{ ! permission && (
< Button
compact
2022-05-23 21:21:29 +01:00
onClick = { ( e : any ) = >
handleAskForPermission ( e )
}
palette = "error" >
2021-12-20 13:37:21 +00:00
< Text id = "app.settings.pages.audio.button_grant" / >
< / Button >
) }
{ error && error . name === "NotAllowedError" && (
2022-05-30 14:42:09 +01:00
< Category >
< I18nError error = "AudioPermissionBlock" / >
< / Category >
2021-12-20 13:37:21 +00:00
) }
< / div >
< / div >
< div className = { styles . select } >
< h3 >
< Text id = "app.settings.pages.audio.output_device" / >
< / h3 >
{ /* TOFIX: create audio output combobox*/ }
< ComboBox
value = {
window . localStorage . getItem (
"audioOutputDevice" ,
) ? ? 0
}
onChange = { ( e ) = >
changeAudioDevice (
e . currentTarget . value ,
"output" ,
)
} >
{ mediaDevices
? . filter (
( device ) = > device . kind === "audiooutput" ,
)
. map ( ( device ) = > {
return (
< option
value = { device . deviceId }
key = { device . deviceId } >
{ device . label || (
< Text id = "app.settings.pages.audio.device_label_NA" / >
) }
< / option >
) ;
} ) }
< / ComboBox >
2021-12-20 20:48:37 +00:00
{ / * < d i v c l a s s N a m e = { s t y l e s . n o t c h e s } >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< div / >
< / div > * / }
2021-12-20 13:37:21 +00:00
< / div >
< / div >
2021-09-09 23:02:47 +01:00
< / div >
2021-12-20 20:48:37 +00:00
< hr / >
< div className = { styles . opus } >
< img height = "20" src = { opusSVG } draggable = { false } / >
Audio codec powered by Opus
< / div >
2021-08-20 02:11:02 +10:00
< / >
) ;
}
function changeAudioDevice ( deviceId : string , deviceType : string ) {
2021-09-09 23:02:47 +01:00
if ( deviceType === "input" ) {
window . localStorage . setItem ( "audioInputDevice" , deviceId ) ;
if ( voiceState . isProducing ( "audio" ) ) {
2021-08-20 02:11:02 +10:00
voiceState . stopProducing ( "audio" ) ;
voiceState . startProducing ( "audio" ) ;
}
2021-09-09 23:02:47 +01:00
} else if ( deviceType === "output" ) {
window . localStorage . setItem ( "audioOutputDevice" , deviceId ) ;
2021-08-20 02:11:02 +10:00
}
}