selecting webrtc device

This commit is contained in:
Roland Osborne 2024-03-04 21:57:38 -08:00
parent 0b4e7da468
commit 9af0fc744f
10 changed files with 167 additions and 31 deletions

View File

@ -176,6 +176,10 @@ export const en = {
editMembership: 'Edit Membership', editMembership: 'Edit Membership',
deleteTopic: 'Delete Topic', deleteTopic: 'Delete Topic',
leaveTopic: 'Leave Topic', leaveTopic: 'Leave Topic',
integrated: 'Integrated',
microphone: 'Microphone',
camera: 'Camera'
}; };
export const fr = { export const fr = {
@ -356,5 +360,9 @@ export const fr = {
editMembership: 'Modifier Membres du Suject', editMembership: 'Modifier Membres du Suject',
deleteTopic: 'Supprimer le Sujet', deleteTopic: 'Supprimer le Sujet',
leaveTopic: 'Quitter le Suject', leaveTopic: 'Quitter le Suject',
integrated: 'Intégré',
microphone: 'Microphone',
camera: 'Caméra'
}; };

View File

@ -164,6 +164,9 @@ export function useRingContext() {
}; };
try { try {
const devices = await navigator.mediaDevices.enumerateDevices();
console.log('>> ', devices);
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: false, video: false,
audio: true, audio: true,
@ -417,6 +420,9 @@ export function useRingContext() {
}, },
enableVideo: async () => { enableVideo: async () => {
if (!accessVideo.current) { if (!accessVideo.current) {
const devices = await navigator.mediaDevices.enumerateDevices();
console.log('>> ', devices);
const stream = await navigator.mediaDevices.getUserMedia({ const stream = await navigator.mediaDevices.getUserMedia({
video: true, video: true,
audio: true, audio: true,
@ -457,6 +463,24 @@ export function useRingContext() {
updateState({ localAudio: false }); updateState({ localAudio: false });
} }
}, },
getDevices: async (type) => {
const filtered = new Map();
const devices = await navigator.mediaDevices.enumerateDevices();
devices.filter(item => item.kind === type + 'input').forEach(item => {
if (item && item.label) {
const entry = filtered.get(item.groupId);
if (entry) {
if (item.label && item.label.length < entry.label.length) {
filtered.set(item.groupId, item);
}
}
else {
filtered.set(item.groupId, item);
}
}
});
return Array.from(filtered.values());
},
} }
return { state, actions } return { state, actions }

View File

@ -18,6 +18,10 @@ export function useSettingsContext() {
strings: en, strings: en,
dateFormat: 'mm/dd', dateFormat: 'mm/dd',
timeFormat: '12h', timeFormat: '12h',
audioInput: null,
audioInputs: [],
videoInput: null,
videoInputs: [],
}); });
const SMALL_MEDIUM = 650; const SMALL_MEDIUM = 650;
@ -43,6 +47,36 @@ export function useSettingsContext() {
} }
}; };
const getDevices = async (type) => {
const filtered = new Map();
const devices = await navigator.mediaDevices.enumerateDevices();
devices.filter(item => item.kind === type + 'input').forEach(item => {
if (item) {
const label = item.label ? item.label : state.strings.integrated;
const entry = filtered.get(item.groupId);
if (entry) {
if (item.label && label.length < entry.label.length) {
filtered.set(item.groupId, { value: item.deviceId, label });
}
}
else {
filtered.set(item.groupId, { value: item.deviceId, label });
}
}
});
return Array.from(filtered.values());
}
useEffect(() => {
getDevices('audio').then(audio => {
updateState({ audioInputs: audio });
});
getDevices('video').then(video => {
updateState({ videoInputs: video });
});
}, [state.strings]);
useEffect(() => { useEffect(() => {
for (let i = 0; i < 10; i++) { for (let i = 0; i < 10; i++) {
setTimeout(handleResize, 100 * i); //cludge for my mobile browser setTimeout(handleResize, 100 * i); //cludge for my mobile browser
@ -100,6 +134,10 @@ export function useSettingsContext() {
} }
} }
const audioInput = localStorage.getItem('audio_input');
const videoInput = localStorage.getItem('video_input');
updateState({ audioInput, videoInput });
return () => { return () => {
window.removeEventListener('resize', handleResize); window.removeEventListener('resize', handleResize);
window.removeEventListener('orientationchange', handleResize); window.removeEventListener('orientationchange', handleResize);
@ -155,6 +193,14 @@ export function useSettingsContext() {
localStorage.setItem('time_format', timeFormat); localStorage.setItem('time_format', timeFormat);
updateState({ timeFormat }); updateState({ timeFormat });
}, },
setAudioInput: (audioInput) => {
localStorage.setItem('audio_input', audioInput);
updateState({ audioInput });
},
setVideoInput: (videoInput) => {
localStorage.setItem('video_input', videoInput);
updateState({ videoInput });
},
} }
return { state, actions } return { state, actions }

View File

@ -135,13 +135,13 @@ export function Profile({ closeProfile }) {
</div> </div>
{ state.display !== 'xlarge' && state.displaySet && ( { state.display !== 'xlarge' && state.displaySet && (
<div className="rightAccess"> <div className="rightAccess">
<AccountAccess />
{ state.display === 'small' && ( { state.display === 'small' && (
<div className="logout" onClick={logout}> <div className="logout">
<LogoutOutlined /> <LogoutOutlined className="icon" onClick={logout} />
<div className="label">{ state.strings.logout }</div> <div className="label" onClick={logout}>{ state.strings.logout }</div>
</div> </div>
)} )}
<AccountAccess />
<div className="contentFill" /> <div className="contentFill" />
</div> </div>
)} )}

View File

@ -205,14 +205,18 @@ export const ProfileWrapper = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
align-items: center; align-items: center;
cursor: pointer; background-color: ${props => props.theme.selectedArea};
color: ${props => props.theme.mainText};
background-color: ${props => props.theme.modalArea};
padding: 8px; padding: 8px;
border-radius: 4px; border-radius: 4px;
justify-content: center; justify-content: center;
.icon {
color: ${props => props.theme.alertText};
cursor: pointer;
}
.label { .label {
cursor: pointer;
padding-left: 8px; padding-left: 8px;
} }
} }

View File

@ -55,6 +55,29 @@ export function AccountAccess() {
return ( return (
<AccountAccessWrapper> <AccountAccessWrapper>
{ modalContext } { modalContext }
<div className="account">
<div className="section">{state.strings.account}</div>
<div className="controls">
<div className="switch">
<div className="control">
<Switch size="small" checked={state.searchable} onChange={enable => saveSearchable(enable)} />
</div>
<div className="switchLabel">{state.strings.registry}</div>
</div>
<div className="link" onClick={actions.setEditSeal}>
<div className="control">
<SettingOutlined />
</div>
<div className="label">{state.strings.sealedTopics}</div>
</div>
<div className="link" onClick={actions.setEditLogin}>
<div className="control">
<LockOutlined />
</div>
<div className="label">{state.strings.changeLogin}</div>
</div>
</div>
</div>
<div className="account"> <div className="account">
<div className="section">{state.strings.application}</div> <div className="section">{state.strings.application}</div>
<div className="controls"> <div className="controls">
@ -94,28 +117,27 @@ export function AccountAccess() {
options={state.languages} options={state.languages}
/> />
</div> </div>
</div> <div className="option">
</div> <div className="label">{state.strings.microphone}</div>
<div className="account"> <Select
<div className="section">{state.strings.account}</div> defaultValue={null}
<div className="controls"> style={{ width: '60%' }}
<div className="switch"> size="small"
<div className="control"> value={state.audioInput}
<Switch size="small" checked={state.searchable} onChange={enable => saveSearchable(enable)} /> onChange={actions.setAudio}
</div> options={[ { value: null, label: 'Default' }, ...state.audioInputs ]}
<div className="switchLabel">{state.strings.registry}</div> />
</div> </div>
<div className="link" onClick={actions.setEditSeal}> <div className="option">
<div className="control"> <div className="label">{state.strings.camera}</div>
<SettingOutlined /> <Select
</div> defaultValue={null}
<div className="label">{state.strings.sealedTopics}</div> style={{ width: '60%' }}
</div> size="small"
<div className="link" onClick={actions.setEditLogin}> value={state.videoInput}
<div className="control"> onChange={actions.setVideo}
<LockOutlined /> options={[ { value: null, label: 'Default' }, ...state.videoInputs ]}
</div> />
<div className="label">{state.strings.changeLogin}</div>
</div> </div>
</div> </div>
</div> </div>

View File

@ -35,6 +35,7 @@ export const AccountAccessWrapper = styled.div`
display: flex; display: flex;
padding-top: 8px; padding-top: 8px;
align-items: center; align-items: center;
width: 100%;
.label { .label {
padding-right: 16px; padding-right: 16px;

View File

@ -2,6 +2,7 @@ import { useRef, useState, useEffect, useContext } from 'react';
import { AccountContext } from 'context/AccountContext'; import { AccountContext } from 'context/AccountContext';
import { ProfileContext } from 'context/ProfileContext'; import { ProfileContext } from 'context/ProfileContext';
import { SettingsContext } from 'context/SettingsContext'; import { SettingsContext } from 'context/SettingsContext';
import { RingContext } from 'context/RingContext';
import { generateSeal, unlockSeal, updateSeal } from 'context/sealUtil'; import { generateSeal, unlockSeal, updateSeal } from 'context/sealUtil';
import { getUsername } from 'api/getUsername'; import { getUsername } from 'api/getUsername';
export function useAccountAccess() { export function useAccountAccess() {
@ -34,6 +35,10 @@ export function useAccountAccess() {
themes: [], themes: [],
language: null, language: null,
languages: [], languages: [],
audioInput: null,
audioInputs: [],
videoInput: null,
videoInputs: [],
seal: null, seal: null,
sealKey: null, sealKey: null,
@ -42,6 +47,7 @@ export function useAccountAccess() {
const profile = useContext(ProfileContext); const profile = useContext(ProfileContext);
const account = useContext(AccountContext); const account = useContext(AccountContext);
const settings = useContext(SettingsContext); const settings = useContext(SettingsContext);
const ring = useContext(RingContext);
const debounce = useRef(null); const debounce = useRef(null);
const updateState = (value) => { const updateState = (value) => {
@ -59,10 +65,21 @@ export function useAccountAccess() {
}, [account.state]); }, [account.state]);
useEffect(() => { useEffect(() => {
const { strings, menuStyle, timeFormat, dateFormat, theme, themes, language, languages } = settings.state; const { audioInput, audioInputs, videoInput, videoInputs, strings, menuStyle, timeFormat, dateFormat, theme, themes, language, languages } = settings.state;
updateState({ strings, menuStyle, timeFormat, dateFormat, theme, themes, language, languages }); updateState({ audioInput, audioInputs, videoInput, videoInputs, strings, menuStyle, timeFormat, dateFormat, theme, themes, language, languages });
}, [settings.state]); }, [settings.state]);
const showDevices = async () => {
const audio = await ring.actions.getDevices('audio');
const video = await ring.actions.getDevices('video');
console.log('devices', audio, video);
};
useEffect(() => {
showDevices();
}, []);
const sealUnlock = async () => { const sealUnlock = async () => {
const unlocked = unlockSeal(state.seal, state.sealUnlock); const unlocked = unlockSeal(state.seal, state.sealUnlock);
await account.actions.unlockSeal(unlocked); await account.actions.unlockSeal(unlocked);
@ -113,6 +130,12 @@ export function useAccountAccess() {
setLanguage: (language) => { setLanguage: (language) => {
settings.actions.setLanguage(language); settings.actions.setLanguage(language);
}, },
setAudio: (device) => {
settings.actions.setAudioInput(device);
},
setVideo: (device) => {
settings.actions.setVideoInput(device);
},
setEditSeal: () => { setEditSeal: () => {
let sealMode; let sealMode;
let sealEnabled = isEnabled(); let sealEnabled = isEnabled();

View File

@ -9,7 +9,7 @@ export const SelectItemWrapper = styled.div`
height: 48px; height: 48px;
width: 100%; width: 100%;
padding-left: 8px; padding-left: 8px;
padding-right: 8px; padding-right: 16px;
display: flex; display: flex;
align-items: center; align-items: center;

8
todo
View File

@ -5,3 +5,11 @@ calling:
- fullscreen - fullscreen
- device selection - device selection
add languages:
spanish
portugues
german
russian
trim docker image