Rework typescript to use modules
web UI now uses modules, and relies less on bodge to make things work. Also fixes an issue where invites where "failed to send to xx" appeared in invite form.
This commit is contained in:
+44
-184
@@ -1,9 +1,28 @@
|
||||
var config: Object = {};
|
||||
var modifiedConfig: Object = {};
|
||||
import { _post, _get, _delete, rmAttr, addAttr } from "./modules/common.js";
|
||||
import { generateInvites } from "./modules/invites.js";
|
||||
import { populateRadios } from "./modules/accounts.js";
|
||||
import { Focus, Unfocus } from "./modules/admin.js";
|
||||
import { showSetting, populateProfiles } from "./modules/settings.js";
|
||||
|
||||
interface aWindow extends Window {
|
||||
setDefaultProfile(name: string): void;
|
||||
deleteProfile(name: string): void;
|
||||
createProfile(): void;
|
||||
showSetting(id: string, runBefore?: () => void): void;
|
||||
config: Object;
|
||||
modifiedConfig: Object;
|
||||
}
|
||||
|
||||
declare var window: aWindow;
|
||||
|
||||
window.config = {};
|
||||
window.modifiedConfig = {};
|
||||
|
||||
window.showSetting = showSetting;
|
||||
|
||||
function sendConfig(restart?: boolean): void {
|
||||
modifiedConfig["restart-program"] = restart;
|
||||
_post("/config", modifiedConfig, function (): void {
|
||||
window.modifiedConfig["restart-program"] = restart;
|
||||
_post("/config", window.modifiedConfig, function (): void {
|
||||
if (this.readyState == 4) {
|
||||
const save = document.getElementById("settingsSave") as HTMLButtonElement
|
||||
if (this.status == 200 || this.status == 204) {
|
||||
@@ -19,159 +38,22 @@ function sendConfig(restart?: boolean): void {
|
||||
save.textContent = "Save";
|
||||
}
|
||||
if (restart) {
|
||||
refreshModal.show();
|
||||
window.Modals.refresh.show();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
(document.getElementById('openAbout') as HTMLButtonElement).onclick = (): void => {
|
||||
aboutModal.show();
|
||||
window.Modals.about.show();
|
||||
};
|
||||
|
||||
const openSettings = (settingsList: HTMLElement, settingsContent: HTMLElement, callback?: () => void): void => _get("/config", null, function (): void {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
settingsList.textContent = '';
|
||||
config = this.response;
|
||||
for (const i in config["order"]) {
|
||||
const section: string = config["order"][i]
|
||||
const sectionCollapse = document.createElement('div') as HTMLDivElement;
|
||||
Unfocus(sectionCollapse);
|
||||
sectionCollapse.id = section;
|
||||
|
||||
const title: string = config[section]["meta"]["name"];
|
||||
const description: string = config[section]["meta"]["description"];
|
||||
const entryListID: string = `${section}_entryList`;
|
||||
// const footerID: string = `${section}_footer`;
|
||||
|
||||
sectionCollapse.innerHTML = `
|
||||
<div class="card card-body">
|
||||
<small class="text-muted">${description}</small>
|
||||
<div class="${entryListID}">
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
for (const x in config[section]["order"]) {
|
||||
const entry: string = config[section]["order"][x];
|
||||
if (entry == "meta") {
|
||||
continue;
|
||||
}
|
||||
let entryName: string = config[section][entry]["name"];
|
||||
let required = false;
|
||||
if (config[section][entry]["required"]) {
|
||||
entryName += ` <sup class="text-danger">*</sup>`;
|
||||
required = true;
|
||||
}
|
||||
if (config[section][entry]["requires_restart"]) {
|
||||
entryName += ` <sup class="text-danger">R</sup>`;
|
||||
}
|
||||
if ("description" in config[section][entry]) {
|
||||
entryName +=`
|
||||
<a class="text-muted" href="#" data-toggle="tooltip" data-placement="right" title="${config[section][entry]['description']}"><i class="fa fa-question-circle-o"></i></a>
|
||||
`;
|
||||
}
|
||||
const entryValue: boolean | string = config[section][entry]["value"];
|
||||
const entryType: string = config[section][entry]["type"];
|
||||
const entryGroup = document.createElement('div');
|
||||
if (entryType == "bool") {
|
||||
entryGroup.classList.add("form-check");
|
||||
entryGroup.innerHTML = `
|
||||
<input class="form-check-input" type="checkbox" value="" id="${section}_${entry}" ${(entryValue as boolean) ? 'checked': ''} ${required ? 'required' : ''}>
|
||||
<label class="form-check-label" for="${section}_${entry}">${entryName}</label>
|
||||
`;
|
||||
(entryGroup.querySelector('input[type=checkbox]') as HTMLInputElement).onclick = function (): void {
|
||||
const me = this as HTMLInputElement;
|
||||
for (const y in config["order"]) {
|
||||
const sect: string = config["order"][y];
|
||||
for (const z in config[sect]["order"]) {
|
||||
const ent: string = config[sect]["order"][z];
|
||||
if (`${sect}_${config[sect][ent]['depends_true']}` == me.id) {
|
||||
(document.getElementById(`${sect}_${ent}`) as HTMLInputElement).disabled = !(me.checked);
|
||||
} else if (`${sect}_${config[sect][ent]['depends_false']}` == me.id) {
|
||||
(document.getElementById(`${sect}_${ent}`) as HTMLInputElement).disabled = me.checked;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
} else if ((entryType == 'text') || (entryType == 'email') || (entryType == 'password') || (entryType == 'number')) {
|
||||
entryGroup.classList.add("form-group");
|
||||
entryGroup.innerHTML = `
|
||||
<label for="${section}_${entry}">${entryName}</label>
|
||||
<input type="${entryType}" class="form-control" id="${section}_${entry}" aria-describedby="${entry}" value="${entryValue}" ${required ? 'required' : ''}>
|
||||
`;
|
||||
} else if (entryType == 'select') {
|
||||
entryGroup.classList.add("form-group");
|
||||
const entryOptions: Array<string> = config[section][entry]["options"];
|
||||
let innerGroup = `
|
||||
<label for="${section}_${entry}">${entryName}</label>
|
||||
<select class="form-control" id="${section}_${entry}" ${required ? 'required' : ''}>
|
||||
`;
|
||||
for (const z in entryOptions) {
|
||||
const entryOption = entryOptions[z];
|
||||
let selected: boolean = (entryOption == entryValue);
|
||||
innerGroup += `
|
||||
<option value="${entryOption}" ${selected ? 'selected' : ''}>${entryOption}</option>
|
||||
`;
|
||||
}
|
||||
innerGroup += `</select>`;
|
||||
entryGroup.innerHTML = innerGroup;
|
||||
}
|
||||
sectionCollapse.getElementsByClassName(entryListID)[0].appendChild(entryGroup);
|
||||
}
|
||||
|
||||
settingsList.innerHTML += `
|
||||
<button type="button" class="list-group-item list-group-item-action" id="${section}_button" onclick="showSetting('${section}')">${title}</button>
|
||||
`;
|
||||
settingsContent.appendChild(sectionCollapse);
|
||||
}
|
||||
if (callback) {
|
||||
callback();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
interface Profile {
|
||||
Admin: boolean;
|
||||
LibraryAccess: string;
|
||||
FromUser: string;
|
||||
}
|
||||
|
||||
(document.getElementById('profiles_button') as HTMLButtonElement).onclick = (): void => showSetting("profiles", populateProfiles);
|
||||
|
||||
const populateProfiles = (noTable?: boolean): void => _get("/profiles", null, function (): void {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
const profileList = document.getElementById('profileList');
|
||||
profileList.textContent = '';
|
||||
availableProfiles = [this.response["default_profile"]];
|
||||
for (let name in this.response["profiles"]) {
|
||||
if (name != availableProfiles[0]) {
|
||||
availableProfiles.push(name);
|
||||
}
|
||||
const reqProfile = this.response["profiles"][name];
|
||||
if (!noTable && name != "default_profile") {
|
||||
const profile: Profile = {
|
||||
Admin: reqProfile["admin"],
|
||||
LibraryAccess: reqProfile["libraries"],
|
||||
FromUser: reqProfile["fromUser"]
|
||||
};
|
||||
profileList.innerHTML += `
|
||||
<td nowrap="nowrap" class="align-middle"><strong>${name}</strong></td>
|
||||
<td nowrap="nowrap" class="align-middle"><input class="${(bsVersion == 5) ? "form-check-input" : ""}" type="radio" name="defaultProfile" onclick="setDefaultProfile('${name}')" ${(name == availableProfiles[0]) ? "checked" : ""}></td>
|
||||
<td nowrap="nowrap" class="align-middle">${profile.FromUser}</td>
|
||||
<td nowrap="nowrap" class="align-middle">${profile.Admin ? "Yes" : "No"}</td>
|
||||
<td nowrap="nowrap" class="align-middle">${profile.LibraryAccess}</td>
|
||||
<td nowrap="nowrap" class="align-middle"><button class="btn btn-outline-danger" id="defaultProfile_${name}" onclick="deleteProfile('${name}')">Delete</button></td>
|
||||
`;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
const setDefaultProfile = (name: string): void => _post("/profiles/default", { "name": name }, function (): void {
|
||||
window.setDefaultProfile = (name: string): void => _post("/profiles/default", { "name": name }, function (): void {
|
||||
if (this.readyState == 4) {
|
||||
if (this.status != 200) {
|
||||
(document.getElementById(`defaultProfile_${availableProfiles[0]}`) as HTMLInputElement).checked = true;
|
||||
(document.getElementById(`defaultProfile_${window.availableProfiles[0]}`) as HTMLInputElement).checked = true;
|
||||
(document.getElementById(`defaultProfile_${name}`) as HTMLInputElement).checked = false;
|
||||
} else {
|
||||
generateInvites();
|
||||
@@ -179,7 +61,7 @@ const setDefaultProfile = (name: string): void => _post("/profiles/default", { "
|
||||
}
|
||||
});
|
||||
|
||||
const deleteProfile = (name: string): void => _delete("/profiles", { "name": name }, function (): void {
|
||||
window.deleteProfile = (name: string): void => _delete("/profiles", { "name": name }, function (): void {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
populateProfiles();
|
||||
}
|
||||
@@ -187,7 +69,7 @@ const deleteProfile = (name: string): void => _delete("/profiles", { "name": nam
|
||||
|
||||
const createProfile = (): void => _get("/users", null, function (): void {
|
||||
if (this.readyState == 4 && this.status == 200) {
|
||||
jfUsers = this.response["users"];
|
||||
window.jfUsers = this.response["users"];
|
||||
populateRadios();
|
||||
const submitButton = document.getElementById('storeDefaults') as HTMLButtonElement;
|
||||
submitButton.disabled = false;
|
||||
@@ -205,10 +87,12 @@ const createProfile = (): void => _get("/users", null, function (): void {
|
||||
Focus(document.getElementById('newProfileBox'));
|
||||
(document.getElementById('newProfileName') as HTMLInputElement).value = '';
|
||||
Focus(document.getElementById('defaultUserRadiosBox'));
|
||||
userDefaultsModal.show();
|
||||
window.Modals.userDefaults.show();
|
||||
}
|
||||
});
|
||||
|
||||
window.createProfile = createProfile;
|
||||
|
||||
function storeProfile(): void {
|
||||
this.disabled = true;
|
||||
this.innerHTML =
|
||||
@@ -239,7 +123,7 @@ function storeProfile(): void {
|
||||
addAttr(button, "btn-primary");
|
||||
rmAttr(button, "btn-success");
|
||||
button.disabled = false;
|
||||
userDefaultsModal.hide();
|
||||
window.Modals.userDefaults.hide();
|
||||
|
||||
}, 1000);
|
||||
populateProfiles();
|
||||
@@ -265,41 +149,17 @@ function storeProfile(): void {
|
||||
});
|
||||
}
|
||||
|
||||
function showSetting(id: string, runBefore?: () => void): void {
|
||||
const els = document.getElementById('settingsLeft').querySelectorAll("button[type=button]:not(.static)") as NodeListOf<HTMLButtonElement>;
|
||||
for (let i = 0; i < els.length; i++) {
|
||||
const el = els[i];
|
||||
if (el.id != `${id}_button`) {
|
||||
rmAttr(el, "active");
|
||||
}
|
||||
const sectEl = document.getElementById(el.id.replace("_button", ""));
|
||||
if (sectEl.id != id) {
|
||||
Unfocus(sectEl);
|
||||
}
|
||||
}
|
||||
addAttr(document.getElementById(`${id}_button`), "active");
|
||||
const section = document.getElementById(id);
|
||||
if (runBefore) {
|
||||
runBefore();
|
||||
}
|
||||
Focus(section);
|
||||
if (screen.width <= 1100) {
|
||||
// ugly
|
||||
setTimeout((): void => section.scrollIntoView(<ScrollIntoViewOptions>{ block: "center", behavior: "smooth" }), 200);
|
||||
}
|
||||
}
|
||||
|
||||
// (document.getElementById('openSettings') as HTMLButtonElement).onclick = (): void => openSettings(document.getElementById('settingsList'), document.getElementById('settingsList'), (): void => settingsModal.show());
|
||||
|
||||
(document.getElementById('settingsSave') as HTMLButtonElement).onclick = function (): void {
|
||||
modifiedConfig = {};
|
||||
window.modifiedConfig = {};
|
||||
const save = this as HTMLButtonElement;
|
||||
let restartSettingsChanged = false;
|
||||
let settingsChanged = false;
|
||||
for (const i in config["order"]) {
|
||||
const section = config["order"][i];
|
||||
for (const x in config[section]["order"]) {
|
||||
const entry = config[section]["order"][x];
|
||||
for (const i in window.config["order"]) {
|
||||
const section = window.config["order"][i];
|
||||
for (const x in window.config[section]["order"]) {
|
||||
const entry = window.config[section]["order"][x];
|
||||
if (entry == "meta") {
|
||||
continue;
|
||||
}
|
||||
@@ -311,13 +171,13 @@ function showSetting(id: string, runBefore?: () => void): void {
|
||||
} else {
|
||||
val = el.value.toString();
|
||||
}
|
||||
if (val != config[section][entry]["value"].toString()) {
|
||||
if (!(section in modifiedConfig)) {
|
||||
modifiedConfig[section] = {};
|
||||
if (val != window.config[section][entry]["value"].toString()) {
|
||||
if (!(section in window.modifiedConfig)) {
|
||||
window.modifiedConfig[section] = {};
|
||||
}
|
||||
modifiedConfig[section][entry] = val;
|
||||
window.modifiedConfig[section][entry] = val;
|
||||
settingsChanged = true;
|
||||
if (config[section][entry]["requires_restart"]) {
|
||||
if (window.config[section][entry]["requires_restart"]) {
|
||||
restartSettingsChanged = true;
|
||||
}
|
||||
}
|
||||
@@ -333,7 +193,7 @@ function showSetting(id: string, runBefore?: () => void): void {
|
||||
if (restartButton) {
|
||||
restartButton.onclick = (): void => sendConfig(true);
|
||||
}
|
||||
restartModal.show();
|
||||
window.Modals.restart.show();
|
||||
} else if (settingsChanged) {
|
||||
save.innerHTML = spinnerHTML;
|
||||
sendConfig();
|
||||
|
||||
Reference in New Issue
Block a user