208 lines
7.6 KiB
JavaScript
208 lines
7.6 KiB
JavaScript
/**
|
|
* Audio/Video Conferencing Configuration Sheet
|
|
* @extends {FormApplication}
|
|
*
|
|
* @param {AVMaster} object The {@link AVMaster} instance being configured.
|
|
* @param {FormApplicationOptions} [options] Application configuration options.
|
|
*/
|
|
class AVConfig extends FormApplication {
|
|
constructor(object, options) {
|
|
super(object || game.webrtc, options);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
static get defaultOptions() {
|
|
return foundry.utils.mergeObject(super.defaultOptions, {
|
|
title: game.i18n.localize("WEBRTC.Title"),
|
|
id: "av-config",
|
|
template: "templates/sidebar/apps/av-config.html",
|
|
popOut: true,
|
|
width: 480,
|
|
height: "auto",
|
|
tabs: [{navSelector: ".tabs", contentSelector: "form", initial: "general"}]
|
|
});
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
async getData(options={}) {
|
|
const settings = this.object.settings;
|
|
const videoSources = await this.object.client.getVideoSources();
|
|
const audioSources = await this.object.client.getAudioSources();
|
|
const audioSinks = await this.object.client.getAudioSinks();
|
|
|
|
// If the currently chosen device is unavailable, display a separate option for 'unavailable device (use default)'
|
|
const { videoSrc, audioSrc, audioSink } = settings.client;
|
|
const videoSrcUnavailable = this._isSourceUnavailable(videoSources, videoSrc);
|
|
const audioSrcUnavailable = this._isSourceUnavailable(audioSources, audioSrc);
|
|
const audioSinkUnavailable = this._isSourceUnavailable(audioSinks, audioSink);
|
|
const isSSL = window.location.protocol === "https:";
|
|
|
|
// Audio/Video modes
|
|
const modes = {
|
|
[AVSettings.AV_MODES.DISABLED]: "WEBRTC.ModeDisabled",
|
|
[AVSettings.AV_MODES.AUDIO]: "WEBRTC.ModeAudioOnly",
|
|
[AVSettings.AV_MODES.VIDEO]: "WEBRTC.ModeVideoOnly",
|
|
[AVSettings.AV_MODES.AUDIO_VIDEO]: "WEBRTC.ModeAudioVideo"
|
|
};
|
|
|
|
// Voice Broadcast modes
|
|
const voiceModes = Object.values(AVSettings.VOICE_MODES).reduce((obj, m) => {
|
|
obj[m] = game.i18n.localize(`WEBRTC.VoiceMode${m.titleCase()}`);
|
|
return obj;
|
|
}, {});
|
|
|
|
// Nameplate settings.
|
|
const nameplates = {
|
|
[AVSettings.NAMEPLATE_MODES.OFF]: "WEBRTC.NameplatesOff",
|
|
[AVSettings.NAMEPLATE_MODES.PLAYER_ONLY]: "WEBRTC.NameplatesPlayer",
|
|
[AVSettings.NAMEPLATE_MODES.CHAR_ONLY]: "WEBRTC.NameplatesCharacter",
|
|
[AVSettings.NAMEPLATE_MODES.BOTH]: "WEBRTC.NameplatesBoth"
|
|
};
|
|
|
|
const dockPositions = Object.fromEntries(Object.values(AVSettings.DOCK_POSITIONS).map(p => {
|
|
return [p, game.i18n.localize(`WEBRTC.DockPosition${p.titleCase()}`)];
|
|
}));
|
|
|
|
// Return data to the template
|
|
return {
|
|
user: game.user,
|
|
modes,
|
|
voiceModes,
|
|
serverTypes: {FVTT: "WEBRTC.FVTTSignalingServer", custom: "WEBRTC.CustomSignalingServer"},
|
|
turnTypes: {server: "WEBRTC.TURNServerProvisioned", custom: "WEBRTC.CustomTURNServer"},
|
|
settings,
|
|
canSelectMode: game.user.isGM && isSSL,
|
|
noSSL: !isSSL,
|
|
videoSources,
|
|
audioSources,
|
|
audioSinks: foundry.utils.isEmpty(audioSinks) ? false : audioSinks,
|
|
videoSrcUnavailable,
|
|
audioSrcUnavailable,
|
|
audioSinkUnavailable,
|
|
audioDisabled: audioSrc === "disabled",
|
|
videoDisabled: videoSrc === "disabled",
|
|
nameplates,
|
|
nameplateSetting: settings.client.nameplates ?? AVSettings.NAMEPLATE_MODES.BOTH,
|
|
dockPositions,
|
|
audioSourceOptions: this.#getDevices(audioSources, audioSrcUnavailable, "WEBRTC.DisableAudioSource"),
|
|
audioSinkOptions: this.#getDevices(audioSinks, audioSinkUnavailable),
|
|
videoSourceOptions: this.#getDevices(videoSources, videoSrcUnavailable, "WEBRTC.DisableVideoSource")
|
|
};
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Get an array of available devices which can be chosen.
|
|
* @param {Record<string, string>} devices
|
|
* @param {string} unavailableDevice
|
|
* @param {string} disabledLabel
|
|
* @returns {FormSelectOption[]}
|
|
*/
|
|
#getDevices(devices, unavailableDevice, disabledLabel) {
|
|
const options = [];
|
|
let hasDefault = false;
|
|
for ( const [k, v] of Object.entries(devices) ) {
|
|
if ( k === "default" ) hasDefault = true;
|
|
options.push({value: k, label: v});
|
|
}
|
|
if ( !hasDefault ) {
|
|
options.unshift({value: "default", label: game.i18n.localize("WEBRTC.DefaultSource")});
|
|
}
|
|
if ( disabledLabel ) {
|
|
options.unshift({value: "disabled", label: game.i18n.localize(disabledLabel)});
|
|
}
|
|
if ( unavailableDevice ) {
|
|
options.push({value: unavailableDevice, label: game.i18n.localize("WEBRTC.UnavailableDevice")});
|
|
}
|
|
return options;
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
/* Event Listeners and Handlers */
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
activateListeners(html) {
|
|
super.activateListeners(html);
|
|
|
|
// Options below are GM only
|
|
if ( !game.user.isGM ) return;
|
|
html.find('select[name="world.turn.type"]').change(this._onTurnTypeChanged.bind(this));
|
|
|
|
// Activate or de-activate the custom server and turn configuration sections based on current settings
|
|
const settings = this.object.settings;
|
|
this._setConfigSectionEnabled(".webrtc-custom-turn-config", settings.world.turn.type === "custom");
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Set a section's input to enabled or disabled
|
|
* @param {string} selector Selector for the section to enable or disable
|
|
* @param {boolean} enabled Whether to enable or disable this section
|
|
* @private
|
|
*/
|
|
_setConfigSectionEnabled(selector, enabled = true) {
|
|
let section = this.element.find(selector);
|
|
if (section) {
|
|
section.css("opacity", enabled ? 1.0 : 0.5);
|
|
section.find("input").prop("disabled", !enabled);
|
|
}
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Determine whether a given video or audio source, or audio sink has become
|
|
* unavailable since the last time it was set.
|
|
* @param {object} sources The available devices
|
|
* @param {string} source The selected device
|
|
* @private
|
|
*/
|
|
_isSourceUnavailable(sources, source) {
|
|
const specialValues = ["default", "disabled"];
|
|
return source && (!specialValues.includes(source)) && !Object.keys(sources).includes(source);
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/**
|
|
* Callback when the turn server type changes
|
|
* Will enable or disable the turn section based on whether the user selected a custom turn or not
|
|
* @param {Event} event The event that triggered the turn server type change
|
|
* @private
|
|
*/
|
|
_onTurnTypeChanged(event) {
|
|
event.preventDefault();
|
|
const choice = event.currentTarget.value;
|
|
this._setConfigSectionEnabled(".webrtc-custom-turn-config", choice === "custom")
|
|
}
|
|
|
|
/* -------------------------------------------- */
|
|
|
|
/** @override */
|
|
async _updateObject(event, formData) {
|
|
const settings = game.webrtc.settings;
|
|
settings.client.videoSrc = settings.client.videoSrc || null;
|
|
settings.client.audioSrc = settings.client.audioSrc || null;
|
|
|
|
const update = foundry.utils.expandObject(formData);
|
|
|
|
// Update world settings
|
|
if ( game.user.isGM ) {
|
|
if ( settings.world.mode !== update.world.mode ) SettingsConfig.reloadConfirm({world: true});
|
|
const world = foundry.utils.mergeObject(settings.world, update.world);
|
|
await game.settings.set("core", "rtcWorldSettings", world);
|
|
}
|
|
|
|
// Update client settings
|
|
const client = foundry.utils.mergeObject(settings.client, update.client);
|
|
await game.settings.set("core", "rtcClientSettings", client);
|
|
}
|
|
}
|