Files
Foundry-VTT-Docker/resources/app/client/apps/av/av-config.js
2025-01-04 00:34:03 +01:00

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);
}
}