Implemented phase 4 for the POC prototype in the Website service.
This commit is contained in:
1 parent
95e0a455e8
commit
9403931109
42 files changed
+3407
-36
No files matched your search
@@ -0,0 +1,60 @@
|
||||
// CCN — passkey sign-in. Without it, or without WebAuthn, the sign-in screen offers the demo sign-in alone.
|
||||
|
||||
const form = document.querySelector("[data-passkey-form]");
|
||||
const supported = window.PublicKeyCredential
|
||||
&& PublicKeyCredential.parseCreationOptionsFromJSON
|
||||
&& PublicKeyCredential.parseRequestOptionsFromJSON;
|
||||
|
||||
if (form && supported) {
|
||||
const button = form.querySelector("[data-passkey]");
|
||||
const status = form.querySelector("[data-passkey-status]");
|
||||
|
||||
const post = async (path, body) => {
|
||||
const response = await fetch(path, {
|
||||
method: "POST",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
const answer = await response.json();
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error(answer.error ?? "Something went wrong. Try again.");
|
||||
}
|
||||
|
||||
return answer;
|
||||
};
|
||||
|
||||
button.hidden = false;
|
||||
button.addEventListener("click", async () => {
|
||||
button.disabled = true;
|
||||
status.textContent = "";
|
||||
|
||||
try {
|
||||
const { mode, options } = await post("/sign-in/passkey/options", { email: form.elements.email.value });
|
||||
let credential;
|
||||
|
||||
if (mode === "create") {
|
||||
// A discoverable passkey, so the next sign-in needs no email.
|
||||
options.authenticatorSelection = { residentKey: "required", userVerification: "preferred" };
|
||||
credential = await navigator.credentials.create({
|
||||
publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(options),
|
||||
});
|
||||
} else {
|
||||
credential = await navigator.credentials.get({
|
||||
publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(options),
|
||||
});
|
||||
}
|
||||
|
||||
const { location } = await post("/sign-in/passkey/finish", {
|
||||
credential: credential.toJSON(),
|
||||
return: form.elements.return.value,
|
||||
});
|
||||
|
||||
window.location.assign(location);
|
||||
} catch (error) {
|
||||
// Dismissing the browser's prompt is not an error worth a message.
|
||||
status.textContent = error.name === "NotAllowedError" ? "" : error.message;
|
||||
button.disabled = false;
|
||||
}
|
||||
});
|
||||
}
|
||||
@@ -21,3 +21,22 @@ if (filters) {
|
||||
sync();
|
||||
desktop.addEventListener("change", sync);
|
||||
}
|
||||
|
||||
// The new-listing form shows every field without scripts; here, only those that apply to the format and grading picked.
|
||||
const listingForm = document.querySelector(".listing-form__form");
|
||||
|
||||
if (listingForm) {
|
||||
const apply = () => {
|
||||
const format = listingForm.querySelector("input[name=format]:checked")?.value;
|
||||
const grading = listingForm.querySelector("input[name=grading]:checked")?.value;
|
||||
const single = format === "single-issues";
|
||||
const scopes = { "single-issues": single, collected: !single, slabbed: single && grading === "slabbed" };
|
||||
|
||||
for (const element of listingForm.querySelectorAll("[data-scope]")) {
|
||||
element.hidden = !scopes[element.dataset.scope];
|
||||
}
|
||||
};
|
||||
|
||||
listingForm.addEventListener("change", apply);
|
||||
apply();
|
||||
}
|
||||
Reference in new issue
Block a user