1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-12-22 17:10:10 +00:00

split into pages, hide email pages when disabled, add history navigation

This commit is contained in:
Harvey Tindall 2021-01-27 00:51:19 +00:00
parent 8c871bc5fa
commit c7f5aa2e2b
Signed by: hrfee
GPG Key ID: BBC65952848FB1A2
2 changed files with 162 additions and 50 deletions

View File

@ -16,23 +16,23 @@
</div> </div>
</div> </div>
</span> </span>
<div class="page-container"> <div class="page-container" id="page-container">
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1">
<div class="row"> <div class="row">
<img class="banner header" src="banner.svg" alt="jfa-go" /> <img class="banner header" src="banner.svg" alt="jfa-go" />
</div> </div>
<div class="row col flex center"> <div class="row col flex center">
<span class="heading">{{ .lang.StartPage.welcome }}</span> <span class="heading welcome">{{ .lang.StartPage.welcome }}</span>
</div> </div>
<div class="row col flex center"> <div class="row col flex center">
<p class="content">{{ .lang.StartPage.pressStart }}</p> <p class="content">{{ .lang.StartPage.pressStart }}</p>
</div> </div>
<section class="section ~neutral banner footer flex-expand middle"> <section class="section ~neutral banner footer flex-expand middle">
<span class="support">{{ .lang.StartPage.httpsNotice }}</span> <span class="support">{{ .lang.StartPage.httpsNotice }}</span>
<span class="button ~urge !normal">{{ .lang.StartPage.start }}</span> <span class="button ~urge !normal next">{{ .lang.StartPage.start }}</span>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.Language.title }}</span> <span class="heading">{{ .lang.Language.title }}</span>
<p class="content" id="language-description"></p> <p class="content" id="language-description"></p>
<label class="label"> <label class="label">
@ -61,7 +61,7 @@
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span> <span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.Login.title }}</span> <span class="heading">{{ .lang.Login.title }}</span>
<p class="content">{{ .lang.Login.description }}</p> <p class="content">{{ .lang.Login.description }}</p>
<div class="pl-1"> <div class="pl-1">
@ -95,7 +95,7 @@
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span> <span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.JellyfinEmby.title }}</span> <span class="heading">{{ .lang.JellyfinEmby.title }}</span>
<p class="content">{{ .lang.JellyfinEmby.description }}</p> <p class="content">{{ .lang.JellyfinEmby.description }}</p>
<label class="label"> <label class="label">
@ -138,7 +138,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.Email.title }}</span> <span class="heading">{{ .lang.Email.title }}</span>
<p class="content" id="email-description"></p> <p class="content" id="email-description"></p>
<div class="row"> <div class="row">
@ -181,6 +181,7 @@
</div> </div>
<div class="col"> <div class="col">
<div id="email-smtp"> <div id="email-smtp">
<p class="subheading">SMTP</p>
<label class="label"> <label class="label">
<span>{{ .lang.Email.encryption }}</span> <span>{{ .lang.Email.encryption }}</span>
<div class="select ~neutral !normal mt-half mb-1"> <div class="select ~neutral !normal mt-half mb-1">
@ -208,6 +209,7 @@
</label> </label>
</div> </div>
<div id="email-mailgun"> <div id="email-mailgun">
<p class="subheading">Mailgun</p>
<label class="label"> <label class="label">
<span class="mt-half">{{ .lang.Email.mailgunApiURL }}</span> <span class="mt-half">{{ .lang.Email.mailgunApiURL }}</span>
<input type="url" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages"> <input type="url" class="input ~neutral !normal mt-half mb-1" id="mailgun-api_url" placeholder="https://api.eu.mailgun.net/v3/mail.jellyf.in/messages">
@ -226,7 +228,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1 related-to-email"> <div class="card ~neutral !low mb-1 unfocused related-to-email">
<span class="heading">{{ .lang.Notifications.title }}</span> <span class="heading">{{ .lang.Notifications.title }}</span>
<p class="content">{{ .lang.Notifications.description }}</p> <p class="content">{{ .lang.Notifications.description }}</p>
<label class="row switch pb-1"> <label class="row switch pb-1">
@ -241,8 +243,14 @@
<span class="mt-half">{{ .lang.Strings.emailSubject }}</span> <span class="mt-half">{{ .lang.Strings.emailSubject }}</span>
<input type="text" class="input ~neutral !normal mt-half mb-1" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}"> <input type="text" class="input ~neutral !normal mt-half mb-1" id="welcome_email-subject" placeholder="{{ .emailLang.WelcomeEmail.title }}">
</label> </label>
<section class="section ~neutral banner footer flex-expand middle">
<span class="button ~neutral !normal back">{{ .lang.Strings.back }}</span>
<div>
<span class="button ~urge !normal next">{{ .lang.Strings.next }}</span>
</div> </div>
<div class="card ~neutral !low mb-1 related-to-email"> </section>
</div>
<div class="card ~neutral !low mb-1 unfocused related-to-email">
<span class="heading">{{ .lang.InviteEmails.title }}</span> <span class="heading">{{ .lang.InviteEmails.title }}</span>
<p class="content">{{ .lang.InviteEmails.description }}</p> <p class="content">{{ .lang.InviteEmails.description }}</p>
<label class="row switch pb-1"> <label class="row switch pb-1">
@ -263,7 +271,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1 related-to-email"> <div class="card ~neutral !low mb-1 unfocused related-to-email">
<span class="heading">{{ .lang.PasswordResets.title }}</span> <span class="heading">{{ .lang.PasswordResets.title }}</span>
<p class="content">{{ .lang.PasswordResets.description }}</p> <p class="content">{{ .lang.PasswordResets.description }}</p>
<label class="row switch pb-1"> <label class="row switch pb-1">
@ -285,7 +293,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.PasswordValidation.title }}</span> <span class="heading">{{ .lang.PasswordValidation.title }}</span>
<p class="content">{{ .lang.PasswordValidation.description }}</p> <p class="content">{{ .lang.PasswordValidation.description }}</p>
<label class="row switch pb-1"> <label class="row switch pb-1">
@ -318,7 +326,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<span class="heading">{{ .lang.HelpMessages.title }}</span> <span class="heading">{{ .lang.HelpMessages.title }}</span>
<p class="content">{{ .lang.HelpMessages.description }}</p> <p class="content">{{ .lang.HelpMessages.description }}</p>
<label class="label"> <label class="label">
@ -348,7 +356,7 @@
</div> </div>
</section> </section>
</div> </div>
<div class="card ~neutral !low mb-1"> <div class="card ~neutral !low mb-1 unfocused">
<div class="row col flex center"> <div class="row col flex center">
<span class="heading">{{ .lang.EndPage.finished }}</span> <span class="heading">{{ .lang.EndPage.finished }}</span>
</div> </div>
@ -356,6 +364,7 @@
<p class="content">{{ .lang.EndPage.restartMessage }}</p> <p class="content">{{ .lang.EndPage.restartMessage }}</p>
</div> </div>
<div class="row col flex center"> <div class="row col flex center">
<span class="button ~neutral !normal back mr-1">{{ .lang.Strings.back }}</span>
<span class="button ~urge !normal" id="restart">{{ .lang.Strings.submit }}</span> <span class="button ~urge !normal" id="restart">{{ .lang.Strings.submit }}</span>
</div> </div>
</div> </div>

View File

@ -19,6 +19,8 @@ class Input {
private _el: HTMLInputElement; private _el: HTMLInputElement;
get value(): string { return ""+this._el.value; } get value(): string { return ""+this._el.value; }
set value(v: string) { this._el.value = v; } set value(v: string) { this._el.value = v; }
// Nothing depends on input, but we add an empty broadcast function so we can just loop over all settings to fix dependents on start.
broadcast = () => {}
constructor(el: HTMLElement, placeholder?: any, value?: any, depends?: string, dependsTrue?: boolean, section?: string) { constructor(el: HTMLElement, placeholder?: any, value?: any, depends?: string, dependsTrue?: boolean, section?: string) {
this._el = el as HTMLInputElement; this._el = el as HTMLInputElement;
if (placeholder) { this._el.placeholder = placeholder; } if (placeholder) { this._el.placeholder = placeholder; }
@ -41,13 +43,21 @@ class Checkbox {
private _el: HTMLInputElement; private _el: HTMLInputElement;
get value(): string { return this._el.checked ? "true" : "false"; } get value(): string { return this._el.checked ? "true" : "false"; }
set value(v: string) { this._el.checked = (v == "true") ? true : false; } set value(v: string) { this._el.checked = (v == "true") ? true : false; }
private _section: string;
private _setting: string;
broadcast = () => {
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": this._el.checked })
document.dispatchEvent(ev);
}
}
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) { constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._el = el as HTMLInputElement; this._el = el as HTMLInputElement;
if (section && setting) { if (section && setting) {
this._el.onchange = () => { this._section = section;
const ev = new CustomEvent(`settings-${section}-${setting}`, { "detail": this._el.checked }) this._setting = setting;
document.dispatchEvent(ev); this._el.onchange = this.broadcast;
};
} }
if (depends) { if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => { document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
@ -71,15 +81,22 @@ class BoolRadios {
this._els[0].checked = bool; this._els[0].checked = bool;
this._els[1].checked = !bool; this._els[1].checked = !bool;
} }
private _section: string;
private _setting: string;
broadcast = () => {
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": this._els[0].checked })
document.dispatchEvent(ev);
}
}
constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) { constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._els = document.getElementsByName(name) as NodeListOf<HTMLInputElement>; this._els = document.getElementsByName(name) as NodeListOf<HTMLInputElement>;
if (section && setting) { if (section && setting) {
const onchange = () => { this._section = section;
const ev = new CustomEvent(`settings-${section}-${setting}`, { "detail": this._els[0].checked }) this._setting = setting;
document.dispatchEvent(ev); this._els[0].onchange = this.broadcast;
}; this._els[1].onchange = this.broadcast;
this._els[0].onchange = onchange;
this._els[1].onchange = onchange;
} }
if (depends) { if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => { document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
@ -103,25 +120,25 @@ class BoolRadios {
} }
} }
class Radios { // class Radios {
private _el: HTMLInputElement; // private _el: HTMLInputElement;
get value(): string { return this._el.value; } // get value(): string { return this._el.value; }
set value(v: string) { this._el.value = v; } // set value(v: string) { this._el.value = v; }
constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string) { // constructor(name: string, depends?: string, dependsTrue?: boolean, section?: string) {
this._el = document.getElementsByName(name)[0] as HTMLInputElement; // this._el = document.getElementsByName(name)[0] as HTMLInputElement;
if (depends) { // if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => { // document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
let el = this._el as HTMLElement; // let el = this._el as HTMLElement;
if (el.parentElement.tagName == "LABEL") { el = el.parentElement; } // if (el.parentElement.tagName == "LABEL") { el = el.parentElement; }
if (event.detail !== dependsTrue) { // if (event.detail !== dependsTrue) {
el.classList.add("unfocused"); // el.classList.add("unfocused");
} else { // } else {
el.classList.remove("unfocused"); // el.classList.remove("unfocused");
} // }
}); // });
} // }
} // }
} // }
class Select { class Select {
private _el: HTMLSelectElement; private _el: HTMLSelectElement;
@ -136,13 +153,21 @@ class Select {
set onchange(f: () => void) { set onchange(f: () => void) {
this._el.addEventListener("change", f); this._el.addEventListener("change", f);
} }
private _section: string;
private _setting: string;
broadcast = () => {
if (this._section && this._setting) {
const ev = new CustomEvent(`settings-${this._section}-${this._setting}`, { "detail": this.value ? true : false })
document.dispatchEvent(ev);
}
}
constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) { constructor(el: HTMLElement, depends?: string, dependsTrue?: boolean, section?: string, setting?: string) {
this._el = el as HTMLSelectElement; this._el = el as HTMLSelectElement;
if (section && setting) { if (section && setting) {
this._el.addEventListener("change", () => { this._section = section;
const ev = new CustomEvent(`settings-${section}-${setting}`, { "detail": this.value ? true : false }) this._setting = setting;
document.dispatchEvent(ev); this._el.addEventListener("change", this.broadcast);
});
} }
if (depends) { if (depends) {
document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => { document.addEventListener(`settings-${section}-${depends}`, (event: boolEvent) => {
@ -246,7 +271,7 @@ const settings = {
}; };
const relatedToEmail = Array.from(document.getElementsByClassName("related-to-email")); const relatedToEmail = Array.from(document.getElementsByClassName("related-to-email"));
settings["email"]["method"].onchange = () => { const emailMethodChange = () => {
const val = settings["email"]["method"].value; const val = settings["email"]["method"].value;
const smtp = document.getElementById("email-smtp"); const smtp = document.getElementById("email-smtp");
const mailgun = document.getElementById("email-mailgun"); const mailgun = document.getElementById("email-mailgun");
@ -254,21 +279,99 @@ settings["email"]["method"].onchange = () => {
smtp.classList.remove("unfocused"); smtp.classList.remove("unfocused");
mailgun.classList.add("unfocused"); mailgun.classList.add("unfocused");
for (let el of relatedToEmail) { for (let el of relatedToEmail) {
el.classList.remove("unfocused"); el.classList.remove("hidden");
} }
} else if (val == "mailgun") { } else if (val == "mailgun") {
mailgun.classList.remove("unfocused"); mailgun.classList.remove("unfocused");
smtp.classList.add("unfocused"); smtp.classList.add("unfocused");
for (let el of relatedToEmail) { for (let el of relatedToEmail) {
el.classList.remove("unfocused"); el.classList.remove("hidden");
} }
} else { } else {
mailgun.classList.add("unfocused"); mailgun.classList.add("unfocused");
smtp.classList.add("unfocused"); smtp.classList.add("unfocused");
for (let el of relatedToEmail) { for (let el of relatedToEmail) {
el.classList.add("unfocused"); el.classList.add("hidden");
}
}
};
settings["email"]["method"].onchange = emailMethodChange;
emailMethodChange();
(window as any).settings = settings;
for (let section in settings) {
for (let setting in settings[section]) {
settings[section][setting].broadcast();
}
}
const pageNames: string[][] = [];
window.history.replaceState("welcome", "Setup - jfa-go");
const changePage = (title: string, pageTitle: string) => {
const urlParams = new URLSearchParams(window.location.search);
const lang = urlParams.get("lang");
let page = "/#" + title;
if (lang) { page += "?lang=" + lang; }
window.history.pushState(title || "welcome", pageTitle, page);
};
const cards = Array.from(document.getElementById("page-container").getElementsByClassName("card")) as Array<HTMLDivElement>;
(window as any).cards = cards;
window.onpopstate = (event: PopStateEvent) => {
if (event.state === "welcome") {
cards[0].classList.remove("unfocused");
for (let i = 1; i < cards.length; i++) { cards[i].classList.add("unfocused"); }
return;
}
for (let i = 0; i < cards.length; i++) {
if (event.state === pageNames[i][0]) {
cards[i].classList.remove("unfocused");
} else {
cards[i].classList.add("unfocused");
} }
} }
}; };
(() => {
for (let i = 0; i < cards.length; i++) {
const card = cards[i];
const back = card.getElementsByClassName("back")[0] as HTMLSpanElement;
const next = card.getElementsByClassName("next")[0] as HTMLSpanElement;
console.log(cards[i]);
const titleEl = cards[i].querySelector("span.heading") as HTMLElement;
let title = titleEl.textContent.replace("/", "_").replace(" ", "-");
if (titleEl.classList.contains("welcome")) {
title = "";
}
let pageTitle = titleEl.textContent + " - jfa-go";
pageNames.push([title, pageTitle]);
if (back) { back.addEventListener("click", () => {
let found = false;
for (let ind = cards.length - 1; ind >= 0; ind--) {
cards[ind].classList.add("unfocused");
if (ind < i && !(cards[ind].classList.contains("hidden")) && !found) {
cards[ind].classList.remove("unfocused");
changePage(pageNames[ind][0], pageNames[ind][1]);
found = true;
}
}
window.scrollTo(0, 0);
}); }
if (next) { next.addEventListener("click", () => {
let found = false;
for (let ind = 0; ind < cards.length; ind++) {
cards[ind].classList.add("unfocused");
if (ind > i && !(cards[ind].classList.contains("hidden")) && !found) {
cards[ind].classList.remove("unfocused");
changePage(pageNames[ind][0], pageNames[ind][1]);
found = true;
}
}
window.scrollTo(0, 0);
}); }
}
})()
loadLangSelector("setup"); loadLangSelector("setup");