Add managing preferredFor from editing
All checks were successful
Build & Test / build-run (push) Successful in 45s
All checks were successful
Build & Test / build-run (push) Successful in 45s
This commit is contained in:
parent
68f54b341b
commit
becaf79690
5 changed files with 97 additions and 7 deletions
|
@ -39,6 +39,7 @@ export class AddInstanceFlow {
|
||||||
hostSecure: secure,
|
hostSecure: secure,
|
||||||
software: "",
|
software: "",
|
||||||
iconURL: null,
|
iconURL: null,
|
||||||
|
preferredFor: []
|
||||||
};
|
};
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
|
@ -101,9 +101,9 @@ Unchecking this is not recommended, and this option only exists for exceptional
|
||||||
<br>
|
<br>
|
||||||
<label for="defaultsList">Default option for:</label><br>
|
<label for="defaultsList">Default option for:</label><br>
|
||||||
<select id="defaultsList" class="full-width" multiple>
|
<select id="defaultsList" class="full-width" multiple>
|
||||||
<option value="" disabled>(None, use the "Redirect always" button to set!)</option>
|
<option id="noDefaults" value="" disabled>(None, use the "Redirect always" button to set!)</option>
|
||||||
</select>
|
</select>
|
||||||
<button id="removeDefaults" disabled>Remove</button>
|
<button id="removeDefaults" type="button" disabled>Remove</button>
|
||||||
<br><br>
|
<br><br>
|
||||||
<button type="submit">OK</button>
|
<button type="submit">OK</button>
|
||||||
<button type="reset" class="close">Cancel</button>
|
<button type="reset" class="close">Cancel</button>
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
import { parseHost } from "./add_an_instance.mjs";
|
|
||||||
import { AddInstanceFlow } from "./add_instance_flow.mjs";
|
import { AddInstanceFlow } from "./add_instance_flow.mjs";
|
||||||
import { dialogDetailsFromInstance, dialogDetailsToInstance, InstanceDetailsDialog, InstanceDetailsDialogData } from "./confirm_instance_details.mjs";
|
import { dialogDetailsFromInstance, dialogDetailsToInstance, InstanceDetailsDialog } from "./confirm_instance_details.mjs";
|
||||||
import { findButtonOrFail, findDialogOrFail, findOlOrFail } from "./dom.mjs";
|
import { findButtonOrFail, findDialogOrFail, findOlOrFail } from "./dom.mjs";
|
||||||
import storageManager, { Instance } from "./storage_manager.mjs";
|
import storageManager, { Instance } from "./storage_manager.mjs";
|
||||||
|
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
import { parseHost } from "./add_an_instance.mjs";
|
import { parseHost } from "./add_an_instance.mjs";
|
||||||
import { FormDialog, ONCE } from "./dialog.mjs";
|
import { FormDialog, ONCE } from "./dialog.mjs";
|
||||||
import { findButtonOrFail, findFormOrFail, findImageOrFail, findInputOrFail, findSelectOrFail } from "./dom.mjs";
|
import { findButtonOrFail, findFormOrFail, findImageOrFail, findInputOrFail, findOptionOrFail, findSelectOrFail } from "./dom.mjs";
|
||||||
import knownSoftware from "./known_software.mjs";
|
import knownSoftware from "./known_software.mjs";
|
||||||
import { Instance } from "./storage_manager.mjs";
|
import { Instance } from "./storage_manager.mjs";
|
||||||
|
|
||||||
|
@ -17,7 +17,8 @@ export type InstanceDetailsDialogData = {
|
||||||
host: string,
|
host: string,
|
||||||
hostSecure: boolean,
|
hostSecure: boolean,
|
||||||
software: string,
|
software: string,
|
||||||
iconURL: string | null
|
iconURL: string | null,
|
||||||
|
preferredFor: string[],
|
||||||
};
|
};
|
||||||
|
|
||||||
export function dialogDetailsFromInstance(instance: Instance): InstanceDetailsDialogData {
|
export function dialogDetailsFromInstance(instance: Instance): InstanceDetailsDialogData {
|
||||||
|
@ -27,7 +28,8 @@ export function dialogDetailsFromInstance(instance: Instance): InstanceDetailsDi
|
||||||
host: host.host,
|
host: host.host,
|
||||||
hostSecure: host.secure,
|
hostSecure: host.secure,
|
||||||
software: instance.software,
|
software: instance.software,
|
||||||
iconURL: instance.iconURL ?? null
|
iconURL: instance.iconURL ?? null,
|
||||||
|
preferredFor: instance.preferredFor,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -36,6 +38,7 @@ export function dialogDetailsToInstance(data: InstanceDetailsDialogData, instanc
|
||||||
instance.origin = mergeHost(data.host, data.hostSecure);
|
instance.origin = mergeHost(data.host, data.hostSecure);
|
||||||
instance.software = data.software;
|
instance.software = data.software;
|
||||||
instance.iconURL = data.iconURL ?? undefined;
|
instance.iconURL = data.iconURL ?? undefined;
|
||||||
|
instance.preferredFor = data.preferredFor;
|
||||||
return instance as Instance;
|
return instance as Instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -46,6 +49,11 @@ export class InstanceDetailsDialog extends FormDialog {
|
||||||
protected instanceSoftware: HTMLSelectElement;
|
protected instanceSoftware: HTMLSelectElement;
|
||||||
protected instanceIcon: HTMLImageElement;
|
protected instanceIcon: HTMLImageElement;
|
||||||
protected closeButton: HTMLButtonElement;
|
protected closeButton: HTMLButtonElement;
|
||||||
|
protected defaultsList?: {
|
||||||
|
list: HTMLSelectElement,
|
||||||
|
removeButton: HTMLButtonElement,
|
||||||
|
noDefaultsOption: HTMLOptionElement,
|
||||||
|
};
|
||||||
|
|
||||||
constructor(dialog: HTMLDialogElement, initializeDOM: boolean = true) {
|
constructor(dialog: HTMLDialogElement, initializeDOM: boolean = true) {
|
||||||
super(dialog, findFormOrFail(dialog, ".instanceDetailsForm"));
|
super(dialog, findFormOrFail(dialog, ".instanceDetailsForm"));
|
||||||
|
@ -56,6 +64,15 @@ export class InstanceDetailsDialog extends FormDialog {
|
||||||
this.instanceSoftware = findSelectOrFail(this.form, "#instanceSoftware");
|
this.instanceSoftware = findSelectOrFail(this.form, "#instanceSoftware");
|
||||||
this.instanceIcon = findImageOrFail(this.form, "#instanceIcon");
|
this.instanceIcon = findImageOrFail(this.form, "#instanceIcon");
|
||||||
this.closeButton = findButtonOrFail(this.form, ".close");
|
this.closeButton = findButtonOrFail(this.form, ".close");
|
||||||
|
try {
|
||||||
|
this.defaultsList = {
|
||||||
|
list: findSelectOrFail(this.form, "#defaultsList"),
|
||||||
|
removeButton: findButtonOrFail(this.form, "#removeDefaults"),
|
||||||
|
noDefaultsOption: findOptionOrFail(this.form, "#noDefaults"),
|
||||||
|
};
|
||||||
|
} catch {
|
||||||
|
this.defaultsList = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
if (initializeDOM) this.initializeDOM();
|
if (initializeDOM) this.initializeDOM();
|
||||||
}
|
}
|
||||||
|
@ -71,6 +88,70 @@ export class InstanceDetailsDialog extends FormDialog {
|
||||||
this.instanceIcon.src = blankImage;
|
this.instanceIcon.src = blankImage;
|
||||||
|
|
||||||
this.closeButton.addEventListener("click", e => this.close());
|
this.closeButton.addEventListener("click", e => this.close());
|
||||||
|
|
||||||
|
if (this.defaultsList) {
|
||||||
|
this.defaultsList.list.addEventListener("change", e => this.#handleListSelectionChange());
|
||||||
|
this.defaultsList.removeButton.addEventListener("click", e => this.#removeSelectedListOptions());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#getRemainingListOptions(): string[] {
|
||||||
|
if (!this.defaultsList) return [];
|
||||||
|
|
||||||
|
const items: string[] = [];
|
||||||
|
|
||||||
|
for (const option of this.defaultsList.list.options) {
|
||||||
|
if (option == this.defaultsList.noDefaultsOption) continue;
|
||||||
|
items.push(option.value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
#removeSelectedListOptions() {
|
||||||
|
if (!this.defaultsList) return;
|
||||||
|
|
||||||
|
// Copy using spread because this breaks when the list changes mid-iteration
|
||||||
|
for (const option of [...this.defaultsList.list.selectedOptions]) {
|
||||||
|
option.remove();
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#handleListChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
#populateDefaultsList(items: string[]) {
|
||||||
|
if (!this.defaultsList) return;
|
||||||
|
|
||||||
|
while (this.defaultsList.list.children.length > 1)
|
||||||
|
this.defaultsList.list.removeChild(this.defaultsList.list.lastChild!);
|
||||||
|
|
||||||
|
for (const item of items) {
|
||||||
|
const option = document.createElement("option");
|
||||||
|
option.value = item;
|
||||||
|
option.innerText = knownSoftware.software[item]?.name ?? knownSoftware.groups[item]?.name ?? item;
|
||||||
|
this.defaultsList.list.appendChild(option);
|
||||||
|
}
|
||||||
|
|
||||||
|
this.#handleListChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleListChange() {
|
||||||
|
this.#handleListEmpty();
|
||||||
|
this.#handleListSelectionChange();
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleListSelectionChange() {
|
||||||
|
if (!this.defaultsList) return;
|
||||||
|
this.defaultsList.removeButton.disabled = this.defaultsList.list.selectedOptions.length === 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#handleListEmpty() {
|
||||||
|
if (!this.defaultsList) return;
|
||||||
|
if (this.defaultsList.list.children.length == 1) {
|
||||||
|
this.defaultsList.noDefaultsOption.removeAttribute("hidden");
|
||||||
|
} else {
|
||||||
|
this.defaultsList.noDefaultsOption.setAttribute("hidden", "");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleSubmit(data: InstanceDetailsDialogData, resolve: (data: InstanceDetailsDialogData) => void) {
|
#handleSubmit(data: InstanceDetailsDialogData, resolve: (data: InstanceDetailsDialogData) => void) {
|
||||||
|
@ -79,6 +160,7 @@ export class InstanceDetailsDialog extends FormDialog {
|
||||||
data.host = this.instanceHost.value;
|
data.host = this.instanceHost.value;
|
||||||
data.hostSecure = this.instanceHostSecure.checked;
|
data.hostSecure = this.instanceHostSecure.checked;
|
||||||
data.software = this.instanceSoftware.value;
|
data.software = this.instanceSoftware.value;
|
||||||
|
data.preferredFor = this.#getRemainingListOptions();
|
||||||
|
|
||||||
resolve(data);
|
resolve(data);
|
||||||
this.close();
|
this.close();
|
||||||
|
@ -91,6 +173,7 @@ export class InstanceDetailsDialog extends FormDialog {
|
||||||
this.instanceHostSecure.checked = data.hostSecure;
|
this.instanceHostSecure.checked = data.hostSecure;
|
||||||
this.instanceSoftware.value = data.software;
|
this.instanceSoftware.value = data.software;
|
||||||
this.instanceIcon.src = data.iconURL ?? blankImage;
|
this.instanceIcon.src = data.iconURL ?? blankImage;
|
||||||
|
this.#populateDefaultsList(data.preferredFor);
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
this.cancelOnceClosed(reject);
|
this.cancelOnceClosed(reject);
|
||||||
|
|
|
@ -1,6 +1,13 @@
|
||||||
// I would've LOVED to use generics for this but unfortunately that's not possible.
|
// I would've LOVED to use generics for this but unfortunately that's not possible.
|
||||||
// Type safety, but at what cost... >~< thanks TypeScript
|
// Type safety, but at what cost... >~< thanks TypeScript
|
||||||
|
|
||||||
|
export function findOptionOrFail(on: Element, selector: string): HTMLOptionElement {
|
||||||
|
const element = on.querySelector(selector);
|
||||||
|
if (!(element instanceof HTMLOptionElement))
|
||||||
|
throw new Error(`${selector} isn't an option`);
|
||||||
|
return element;
|
||||||
|
}
|
||||||
|
|
||||||
export function findOlOrFail(on: Element, selector: string): HTMLOListElement {
|
export function findOlOrFail(on: Element, selector: string): HTMLOListElement {
|
||||||
const element = on.querySelector(selector);
|
const element = on.querySelector(selector);
|
||||||
if (!(element instanceof HTMLOListElement))
|
if (!(element instanceof HTMLOListElement))
|
||||||
|
|
Loading…
Add table
Reference in a new issue