Add confirm instance details dialog
This commit is contained in:
parent
790a9f4a7d
commit
0f935f5453
5 changed files with 192 additions and 10 deletions
|
@ -10,7 +10,14 @@ export function parseHost(host: string): { host: string, secure: boolean } | nul
|
|||
};
|
||||
}
|
||||
|
||||
export function initializeAddInstanceDialog(dialog: HTMLDialogElement): {
|
||||
export function initializeAddInstanceDialog(
|
||||
dialog: HTMLDialogElement,
|
||||
callback: (
|
||||
host: string,
|
||||
secure: boolean,
|
||||
autoQueryMetadata: boolean,
|
||||
) => void
|
||||
): {
|
||||
showAddInstanceDialog: () => void,
|
||||
hideAddInstanceDialog: () => void,
|
||||
} {
|
||||
|
@ -32,12 +39,14 @@ export function initializeAddInstanceDialog(dialog: HTMLDialogElement): {
|
|||
instanceHost.setCustomValidity("");
|
||||
});
|
||||
|
||||
form.addEventListener("submit", async e => {
|
||||
const autoQueryMetadata = form.querySelector("#autoQueryMetadata");
|
||||
if (!(autoQueryMetadata instanceof HTMLInputElement))
|
||||
throw new Error("#autoQueryMetadata isn't an input");
|
||||
|
||||
form.addEventListener("submit", e => {
|
||||
// A sane browser doesn't allow for submitting the form if the above validation fails
|
||||
const { host, secure } = parseHost(instanceHost.value)!;
|
||||
console.log(
|
||||
await fetch(`/api/instance_info/${secure}/${encodeURI(host)}`).then(r => r.json())
|
||||
);
|
||||
callback(host, secure, autoQueryMetadata.checked);
|
||||
form.reset();
|
||||
});
|
||||
|
||||
|
|
70
static/confirm_instance_details.mts
Normal file
70
static/confirm_instance_details.mts
Normal file
|
@ -0,0 +1,70 @@
|
|||
// This file handles the "Confirm instance details" dialog
|
||||
|
||||
import knownSoftware from "./known_software.mjs";
|
||||
|
||||
const blankImage = "data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==";
|
||||
|
||||
export function initializeInstanceDetailsDialog(dialog: HTMLDialogElement): {
|
||||
showInstanceDetailsDialog: () => void,
|
||||
hideInstanceDetailsDialog: () => void,
|
||||
populateInstanceDetailsDialog: (
|
||||
instanceNameValue: string,
|
||||
instanceSoftwareValue: string,
|
||||
instanceIconValue: string | null
|
||||
) => void,
|
||||
} {
|
||||
const showInstanceDetailsDialog = () => dialog.showModal();
|
||||
const hideInstanceDetailsDialog = () => dialog.close();
|
||||
|
||||
const form = dialog.querySelector(".instanceDetailsForm");
|
||||
if (!(form instanceof HTMLFormElement))
|
||||
throw new Error(".instanceDetailsForm isn't a form");
|
||||
|
||||
const instanceName = form.querySelector("#instanceName");
|
||||
if (!(instanceName instanceof HTMLInputElement))
|
||||
throw new Error("#instanceName isn't an input");
|
||||
|
||||
const instanceSoftware = form.querySelector("#instanceSoftware");
|
||||
if (!(instanceSoftware instanceof HTMLSelectElement))
|
||||
throw new Error("#instanceSoftware isn't a select");
|
||||
|
||||
for (const [name, software] of Object.entries(knownSoftware.software)) {
|
||||
const option = new Option(software.name, name);
|
||||
instanceSoftware.appendChild(option);
|
||||
}
|
||||
|
||||
const instanceIcon = form.querySelector("#instanceIcon");
|
||||
if (!(instanceIcon instanceof HTMLImageElement))
|
||||
throw new Error("#instanceIcon isn't an image");
|
||||
|
||||
instanceIcon.src = blankImage;
|
||||
|
||||
const populateInstanceDetailsDialog = (
|
||||
instanceNameValue: string,
|
||||
instanceSoftwareValue: string,
|
||||
instanceIconValue: string | null
|
||||
) => {
|
||||
instanceName.value = instanceNameValue;
|
||||
instanceSoftware.value = instanceSoftwareValue;
|
||||
instanceIcon.src = instanceIconValue ?? blankImage;
|
||||
};
|
||||
|
||||
form.addEventListener("submit", e => {
|
||||
form.reset();
|
||||
});
|
||||
|
||||
const closeButton = form.querySelector(".close");
|
||||
if (!(closeButton instanceof HTMLButtonElement))
|
||||
throw new Error(".close isn't a button");
|
||||
|
||||
closeButton.addEventListener("click", e => {
|
||||
instanceIcon.src = blankImage;
|
||||
hideInstanceDetailsDialog();
|
||||
});
|
||||
|
||||
return {
|
||||
showInstanceDetailsDialog,
|
||||
hideInstanceDetailsDialog,
|
||||
populateInstanceDetailsDialog
|
||||
};
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
:root {
|
||||
--red: #cb0b0b;
|
||||
--blue: #2081c3;
|
||||
--transparent-black: #0008;
|
||||
--large: 2em;
|
||||
--medium: 1em;
|
||||
}
|
||||
|
@ -25,7 +26,7 @@ dialog {
|
|||
|
||||
dialog::backdrop {
|
||||
backdrop-filter: blur(5px);
|
||||
background-color: #0008;
|
||||
background-color: var(--transparent-black);
|
||||
transition: background-color 0.125s ease-out;
|
||||
|
||||
@starting-style {
|
||||
|
@ -65,6 +66,16 @@ abbr[title] {
|
|||
flex-direction: row;
|
||||
}
|
||||
|
||||
.flex-row-reverse {
|
||||
display: flex;
|
||||
flex-direction: row-reverse;
|
||||
}
|
||||
|
||||
.flex-column-reverse {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
}
|
||||
|
||||
.half-width {
|
||||
min-width: 50%;
|
||||
}
|
||||
|
@ -73,8 +84,12 @@ abbr[title] {
|
|||
min-height: 50%;
|
||||
}
|
||||
|
||||
.full-height {
|
||||
min-height: 100%;
|
||||
}
|
||||
|
||||
.separator-bottom {
|
||||
border-bottom: solid 1px #0008;
|
||||
border-bottom: solid 1px var(--transparent-black);
|
||||
}
|
||||
|
||||
.margin-auto-top {
|
||||
|
@ -83,4 +98,25 @@ abbr[title] {
|
|||
|
||||
.margin-large-bottom {
|
||||
margin-bottom: var(--large);
|
||||
}
|
||||
|
||||
.square {
|
||||
aspect-ratio: 1;
|
||||
}
|
||||
|
||||
.iconContainer {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
padding: 1px;
|
||||
border: solid 1px var(--transparent-black);
|
||||
}
|
||||
|
||||
.icon {
|
||||
position: relative;
|
||||
max-width: 64px;
|
||||
max-height: 64px;
|
||||
margin: auto;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
translate: -50% -50%;
|
||||
}
|
|
@ -58,6 +58,38 @@ We do not track or save any requests or data.">
|
|||
<button type="reset" class="close">Cancel</button>
|
||||
</form>
|
||||
</dialog>
|
||||
<dialog id="instanceDetails">
|
||||
<h1>Confirm instance details</h1>
|
||||
<form method="dialog" class="instanceDetailsForm">
|
||||
<div class="flex-row">
|
||||
<div class="half-width">
|
||||
<label for="instanceName">Instance name</label>
|
||||
<br>
|
||||
<input id="instanceName" type="text" name="instanceName" />
|
||||
<br><br>
|
||||
<label for="instanceSoftware">Instance software</label>
|
||||
<br>
|
||||
<select id="instanceSoftware" type="text" name="instanceSoftware" required>
|
||||
<option value="">(Please select)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="half-width flex-row-reverse">
|
||||
<div class="full-height flex-column-reverse">
|
||||
<div>
|
||||
<label for="iconContainer">Instance icon</label>
|
||||
<div id="iconContainer" class="square iconContainer">
|
||||
<!-- This data URI is for a transparent gif image -->
|
||||
<img id="instanceIcon" alt="Icon for the selected instance" class="icon" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<button type="submit">OK</button>
|
||||
<button type="reset" class="close">Cancel</button>
|
||||
</form>
|
||||
</dialog>
|
||||
</body>
|
||||
|
||||
</html>
|
|
@ -1,4 +1,5 @@
|
|||
import { initializeAddInstanceDialog } from "./add_an_instance.mjs";
|
||||
import { initializeInstanceDetailsDialog } from "./confirm_instance_details.mjs";
|
||||
import knownSoftware from "./known_software.mjs";
|
||||
import storageManager from "./storage_manager.mjs";
|
||||
console.log(knownSoftware);
|
||||
|
@ -9,7 +10,41 @@ export function getMainDialog(): HTMLDialogElement {
|
|||
return document.getElementById('mainDialog') as HTMLDialogElement;
|
||||
}
|
||||
|
||||
const dialog = document.querySelector("#addInstance");
|
||||
if (!(dialog instanceof HTMLDialogElement))
|
||||
const detailsDialog = document.querySelector("#instanceDetails");
|
||||
if (!(detailsDialog instanceof HTMLDialogElement))
|
||||
throw new Error("Couldn't find instanceDetails dialog");
|
||||
export const {
|
||||
showInstanceDetailsDialog,
|
||||
hideInstanceDetailsDialog,
|
||||
populateInstanceDetailsDialog
|
||||
} = initializeInstanceDetailsDialog(detailsDialog);
|
||||
|
||||
const addInstanceDialogCallback = async (
|
||||
host: string,
|
||||
secure: boolean,
|
||||
autoQueryMetadata: boolean,
|
||||
) => {
|
||||
if (!autoQueryMetadata) {
|
||||
showInstanceDetailsDialog();
|
||||
return;
|
||||
}
|
||||
const { name, software, iconURL } =
|
||||
await fetch(`/api/instance_info/${secure}/${encodeURI(host)}`)
|
||||
.then(r => r.json());
|
||||
if (
|
||||
typeof name !== "string"
|
||||
|| typeof software !== "string"
|
||||
|| !(typeof iconURL === "string" || iconURL === null)
|
||||
)
|
||||
throw new Error("Invalid API response");
|
||||
populateInstanceDetailsDialog(name, software, iconURL as string | null);
|
||||
showInstanceDetailsDialog();
|
||||
}
|
||||
|
||||
const addDialog = document.querySelector("#addInstance");
|
||||
if (!(addDialog instanceof HTMLDialogElement))
|
||||
throw new Error("Couldn't find addInstance dialog");
|
||||
export const { showAddInstanceDialog, hideAddInstanceDialog } = initializeAddInstanceDialog(dialog);
|
||||
export const {
|
||||
showAddInstanceDialog,
|
||||
hideAddInstanceDialog
|
||||
} = initializeAddInstanceDialog(addDialog, addInstanceDialogCallback);
|
||||
|
|
Loading…
Add table
Reference in a new issue