mirror of
https://github.com/jech/galene.git
synced 2024-11-22 16:45: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:
parent
f5cb2ff328
commit
b8011371cf
2 changed files with 177 additions and 44 deletions
|
@ -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>
|
||||||
|
|
215
static/sfu.js
215
static/sfu.js
|
@ -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');
|
||||||
|
|
Loading…
Reference in a new issue