diff --git a/src/assets/icons/icon.tsx b/src/assets/icons/icon.tsx new file mode 100644 index 00000000..e84bf61a --- /dev/null +++ b/src/assets/icons/icon.tsx @@ -0,0 +1,12 @@ +import styled from "@emotion/styled"; +import MicMute from "./mic_off.svg"; +import MicUnmute from "./mic_on.svg"; + +const Icon = styled.img` + width: 8rem; + padding-right: 1em; +`; + +export const MicMuted = () => ; + +export const MicUnmuted = () => ; diff --git a/src/assets/icons/mic_off.svg b/src/assets/icons/mic_off.svg new file mode 100644 index 00000000..3b7b7b8a --- /dev/null +++ b/src/assets/icons/mic_off.svg @@ -0,0 +1,5 @@ + + + + \ No newline at end of file diff --git a/src/assets/icons/mic_on.svg b/src/assets/icons/mic_on.svg new file mode 100644 index 00000000..eafe0658 --- /dev/null +++ b/src/assets/icons/mic_on.svg @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/src/components/production-line/production-line.tsx b/src/components/production-line/production-line.tsx index dbb04072..6ea0672d 100644 --- a/src/components/production-line/production-line.tsx +++ b/src/components/production-line/production-line.tsx @@ -9,16 +9,25 @@ import { ActionButton } from "../landing-page/form-elements.tsx"; import { UserList } from "./user-list.tsx"; import { API } from "../../api/api.ts"; import { noop } from "../../helpers.ts"; +import { MicMuted, MicUnmuted } from "../../assets/icons/icon.tsx"; import { Spinner } from "../loader/loader.tsx"; const TempDiv = styled.div` padding: 1rem; `; + +const UserControlBtn = styled.button` + background-color: transparent; + border-color: transparent; +`; + export const ProductionLine: FC = () => { // const { productionId, lineId } = useParams(); - const [{ joinProductionOptions }, dispatch] = useGlobalState(); + const [{ joinProductionOptions, mediaStreamInput }, dispatch] = + useGlobalState(); const navigate = useNavigate(); const audioContainerRef = useRef(null); + const [micMute, setMicMute] = useState(true); const [participants, setParticipants] = useState< { name: string; sessionid: string }[] | null >(null); @@ -81,7 +90,15 @@ export const ProductionLine: FC = () => { navigate("/"); }; - // Mute/Unmute mic + useEffect(() => { + if (mediaStreamInput) { + mediaStreamInput.getTracks().forEach((track) => { + // eslint-disable-next-line no-param-reassign + track.enabled = !micMute; + }); + } + }, [mediaStreamInput, micMute]); + // Mute/Unmute speaker // Show active sink and mic // Exit button (link to /, clear production from state) @@ -95,6 +112,11 @@ export const ProductionLine: FC = () => { {!loading && ( <> Production View + + setMicMute(!micMute)}> + {micMute ? : } + + {audioElements.length && ( Incoming Audio Channels: {audioElements.length} diff --git a/src/components/production-line/use-rtc-connection.ts b/src/components/production-line/use-rtc-connection.ts index 04fc2438..a19860b2 100644 --- a/src/components/production-line/use-rtc-connection.ts +++ b/src/components/production-line/use-rtc-connection.ts @@ -198,6 +198,11 @@ export const useRtcConnection = ({ rtcPeerConnection, inputAudioStream, }); + + dispatch({ + type: "CONNECTED_MEDIASTREAM", + payload: inputAudioStream, + }); } const { teardown } = establishConnection({ @@ -217,6 +222,11 @@ export const useRtcConnection = ({ onConnectionStateChange ); + dispatch({ + type: "CONNECTED_MEDIASTREAM", + payload: null, + }); + rtcPeerConnection.close(); }; }, [ diff --git a/src/global-state/global-state-actions.ts b/src/global-state/global-state-actions.ts index b81f5c55..53522e7b 100644 --- a/src/global-state/global-state-actions.ts +++ b/src/global-state/global-state-actions.ts @@ -5,7 +5,8 @@ export type TGlobalStateAction = | TProductionCreated | TProductionListFetched | TUpdateDevicesAction - | TUpdateJoinProductionOptions; + | TUpdateJoinProductionOptions + | TMediaStream; export type TPublishError = { type: "ERROR"; @@ -29,3 +30,8 @@ export type TUpdateJoinProductionOptions = { type: "UPDATE_JOIN_PRODUCTION_OPTIONS"; payload: TJoinProductionOptions | null; }; + +export type TMediaStream = { + type: "CONNECTED_MEDIASTREAM"; + payload: MediaStream | null; +}; diff --git a/src/global-state/global-state-reducer.ts b/src/global-state/global-state-reducer.ts index b397fbd2..ed1a95e8 100644 --- a/src/global-state/global-state-reducer.ts +++ b/src/global-state/global-state-reducer.ts @@ -9,6 +9,7 @@ const initialGlobalState: TGlobalState = { reloadProductionList: true, devices: null, joinProductionOptions: null, + mediaStreamInput: null, }; const globalReducer: Reducer = ( @@ -43,6 +44,11 @@ const globalReducer: Reducer = ( ...state, joinProductionOptions: action.payload, }; + case "CONNECTED_MEDIASTREAM": + return { + ...state, + mediaStreamInput: action.payload, + }; default: return state; } diff --git a/src/global-state/types.ts b/src/global-state/types.ts index acadd021..8eb0b69c 100644 --- a/src/global-state/types.ts +++ b/src/global-state/types.ts @@ -9,4 +9,5 @@ export type TGlobalState = { reloadProductionList: boolean; devices: MediaDeviceInfo[] | null; joinProductionOptions: TJoinProductionOptions | null; + mediaStreamInput: MediaStream | null; };