1
Fork 0
mirror of https://github.com/jech/galene.git synced 2024-11-23 00:55:58 +01:00

Rework settings handling in sfu.js.

There is now a data structure that contains the ultimate truth
about the user's preferences.  We no longer grovel through the
UI components.
This commit is contained in:
Juliusz Chroboczek 2020-09-18 17:57:37 +02:00
parent f5cb2ff328
commit b8011371cf
2 changed files with 177 additions and 44 deletions

View file

@ -146,14 +146,12 @@
<div id="mediaoptions"> <div id="mediaoptions">
<label for="videoselect">Camera:</label> <label for="videoselect">Camera:</label>
<select id="videoselect" class="select select-inline"> <select id="videoselect" class="select select-inline">
<option>default</option> <option value="">off</option>
<option>off</option>
</select> </select>
<label for="audioselect">Microphone:</label> <label for="audioselect">Microphone:</label>
<select id="audioselect" class="select select-inline"> <select id="audioselect" class="select select-inline">
<option>default</option> <option value="">off</option>
<option>off</option>
</select> </select>
</div> </div>

View file

@ -29,7 +29,7 @@ let fallbackUserPass = null;
* @param {string} username * @param {string} username
* @param {string} password * @param {string} password
*/ */
function setUserPass(username, password) { function storeUserPass(username, password) {
let userpass = {username: username, password: password}; let userpass = {username: username, password: password};
try { try {
window.sessionStorage.setItem('userpass', JSON.stringify(userpass)); window.sessionStorage.setItem('userpass', JSON.stringify(userpass));
@ -41,9 +41,12 @@ function setUserPass(username, password) {
} }
/** /**
* Returns null if the user hasn't logged in yet.
*
* @returns {userpass} * @returns {userpass}
*/ */
function getUserPass() { function getUserPass() {
/** @type{userpass} */
let userpass; let userpass;
try { try {
let json = window.sessionStorage.getItem('userpass'); let json = window.sessionStorage.getItem('userpass');
@ -56,6 +59,8 @@ function getUserPass() {
} }
/** /**
* Return null if the user hasn't logged in yet.
*
* @returns {string} * @returns {string}
*/ */
function getUsername() { function getUsername() {
@ -65,6 +70,104 @@ function getUsername() {
return userpass.username; return userpass.username;
} }
/**
* @typedef {Object} settings
* @property {boolean} [localMute]
* @property {string} [video]
* @property {string} [audio]
* @property {string} [send]
* @property {string} [request]
* @property {boolean} [activityDetection]
*/
/** @type{settings} */
let fallbackSettings = null;
/**
* @param {settings} settings
*/
function storeSettings(settings) {
try {
window.sessionStorage.setItem('settings', JSON.stringify(settings));
fallbackSettings = null;
} catch(e) {
console.warn("Couldn't store password:", e);
fallbackSettings = settings;
}
}
/**
* This always returns a dictionary.
*
* @returns {settings}
*/
function getSettings() {
/** @type{settings} */
let settings;
try {
let json = window.sessionStorage.getItem('settings');
settings = JSON.parse(json);
} catch(e) {
console.warn("Couldn't retrieve password:", e);
settings = fallbackSettings;
}
return settings || {};
}
/**
* @param {settings} settings
*/
function updateSettings(settings) {
let s = getSettings();
for(let key in settings)
s[key] = settings[key];
storeSettings(s);
}
function reflectSettings() {
let settings = getSettings();
let store = false;
setLocalMute(settings.localMute);
let videoselect =
/** @type {HTMLSelectElement} */(document.getElementById('videoselect'));
if(!settings.video || !selectOptionAvailable(videoselect, settings.video)) {
settings.video = selectOptionDefault(videoselect);
store = true;
}
videoselect.value = settings.video;
let audioselect =
/** @type {HTMLSelectElement} */(document.getElementById('audioselect'));
if(!settings.audio || !selectOptionAvailable(audioselect, settings.audio)) {
settings.audio = selectOptionDefault(audioselect);
store = true;
}
audioselect.value = settings.audio;
if(settings.request)
document.getElementById('requestselect').value = settings.request;
else {
settings.request = document.getElementById('requestselect').value;
store = true;
}
if(settings.send)
document.getElementById('sendselect').value = settings.send;
else {
settings.send = document.getElementById('sendselect').value;
store = true;
}
document.getElementById('activitybox').checked = settings.activityDetection;
if(store)
storeSettings(settings);
}
function showVideo() { function showVideo() {
let width = window.innerWidth; let width = window.innerWidth;
let video_container = document.getElementById('video-container'); let video_container = document.getElementById('video-container');
@ -130,7 +233,7 @@ function gotConnected() {
let up = getUserPass(); let up = getUserPass();
this.login(up.username, up.password); this.login(up.username, up.password);
this.join(group); this.join(group);
this.request(document.getElementById('requestselect').value); this.request(getSettings().request);
} }
/** /**
@ -166,7 +269,7 @@ function gotDownStream(c) {
setMediaStatus(c); setMediaStatus(c);
} }
c.onstats = gotDownStats; c.onstats = gotDownStats;
if(document.getElementById('activitybox').checked) if(getSettings().activityDetection)
c.setStatsInterval(activityDetectionInterval); c.setStatsInterval(activityDetectionInterval);
} }
@ -226,22 +329,14 @@ function setButtonsVisibility() {
setVisibility('mediaoptions', permissions.present); setVisibility('mediaoptions', permissions.present);
} }
/** @type {boolean} */
let localMute = false;
function toggleLocalMute() {
setLocalMute(!localMute);
}
/** /**
* @param {boolean} mute * @param {boolean} mute
*/ */
function setLocalMute(mute) { function setLocalMute(mute) {
localMute = mute; muteLocalTracks(mute);
muteLocalTracks(localMute);
let button = document.getElementById('mutebutton'); let button = document.getElementById('mutebutton');
let icon = button.querySelector("span .fa"); let icon = button.querySelector("span .fa");
if(localMute){ if(mute){
icon.classList.add('fa-microphone-slash'); icon.classList.add('fa-microphone-slash');
icon.classList.remove('fa-microphone'); icon.classList.remove('fa-microphone');
button.classList.add('muted'); button.classList.add('muted');
@ -254,17 +349,22 @@ function setLocalMute(mute) {
document.getElementById('videoselect').onchange = function(e) { document.getElementById('videoselect').onchange = function(e) {
e.preventDefault(); e.preventDefault();
updateSettings({video: this.value});
changePresentation(); changePresentation();
}; };
document.getElementById('audioselect').onchange = function(e) { document.getElementById('audioselect').onchange = function(e) {
e.preventDefault(); e.preventDefault();
updateSettings({audio: this.value});
changePresentation(); changePresentation();
}; };
document.getElementById('mutebutton').onclick = function(e) { document.getElementById('mutebutton').onclick = function(e) {
e.preventDefault(); e.preventDefault();
toggleLocalMute(); let localMute = getSettings().localMute;
localMute = !localMute;
updateSettings({localMute: localMute})
setLocalMute(localMute);
} }
document.getElementById('sharebutton').onclick = function(e) { document.getElementById('sharebutton').onclick = function(e) {
@ -279,7 +379,7 @@ document.getElementById('unsharebutton').onclick = function(e) {
/** @returns {number} */ /** @returns {number} */
function getMaxVideoThroughput() { function getMaxVideoThroughput() {
let v = document.getElementById('sendselect').value; let v = getSettings().send;
switch(v) { switch(v) {
case 'lowest': case 'lowest':
return 150000; return 150000;
@ -296,6 +396,7 @@ function getMaxVideoThroughput() {
} }
document.getElementById('sendselect').onchange = async function(e) { document.getElementById('sendselect').onchange = async function(e) {
updateSettings({send: this.value});
let t = getMaxVideoThroughput(); let t = getMaxVideoThroughput();
for(let id in serverConnection.up) { for(let id in serverConnection.up) {
let c = serverConnection.up[id]; let c = serverConnection.up[id];
@ -306,6 +407,7 @@ document.getElementById('sendselect').onchange = async function(e) {
document.getElementById('requestselect').onchange = function(e) { document.getElementById('requestselect').onchange = function(e) {
e.preventDefault(); e.preventDefault();
updateSettings({request: this.value});
serverConnection.request(this.value); serverConnection.request(this.value);
}; };
@ -314,13 +416,14 @@ const activityDetectionPeriod = 700;
const activityDetectionThreshold = 0.2; const activityDetectionThreshold = 0.2;
document.getElementById('activitybox').onchange = function(e) { document.getElementById('activitybox').onchange = function(e) {
updateSettings({activityDetection: this.checked});
for(let id in serverConnection.down) { for(let id in serverConnection.down) {
let c = serverConnection.down[id]; let c = serverConnection.down[id];
if(this.checked) if(this.checked)
c.setStatsInterval(activityDetectionInterval); c.setStatsInterval(activityDetectionInterval);
else { else {
setActive(c, false);
c.setStatsInterval(0); c.setStatsInterval(0);
setActive(c, false);
} }
} }
} }
@ -385,24 +488,19 @@ function gotDownStats(stats) {
} }
/** /**
* @param {string} value * @param {HTMLSelectElement} select
* @param {string} label
* @param {string} [value]
*/ */
function mapMediaOption(value) {
switch(value) {
case 'default':
return true;
case 'off':
return false;
default:
return {deviceId: value};
}
}
function addSelectOption(select, label, value) { function addSelectOption(select, label, value) {
if(!value) if(!value)
value = label; value = label;
for(let i = 0; i < select.children.length; i++) { for(let i = 0; i < select.children.length; i++) {
if(select.children[i].value === value) { let child = /** @type {HTMLOptionElement} */ (select.children[i]);
if(child.value === value) {
if(child.label !== label) {
child.label = label;
}
return; return;
} }
} }
@ -413,12 +511,46 @@ function addSelectOption(select, label, value) {
select.appendChild(option); select.appendChild(option);
} }
// media names might not be available before we call getDisplayMedia. So /**
// we call this lazily. * @param {HTMLSelectElement} select
* @param {string} value
*/
function selectOptionAvailable(select, value) {
let children = select.children;
for(let i = 0; i < children.length; i++) {
let child = /** @type {HTMLOptionElement} */ (select.children[i]);
if(child.value === value) {
return true;
}
}
return false;
}
/**
* @param {HTMLSelectElement} select
* @returns {string}
*/
function selectOptionDefault(select) {
/* First non-empty option. */
for(let i = 0; i < select.children.length; i++) {
let child = /** @type {HTMLOptionElement} */ (select.children[i]);
if(child.value)
return child.value;
}
/* The empty option is always available. */
return '';
}
/* media names might not be available before we call getDisplayMedia. So
we call this twice, the second time to update the menu with user-readable
labels. */
/** @type {boolean} */ /** @type {boolean} */
let mediaChoicesDone = false; let mediaChoicesDone = false;
async function setMediaChoices() { /**
* @param{boolean} done
*/
async function setMediaChoices(done) {
if(mediaChoicesDone) if(mediaChoicesDone)
return; return;
@ -449,9 +581,10 @@ async function setMediaChoices() {
} }
}); });
mediaChoicesDone = true; mediaChoicesDone = done;
} }
/** /**
* @param {string} [id] * @param {string} [id]
*/ */
@ -508,8 +641,10 @@ async function addLocalMedia(id) {
if(!getUserPass()) if(!getUserPass())
return; return;
let audio = mapMediaOption(document.getElementById('audioselect').value); let settings = getSettings();
let video = mapMediaOption(document.getElementById('videoselect').value);
let audio = settings.audio ? {deviceId: settings.audio} : false;
let video = settings.video ? {deviceId: settings.video} : false;
let old = id && serverConnection.up[id]; let old = id && serverConnection.up[id];
@ -534,15 +669,16 @@ async function addLocalMedia(id) {
return; return;
} }
setMediaChoices(); setMediaChoices(true);
let c = newUpStream(id); let c = newUpStream(id);
c.kind = 'local'; c.kind = 'local';
c.stream = stream; c.stream = stream;
let mute = getSettings().localMute;
stream.getTracks().forEach(t => { stream.getTracks().forEach(t => {
c.labels[t.id] = t.kind c.labels[t.id] = t.kind
if(t.kind == 'audio' && localMute) if(t.kind == 'audio' && mute)
t.enabled = false; t.enabled = false;
let sender = c.pc.addTrack(t, stream); let sender = c.pc.addTrack(t, stream);
}); });
@ -1212,7 +1348,7 @@ document.getElementById('userform').onsubmit = function(e) {
e.preventDefault(); e.preventDefault();
let username = document.getElementById('username').value.trim(); let username = document.getElementById('username').value.trim();
let password = document.getElementById('password').value; let password = document.getElementById('password').value;
setUserPass(username, password); storeUserPass(username, password);
serverConnect(); serverConnect();
}; };
@ -1222,7 +1358,6 @@ document.getElementById('disconnectbutton').onclick = function(e) {
if (user_box.classList.contains("show")) { if (user_box.classList.contains("show")) {
user_box.classList.toggle("show"); user_box.classList.toggle("show");
} }
}; };
function openNav() { function openNav() {
@ -1328,7 +1463,7 @@ function start() {
document.getElementById('title').textContent = title; document.getElementById('title').textContent = title;
} }
setLocalMute(localMute); setMediaChoices(false).then(e => reflectSettings());
document.getElementById("user").classList.add('invisible'); document.getElementById("user").classList.add('invisible');
document.getElementById("login-container").classList.remove('invisible'); document.getElementById("login-container").classList.remove('invisible');