Refactor devolutions-patch.js for reduced indent size

Refactor the `windows-rdp/devolutions-patch.js` script to use a two-space
indentation style from a previous four-space style. This change improves
code readability and consistency with the project's codebase formatting
style.

All functional code remains unaffected; only formatting has been
adjusted.
atif/qol-improvments
Muhammad Atif Ali 8 months ago
parent 5e02fd8056
commit 01a25f7182

@ -53,23 +53,23 @@ const SCREEN_POLL_INTERVAL_MS = 500;
* @satisfies {FormFieldEntries} * @satisfies {FormFieldEntries}
*/ */
const formFieldEntries = { const formFieldEntries = {
/** @readonly */ /** @readonly */
username: { username: {
/** @readonly */ /** @readonly */
querySelector: "web-client-username-control input", querySelector: "web-client-username-control input",
/** @readonly */ /** @readonly */
value: "${CODER_USERNAME}", value: "${CODER_USERNAME}",
}, },
/** @readonly */ /** @readonly */
password: { password: {
/** @readonly */ /** @readonly */
querySelector: "web-client-password-control input", querySelector: "web-client-password-control input",
/** @readonly */ /** @readonly */
value: "${CODER_PASSWORD}", value: "${CODER_PASSWORD}",
}, },
}; };
/** /**
@ -93,32 +93,32 @@ const formFieldEntries = {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
function setInputValue(inputField, inputText) { function setInputValue(inputField, inputText) {
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
// Adding timeout for input event, even though we'll be dispatching it // Adding timeout for input event, even though we'll be dispatching it
// immediately, just in the off chance that something in the Angular app // immediately, just in the off chance that something in the Angular app
// intercepts it or stops it from propagating properly // intercepts it or stops it from propagating properly
const timeoutId = window.setTimeout(() => { const timeoutId = window.setTimeout(() => {
reject(new Error("Input event did not get processed correctly in time.")); reject(new Error("Input event did not get processed correctly in time."));
}, 3_000); }, 3_000);
const handleSuccessfulDispatch = () => { const handleSuccessfulDispatch = () => {
window.clearTimeout(timeoutId); window.clearTimeout(timeoutId);
inputField.removeEventListener("input", handleSuccessfulDispatch); inputField.removeEventListener("input", handleSuccessfulDispatch);
resolve(); resolve();
}; };
inputField.addEventListener("input", handleSuccessfulDispatch); inputField.addEventListener("input", handleSuccessfulDispatch);
// Code assumes that Angular will have an event handler in place to handle // Code assumes that Angular will have an event handler in place to handle
// the new event // the new event
const inputEvent = new Event("input", { const inputEvent = new Event("input", {
bubbles: true, bubbles: true,
cancelable: true, cancelable: true,
}); });
inputField.value = inputText; inputField.value = inputText;
inputField.dispatchEvent(inputEvent); inputField.dispatchEvent(inputEvent);
}); });
} }
/** /**
@ -137,91 +137,91 @@ function setInputValue(inputField, inputText) {
* @returns {Promise<void>} * @returns {Promise<void>}
*/ */
async function autoSubmitForm(myForm) { async function autoSubmitForm(myForm) {
const setProtocolValue = () => { const setProtocolValue = () => {
/** @type {HTMLDivElement | null} */ /** @type {HTMLDivElement | null} */
const protocolDropdownTrigger = myForm.querySelector('div[role="button"]'); const protocolDropdownTrigger = myForm.querySelector('div[role="button"]');
if (protocolDropdownTrigger === null) { if (protocolDropdownTrigger === null) {
throw new Error("No clickable trigger for setting protocol value"); throw new Error("No clickable trigger for setting protocol value");
} }
protocolDropdownTrigger.click(); protocolDropdownTrigger.click();
// Can't use form as container for querying the list of dropdown options, // Can't use form as container for querying the list of dropdown options,
// because the elements don't actually exist inside the form. They're placed // because the elements don't actually exist inside the form. They're placed
// in the top level of the HTML doc, and repositioned to make it look like // in the top level of the HTML doc, and repositioned to make it look like
// they're part of the form. Avoids CSS stacking context issues, maybe? // they're part of the form. Avoids CSS stacking context issues, maybe?
/** @type {HTMLLIElement | null} */ /** @type {HTMLLIElement | null} */
const protocolOption = document.querySelector( const protocolOption = document.querySelector(
`p-dropdownitem[ng-reflect-label="${PROTOCOL}"] li`, 'p-dropdownitem[ng-reflect-label="' + PROTOCOL + '"] li',
); );
if (protocolOption === null) { if (protocolOption === null) {
throw new Error( throw new Error(
"Unable to find protocol option on screen that matches desired protocol", "Unable to find protocol option on screen that matches desired protocol",
); );
} }
protocolOption.click(); protocolOption.click();
}; };
const setHostname = () => { const setHostname = () => {
/** @type {HTMLInputElement | null} */ /** @type {HTMLInputElement | null} */
const hostnameInput = myForm.querySelector("p-autocomplete#hostname input"); const hostnameInput = myForm.querySelector("p-autocomplete#hostname input");
if (hostnameInput === null) { if (hostnameInput === null) {
throw new Error("Unable to find field for adding hostname"); throw new Error("Unable to find field for adding hostname");
} }
return setInputValue(hostnameInput, HOSTNAME); return setInputValue(hostnameInput, HOSTNAME);
}; };
const setCoderFormFieldValues = async () => { const setCoderFormFieldValues = async () => {
// The RDP form will not appear on screen unless the dropdown is set to use // The RDP form will not appear on screen unless the dropdown is set to use
// the RDP protocol // the RDP protocol
const rdpSubsection = myForm.querySelector("rdp-form"); const rdpSubsection = myForm.querySelector("rdp-form");
if (rdpSubsection === null) { if (rdpSubsection === null) {
throw new Error( throw new Error(
"Unable to find RDP subsection. Is the value of the protocol set to RDP?", "Unable to find RDP subsection. Is the value of the protocol set to RDP?",
); );
} }
for (const { value, querySelector } of Object.values(formFieldEntries)) { for (const { value, querySelector } of Object.values(formFieldEntries)) {
/** @type {HTMLInputElement | null} */ /** @type {HTMLInputElement | null} */
const input = document.querySelector(querySelector); const input = document.querySelector(querySelector);
if (input === null) { if (input === null) {
throw new Error( throw new Error(
`Unable to element that matches query "${querySelector}"`, 'Unable to element that matches query "' + querySelector + '"',
); );
} }
await setInputValue(input, value); await setInputValue(input, value);
} }
}; };
const triggerSubmission = () => { const triggerSubmission = () => {
/** @type {HTMLButtonElement | null} */ /** @type {HTMLButtonElement | null} */
const submitButton = myForm.querySelector( const submitButton = myForm.querySelector(
'p-button[ng-reflect-type="submit"] button', 'p-button[ng-reflect-type="submit"] button',
); );
if (submitButton === null) { if (submitButton === null) {
throw new Error("Unable to find submission button"); throw new Error("Unable to find submission button");
} }
if (submitButton.disabled) { if (submitButton.disabled) {
throw new Error( throw new Error(
"Unable to submit form because submit button is disabled. Are all fields filled out correctly?", "Unable to submit form because submit button is disabled. Are all fields filled out correctly?",
); );
} }
submitButton.click(); submitButton.click();
}; };
setProtocolValue(); setProtocolValue();
await setHostname(); await setHostname();
await setCoderFormFieldValues(); await setCoderFormFieldValues();
triggerSubmission(); triggerSubmission();
} }
/** /**
@ -231,58 +231,58 @@ async function autoSubmitForm(myForm) {
* @returns {void} * @returns {void}
*/ */
function setupFormDetection() { function setupFormDetection() {
/** @type {HTMLFormElement | null} */ /** @type {HTMLFormElement | null} */
let formValueFromLastMutation = null; let formValueFromLastMutation = null;
/** @returns {void} */ /** @returns {void} */
const onDynamicTabMutation = () => { const onDynamicTabMutation = () => {
/** @type {HTMLFormElement | null} */ /** @type {HTMLFormElement | null} */
const latestForm = document.querySelector("web-client-form > form"); const latestForm = document.querySelector("web-client-form > form");
// Only try to auto-fill if we went from having no form on screen to // Only try to auto-fill if we went from having no form on screen to
// having a form on screen. That way, we don't accidentally override the // having a form on screen. That way, we don't accidentally override the
// form if the user is trying to customize values, and this essentially // form if the user is trying to customize values, and this essentially
// makes the script values function as default values // makes the script values function as default values
const mounted = formValueFromLastMutation === null && latestForm !== null; const mounted = formValueFromLastMutation === null && latestForm !== null;
if (mounted) { if (mounted) {
autoSubmitForm(latestForm); autoSubmitForm(latestForm);
} }
formValueFromLastMutation = latestForm; formValueFromLastMutation = latestForm;
}; };
/** @type {number | undefined} */ /** @type {number | undefined} */
let pollingId = undefined; let pollingId = undefined;
/** @returns {void} */ /** @returns {void} */
const checkScreenForDynamicTab = () => { const checkScreenForDynamicTab = () => {
const dynamicTab = document.querySelector("web-client-dynamic-tab"); const dynamicTab = document.querySelector("web-client-dynamic-tab");
// Keep polling until the main content container is on screen // Keep polling until the main content container is on screen
if (dynamicTab === null) { if (dynamicTab === null) {
return; return;
} }
window.clearInterval(pollingId); window.clearInterval(pollingId);
// Call the mutation callback manually, to ensure it runs at least once // Call the mutation callback manually, to ensure it runs at least once
onDynamicTabMutation(); onDynamicTabMutation();
// Having the mutation observer is kind of an extra safety net that isn't // Having the mutation observer is kind of an extra safety net that isn't
// really expected to run that often. Most of the content in the dynamic // really expected to run that often. Most of the content in the dynamic
// tab is being rendered through Canvas, which won't trigger any mutations // tab is being rendered through Canvas, which won't trigger any mutations
// that the observer can detect // that the observer can detect
const dynamicTabObserver = new MutationObserver(onDynamicTabMutation); const dynamicTabObserver = new MutationObserver(onDynamicTabMutation);
dynamicTabObserver.observe(dynamicTab, { dynamicTabObserver.observe(dynamicTab, {
subtree: true, subtree: true,
childList: true, childList: true,
}); });
}; };
pollingId = window.setInterval( pollingId = window.setInterval(
checkScreenForDynamicTab, checkScreenForDynamicTab,
SCREEN_POLL_INTERVAL_MS, SCREEN_POLL_INTERVAL_MS,
); );
} }
/** /**
@ -292,34 +292,34 @@ function setupFormDetection() {
* @returns {void} * @returns {void}
*/ */
function setupAlwaysOnStyles() { function setupAlwaysOnStyles() {
const styleId = "coder-patch--styles-always-on"; const styleId = "coder-patch--styles-always-on";
const existingContainer = document.querySelector(`#${styleId}`); const existingContainer = document.querySelector("#" + styleId);
if (existingContainer) { if (existingContainer) {
return; return;
} }
const styleContainer = document.createElement("style"); const styleContainer = document.createElement("style");
styleContainer.id = styleId; styleContainer.id = styleId;
styleContainer.innerHTML = ` styleContainer.innerHTML = `
/* app-menu corresponds to the sidebar of the default view. */ /* app-menu corresponds to the sidebar of the default view. */
app-menu { app-menu {
display: none !important; display: none !important;
} }
`; `;
document.head.appendChild(styleContainer); document.head.appendChild(styleContainer);
} }
function hideFormForInitialSubmission() { function hideFormForInitialSubmission() {
const styleId = "coder-patch--styles-initial-submission"; const styleId = "coder-patch--styles-initial-submission";
const cssOpacityVariableName = "--coder-opacity-multiplier"; const cssOpacityVariableName = "--coder-opacity-multiplier";
/** @type {HTMLStyleElement | null} */ /** @type {HTMLStyleElement | null} */
let styleContainer = document.querySelector(`#${styleId}`); let styleContainer = document.querySelector("#" + styleId);
if (!styleContainer) { if (!styleContainer) {
styleContainer = document.createElement("style"); styleContainer = document.createElement("style");
styleContainer.id = styleId; styleContainer.id = styleId;
styleContainer.innerHTML = ` styleContainer.innerHTML = `
/* /*
Have to use opacity instead of visibility, because the element still Have to use opacity instead of visibility, because the element still
needs to be interactive via the script so that it can be auto-filled. needs to be interactive via the script so that it can be auto-filled.
@ -350,50 +350,50 @@ function hideFormForInitialSubmission() {
} }
`; `;
document.head.appendChild(styleContainer); document.head.appendChild(styleContainer);
} }
// The root node being undefined should be physically impossible (if it's // The root node being undefined should be physically impossible (if it's
// undefined, the browser itself is busted), but we need to do a type check // undefined, the browser itself is busted), but we need to do a type check
// here so that the rest of the function doesn't need to do type checks over // here so that the rest of the function doesn't need to do type checks over
// and over. // and over.
const rootNode = document.querySelector(":root"); const rootNode = document.querySelector(":root");
if (!(rootNode instanceof HTMLHtmlElement)) { if (!(rootNode instanceof HTMLHtmlElement)) {
// Remove the container entirely because if the browser is busted, who knows // Remove the container entirely because if the browser is busted, who knows
// if the CSS variables can be applied correctly. Better to have something // if the CSS variables can be applied correctly. Better to have something
// be a bit more ugly/painful to use, than have it be impossible to use // be a bit more ugly/painful to use, than have it be impossible to use
styleContainer.remove(); styleContainer.remove();
return; return;
} }
// It's safe to make the form visible preemptively because Devolutions // It's safe to make the form visible preemptively because Devolutions
// outputs the Windows view through an HTML canvas that it overlays on top // outputs the Windows view through an HTML canvas that it overlays on top
// of the rest of the app. Even if the form isn't hidden at the style level, // of the rest of the app. Even if the form isn't hidden at the style level,
// it will still be covered up. // it will still be covered up.
const restoreOpacity = () => { const restoreOpacity = () => {
rootNode.style.setProperty(cssOpacityVariableName, "1"); rootNode.style.setProperty(cssOpacityVariableName, "1");
}; };
// If this file gets more complicated, it might make sense to set up the // If this file gets more complicated, it might make sense to set up the
// timeout and event listener so that if one triggers, it cancels the other, // timeout and event listener so that if one triggers, it cancels the other,
// but having restoreOpacity run more than once is a no-op for right now. // but having restoreOpacity run more than once is a no-op for right now.
// Not a big deal if these don't get cleaned up. // Not a big deal if these don't get cleaned up.
// Have the form automatically reappear no matter what, so that if something // Have the form automatically reappear no matter what, so that if something
// does break, the user isn't left out to dry // does break, the user isn't left out to dry
window.setTimeout(restoreOpacity, 5_000); window.setTimeout(restoreOpacity, 5_000);
/** @type {HTMLFormElement | null} */ /** @type {HTMLFormElement | null} */
const form = document.querySelector("web-client-form > form"); const form = document.querySelector("web-client-form > form");
form?.addEventListener( form?.addEventListener(
"submit", "submit",
() => { () => {
// Not restoring opacity right away just to give the HTML canvas a little // Not restoring opacity right away just to give the HTML canvas a little
// bit of time to get spun up and cover up the main form // bit of time to get spun up and cover up the main form
window.setTimeout(restoreOpacity, 1_000); window.setTimeout(restoreOpacity, 1_000);
}, },
{ once: true }, { once: true },
); );
} }
// Always safe to call these immediately because even if the Angular app isn't // Always safe to call these immediately because even if the Angular app isn't
@ -403,7 +403,7 @@ setupAlwaysOnStyles();
hideFormForInitialSubmission(); hideFormForInitialSubmission();
if (document.readyState === "loading") { if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", setupFormDetection); document.addEventListener("DOMContentLoaded", setupFormDetection);
} else { } else {
setupFormDetection(); setupFormDetection();
} }

Loading…
Cancel
Save