diff --git a/public/locales/en-GB/app.json b/public/locales/en-GB/app.json index ef330e6..7d4ae7b 100644 --- a/public/locales/en-GB/app.json +++ b/public/locales/en-GB/app.json @@ -117,7 +117,7 @@ "This call already exists, would you like to join?": "This call already exists, would you like to join?", "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>Terms and conditions": "This site is protected by ReCAPTCHA and the Google <2>Privacy Policy and <6>Terms of Service apply.<9>By clicking \"Register\", you agree to our <12>Terms and conditions", "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)": "This will make a speaker's audio seem as if it is coming from where their tile is positioned on screen. (Experimental feature: this may impact the stability of audio.)", - "This will send anonymized data (such as the duration of a call and the number of participants) to the element call team to help us optimise the application based on how it is used.": "This will send anonymized data (such as the duration of a call and the number of participants) to the element call team to help us optimise the application based on how it is used.", + "This will send anonymised data (such as the duration of a call and the number of participants) to the Element Call team to help us optimise the application based on how it is used.": "This will send anonymised data (such as the duration of a call and the number of participants) to the Element Call team to help us optimise the application based on how it is used.", "Turn off camera": "Turn off camera", "Turn on camera": "Turn on camera", "Unmute microphone": "Unmute microphone", diff --git a/scripts/dockerbuild.sh b/scripts/dockerbuild.sh index 7dce8f6..cda60c2 100755 --- a/scripts/dockerbuild.sh +++ b/scripts/dockerbuild.sh @@ -7,7 +7,6 @@ export VITE_PRODUCT_NAME="Element Call" git clone https://github.com/matrix-org/matrix-js-sdk.git cd matrix-js-sdk -git checkout robertlong/group-call yarn install yarn run build yarn link diff --git a/src/input/Input.tsx b/src/input/Input.tsx index cd7603f..acec7d3 100644 --- a/src/input/Input.tsx +++ b/src/input/Input.tsx @@ -14,7 +14,7 @@ See the License for the specific language governing permissions and limitations under the License. */ -import React, { ChangeEvent, FC, forwardRef, ReactNode } from "react"; +import React, { ChangeEvent, FC, forwardRef, ReactNode, useId } from "react"; import classNames from "classnames"; import styles from "./Input.module.css"; @@ -96,6 +96,8 @@ export const InputField = forwardRef< }, ref ) => { + const descriptionId = useId(); + return ( } disabled={disabled} + aria-describedby={descriptionId} {...rest} /> ) : ( @@ -122,6 +125,7 @@ export const InputField = forwardRef< type={type} checked={checked} disabled={disabled} + aria-describedby={descriptionId} {...rest} /> )} @@ -135,7 +139,11 @@ export const InputField = forwardRef< {label} {suffix && {suffix}} - {description &&

{description}

} + {description && ( +

+ {description} +

+ )}
); } diff --git a/src/settings/SettingsModal.tsx b/src/settings/SettingsModal.tsx index cb7acb2..f895538 100644 --- a/src/settings/SettingsModal.tsx +++ b/src/settings/SettingsModal.tsx @@ -159,7 +159,7 @@ export const SettingsModal = (props: Props) => { type="checkbox" checked={optInAnalytics} description={t( - "This will send anonymized data (such as the duration of a call and the number of participants) to the element call team to help us optimise the application based on how it is used." + "This will send anonymised data (such as the duration of a call and the number of participants) to the Element Call team to help us optimise the application based on how it is used." )} onChange={(event: React.ChangeEvent) => setOptInAnalytics(event.target.checked) diff --git a/src/video-grid/VideoTileContainer.tsx b/src/video-grid/VideoTileContainer.tsx index cb93dbd..0478bb4 100644 --- a/src/video-grid/VideoTileContainer.tsx +++ b/src/video-grid/VideoTileContainer.tsx @@ -71,8 +71,8 @@ export function VideoTileContainer({ stream ?? null, audioContext, audioDestination, - isLocal, - localVolume + localVolume, + isLocal || maximised ); const { modalState: videoTileSettingsModalState, diff --git a/src/video-grid/useMediaStream.ts b/src/video-grid/useMediaStream.ts index 61c6524..d1879bd 100644 --- a/src/video-grid/useMediaStream.ts +++ b/src/video-grid/useMediaStream.ts @@ -20,6 +20,7 @@ import { acquireContext, releaseContext, } from "matrix-js-sdk/src/webrtc/audioContext"; +import { logger } from "matrix-js-sdk/src/logger"; import { useSpatialAudio } from "../settings/useSetting"; import { useEventTarget } from "../useEvents"; @@ -213,10 +214,10 @@ export const useSpatialMediaStream = ( stream: MediaStream | null, audioContext: AudioContext, audioDestination: AudioNode, - mute = false, - localVolume?: number + localVolume: number, + mute = false ): [RefObject, RefObject] => { - const tileRef = useRef(); + const tileRef = useRef(null); const [spatialAudio] = useSpatialAudio(); // We always handle audio separately form the video element const mediaRef = useMediaStream(stream, null, true); @@ -227,53 +228,63 @@ export const useSpatialMediaStream = ( const sourceRef = useRef(); useEffect(() => { - if (spatialAudio && tileRef.current && !mute && audioTrackCount > 0) { - if (!pannerNodeRef.current) { - pannerNodeRef.current = new PannerNode(audioContext, { - panningModel: "HRTF", - refDistance: 3, - }); + if (spatialAudio) { + if (tileRef.current && !mute && audioTrackCount > 0) { + logger.debug(`Rendering spatial audio for ${stream!.id}`); + + if (!pannerNodeRef.current) { + pannerNodeRef.current = new PannerNode(audioContext, { + panningModel: "HRTF", + refDistance: 3, + }); + } + if (!gainNodeRef.current) { + gainNodeRef.current = new GainNode(audioContext, { + gain: localVolume, + }); + } + if (!sourceRef.current || sourceRef.current.mediaStream !== stream!) { + sourceRef.current = audioContext.createMediaStreamSource(stream!); + } + + const tile = tileRef.current; + const source = sourceRef.current; + const gainNode = gainNodeRef.current; + const pannerNode = pannerNodeRef.current; + + const updatePosition = () => { + const bounds = tile.getBoundingClientRect(); + const windowSize = Math.max(window.innerWidth, window.innerHeight); + // Position the source relative to its placement in the window + pannerNodeRef.current!.positionX.value = + (bounds.x + bounds.width / 2) / windowSize - 0.5; + pannerNodeRef.current!.positionY.value = + (bounds.y + bounds.height / 2) / windowSize - 0.5; + // Put the source in front of the listener + pannerNodeRef.current!.positionZ.value = -2; + }; + + gainNode.gain.value = localVolume; + updatePosition(); + source.connect(gainNode).connect(pannerNode).connect(audioDestination); + // HACK: We abuse the CSS transitionrun event to detect when the tile + // moves, because useMeasure, IntersectionObserver, etc. all have no + // ability to track changes in the CSS transform property + tile.addEventListener("transitionrun", updatePosition); + + return () => { + tile.removeEventListener("transitionrun", updatePosition); + source.disconnect(); + gainNode.disconnect(); + pannerNode.disconnect(); + }; + } else if (stream) { + logger.debug( + `Not rendering spatial audio for ${stream.id} (tile ref ${Boolean( + tileRef.current + )}, mute ${mute}, track count ${audioTrackCount})` + ); } - if (!gainNodeRef.current) { - gainNodeRef.current = new GainNode(audioContext, { - gain: localVolume, - }); - } - if (!sourceRef.current) { - sourceRef.current = audioContext.createMediaStreamSource(stream!); - } - - const tile = tileRef.current; - const source = sourceRef.current; - const gainNode = gainNodeRef.current; - const pannerNode = pannerNodeRef.current; - - const updatePosition = () => { - const bounds = tile.getBoundingClientRect(); - const windowSize = Math.max(window.innerWidth, window.innerHeight); - // Position the source relative to its placement in the window - pannerNodeRef.current!.positionX.value = - (bounds.x + bounds.width / 2) / windowSize - 0.5; - pannerNodeRef.current!.positionY.value = - (bounds.y + bounds.height / 2) / windowSize - 0.5; - // Put the source in front of the listener - pannerNodeRef.current!.positionZ.value = -2; - }; - - gainNode.gain.value = localVolume; - updatePosition(); - source.connect(gainNode).connect(pannerNode).connect(audioDestination); - // HACK: We abuse the CSS transitionrun event to detect when the tile - // moves, because useMeasure, IntersectionObserver, etc. all have no - // ability to track changes in the CSS transform property - tile.addEventListener("transitionrun", updatePosition); - - return () => { - tile.removeEventListener("transitionrun", updatePosition); - source.disconnect(); - gainNode.disconnect(); - pannerNode.disconnect(); - }; } }, [ stream,