redo login/register pages and install button
This commit is contained in:
parent
e776bf6d3f
commit
ba5ab05408
11 changed files with 698 additions and 600 deletions
|
@ -1,4 +1,5 @@
|
|||
import {I18n} from "./i18n.js";
|
||||
import {makeRegister} from "./register.js";
|
||||
import {mobile} from "./utils/utils.js";
|
||||
console.log(mobile);
|
||||
const serverbox = document.getElementById("instancebox") as HTMLDivElement;
|
||||
|
@ -127,7 +128,7 @@ fetch("/instances.json")
|
|||
div.append(statbox);
|
||||
div.onclick = (_) => {
|
||||
if (instance.online) {
|
||||
window.location.href = "/register.html?instance=" + encodeURI(instance.name);
|
||||
makeRegister(true, instance.name);
|
||||
} else {
|
||||
alert(I18n.getTranslation("home.warnOffiline"));
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ import {I18n} from "./i18n.js";
|
|||
let templateID = new URLSearchParams(window.location.search).get("templateID");
|
||||
await I18n.done;
|
||||
|
||||
if (!Localuser.users.currentuser) {
|
||||
if (!(sessionStorage.getItem("currentuser") || Localuser.users.currentuser)) {
|
||||
window.location.href = "/login.html";
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -3,7 +3,7 @@ import {Channel} from "./channel.js";
|
|||
import {Direct} from "./direct.js";
|
||||
import {AVoice} from "./audio/voice.js";
|
||||
import {User} from "./user.js";
|
||||
import {createImg, getapiurls, getBulkUsers, SW} from "./utils/utils.js";
|
||||
import {createImg, getapiurls, getBulkUsers, installPGet, SW} from "./utils/utils.js";
|
||||
import {getBulkInfo, setTheme, Specialuser} from "./utils/utils.js";
|
||||
import {
|
||||
channeljson,
|
||||
|
@ -1719,7 +1719,8 @@ class Localuser {
|
|||
required: true,
|
||||
password: true,
|
||||
});
|
||||
form.addTextInput(I18n.getTranslation("localuser.2faCode"), "code", {required: true});
|
||||
form.addTextInput(I18n.localuser["2faCode:"](), "code", {required: true});
|
||||
debugger;
|
||||
form.setValue("secret", secret);
|
||||
});
|
||||
}
|
||||
|
@ -2197,6 +2198,15 @@ class Localuser {
|
|||
this.instanceStats();
|
||||
});
|
||||
})();
|
||||
const installP = installPGet();
|
||||
if (installP) {
|
||||
const c = settings.addButton(I18n.localuser.install());
|
||||
c.addText(I18n.localuser.installDesc());
|
||||
c.addButtonInput("", I18n.localuser.installJank(), async () => {
|
||||
//@ts-expect-error have to do this :3
|
||||
await installP.prompt();
|
||||
});
|
||||
}
|
||||
settings.show();
|
||||
}
|
||||
readonly botTokens: Map<string, string> = new Map();
|
||||
|
|
|
@ -22,35 +22,6 @@
|
|||
</style>
|
||||
</head>
|
||||
<body class="no-theme">
|
||||
<div id="logindiv">
|
||||
<h1>Login</h1>
|
||||
<form id="form" submit="check(e)">
|
||||
<label for="instance" id="instanceField"><b>Instance:</b></label>
|
||||
<p id="verify"></p>
|
||||
<input
|
||||
type="search"
|
||||
list="instances"
|
||||
placeholder="Instance URL"
|
||||
name="instance"
|
||||
id="instancein"
|
||||
value=""
|
||||
required
|
||||
/>
|
||||
|
||||
<label for="uname" id="emailField"><b>Email:</b></label>
|
||||
<input type="text" placeholder="Enter email address" name="uname" id="uname" required />
|
||||
|
||||
<label for="psw" id="pwField"><b>Password:</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="psw" id="psw" required />
|
||||
<p class="wrongred" id="wrong"></p>
|
||||
|
||||
<div id="h-captcha"></div>
|
||||
<button type="submit" id="loginButton">Login</button>
|
||||
</form>
|
||||
<a href="/register.html" id="switch">Don't have an account?</a>
|
||||
<div id="recover"></div>
|
||||
</div>
|
||||
<datalist id="instances"></datalist>
|
||||
<script src="/login.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,10 +1,10 @@
|
|||
import {getBulkInfo, Specialuser} from "./utils/utils.js";
|
||||
import {instanceinfo, adduser} from "./utils/utils.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
import {Dialog, FormError} from "./settings.js";
|
||||
import {checkInstance} from "./utils/utils.js";
|
||||
function generateRecArea() {
|
||||
const recover = document.getElementById("recover");
|
||||
import {Dialog} from "./settings.js";
|
||||
import {makeRegister} from "./register.js";
|
||||
function generateRecArea(recover = document.getElementById("recover")) {
|
||||
if (!recover) return;
|
||||
recover.innerHTML = "";
|
||||
const can = localStorage.getItem("canRecover");
|
||||
if (can) {
|
||||
const a = document.createElement("a");
|
||||
|
@ -13,248 +13,98 @@ function generateRecArea() {
|
|||
recover.append(a);
|
||||
}
|
||||
}
|
||||
checkInstance.alt = async (e) => {
|
||||
const recover = document.getElementById("recover");
|
||||
if (!recover) return;
|
||||
recover.innerHTML = "";
|
||||
try {
|
||||
const json = (await (await fetch(e.api + "/policies/instance/config")).json()) as {
|
||||
can_recover_account: boolean;
|
||||
};
|
||||
if (!json || !json.can_recover_account) throw Error("can't recover account");
|
||||
localStorage.setItem("canRecover", "true");
|
||||
generateRecArea();
|
||||
} catch {
|
||||
localStorage.removeItem("canRecover");
|
||||
generateRecArea();
|
||||
}
|
||||
};
|
||||
await I18n.done;
|
||||
generateRecArea();
|
||||
(async () => {
|
||||
await I18n.done;
|
||||
const instanceField = document.getElementById("instanceField");
|
||||
const emailField = document.getElementById("emailField");
|
||||
const pwField = document.getElementById("pwField");
|
||||
const loginButton = document.getElementById("loginButton");
|
||||
const noAccount = document.getElementById("switch");
|
||||
if (instanceField && emailField && pwField && loginButton && noAccount) {
|
||||
instanceField.textContent = I18n.getTranslation("htmlPages.instanceField");
|
||||
emailField.textContent = I18n.getTranslation("htmlPages.emailField");
|
||||
pwField.textContent = I18n.getTranslation("htmlPages.pwField");
|
||||
loginButton.textContent = I18n.getTranslation("htmlPages.loginButton");
|
||||
noAccount.textContent = I18n.getTranslation("htmlPages.noAccount");
|
||||
}
|
||||
})();
|
||||
|
||||
function trimswitcher() {
|
||||
const json = getBulkInfo();
|
||||
const map = new Map();
|
||||
for (const thing in json.users) {
|
||||
const user = json.users[thing];
|
||||
let wellknown = user.serverurls.wellknown;
|
||||
if (wellknown.at(-1) !== "/") {
|
||||
wellknown += "/";
|
||||
const recMap = new Map<string, Promise<boolean>>();
|
||||
async function recover(e: instanceinfo, recover = document.getElementById("recover")) {
|
||||
const prom = new Promise<boolean>(async (res) => {
|
||||
if (!recover) {
|
||||
res(false);
|
||||
return;
|
||||
}
|
||||
wellknown = (user.id || user.email) + "@" + wellknown;
|
||||
if (map.has(wellknown)) {
|
||||
const otheruser = map.get(wellknown);
|
||||
if (otheruser[1].serverurls.wellknown.at(-1) === "/") {
|
||||
delete json.users[otheruser[0]];
|
||||
map.set(wellknown, [thing, user]);
|
||||
} else {
|
||||
delete json.users[thing];
|
||||
recover.innerHTML = "";
|
||||
try {
|
||||
if (!(await recMap.get(e.api))) {
|
||||
if (recMap.has(e.api)) {
|
||||
throw Error("can't recover");
|
||||
}
|
||||
recMap.set(e.api, prom);
|
||||
const json = (await (await fetch(e.api + "/policies/instance/config")).json()) as {
|
||||
can_recover_account: boolean;
|
||||
};
|
||||
if (!json || !json.can_recover_account) throw Error("can't recover account");
|
||||
}
|
||||
} else {
|
||||
map.set(wellknown, [thing, user]);
|
||||
res(true);
|
||||
localStorage.setItem("canRecover", "true");
|
||||
generateRecArea(recover);
|
||||
} catch {
|
||||
res(false);
|
||||
localStorage.removeItem("canRecover");
|
||||
generateRecArea(recover);
|
||||
} finally {
|
||||
res(false);
|
||||
}
|
||||
}
|
||||
for (const thing in json.users) {
|
||||
if (thing.at(-1) === "/") {
|
||||
const user = json.users[thing];
|
||||
delete json.users[thing];
|
||||
json.users[thing.slice(0, -1)] = user;
|
||||
}
|
||||
}
|
||||
localStorage.setItem("userinfos", JSON.stringify(json));
|
||||
console.log(json);
|
||||
}
|
||||
|
||||
function adduser(user: typeof Specialuser.prototype.json) {
|
||||
user = new Specialuser(user);
|
||||
const info = getBulkInfo();
|
||||
info.users[user.uid] = user;
|
||||
info.currentuser = user.uid;
|
||||
sessionStorage.setItem("currentuser", user.uid);
|
||||
localStorage.setItem("userinfos", JSON.stringify(info));
|
||||
return user;
|
||||
}
|
||||
const instancein = document.getElementById("instancein") as HTMLInputElement;
|
||||
let timeout: ReturnType<typeof setTimeout> | string | number | undefined | null = null;
|
||||
// let instanceinfo;
|
||||
|
||||
const switchurl = document.getElementById("switch") as HTMLAreaElement;
|
||||
if (switchurl) {
|
||||
switchurl.href += window.location.search;
|
||||
const instance = new URLSearchParams(window.location.search).get("instance");
|
||||
console.log(instance);
|
||||
if (instance) {
|
||||
instancein.value = instance;
|
||||
checkInstance(instance);
|
||||
}
|
||||
}
|
||||
|
||||
if (instancein) {
|
||||
console.log(instancein);
|
||||
instancein.addEventListener("keydown", () => {
|
||||
const verify = document.getElementById("verify");
|
||||
verify!.textContent = I18n.getTranslation("login.waiting");
|
||||
if (timeout !== null && typeof timeout !== "string") {
|
||||
clearTimeout(timeout);
|
||||
}
|
||||
timeout = setTimeout(() => checkInstance((instancein as HTMLInputElement).value), 1000);
|
||||
});
|
||||
if (
|
||||
localStorage.getItem("instanceinfo") &&
|
||||
!new URLSearchParams(window.location.search).get("instance")
|
||||
) {
|
||||
const json = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||
if (json.value) {
|
||||
(instancein as HTMLInputElement).value = json.value;
|
||||
} else {
|
||||
(instancein as HTMLInputElement).value = json.wellknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function login(username: string, password: string, captcha: string) {
|
||||
if (captcha === "") {
|
||||
captcha = "";
|
||||
}
|
||||
const options = {
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
login: username,
|
||||
password,
|
||||
undelete: false,
|
||||
captcha_key: captcha,
|
||||
}),
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8",
|
||||
export async function makeLogin(trasparentBg = false, instance = "") {
|
||||
const dialog = new Dialog("");
|
||||
const opt = dialog.options;
|
||||
opt.addTitle(I18n.login.login());
|
||||
const picker = opt.addInstancePicker(
|
||||
(info) => {
|
||||
const api = info.login + (info.login.startsWith("/") ? "/" : "");
|
||||
form.fetchURL = api + "/auth/login";
|
||||
recover(info, rec);
|
||||
},
|
||||
{
|
||||
instance,
|
||||
},
|
||||
};
|
||||
try {
|
||||
const info = JSON.parse(localStorage.getItem("instanceinfo")!);
|
||||
const api = info.login + (info.login.startsWith("/") ? "/" : "");
|
||||
return await fetch(api + "/auth/login", options)
|
||||
.then((response) => response.json())
|
||||
.then((response) => {
|
||||
console.log(response, response.message);
|
||||
if (response.message === "Invalid Form Body") {
|
||||
return response.errors.login._errors[0].message;
|
||||
console.log("test");
|
||||
}
|
||||
//this.serverurls||!this.email||!this.token
|
||||
console.log(response);
|
||||
|
||||
if (response.captcha_sitekey) {
|
||||
const capt = document.getElementById("h-captcha");
|
||||
if (capt!.children.length) {
|
||||
eval("hcaptcha.reset()");
|
||||
} else {
|
||||
const capty = document.createElement("div");
|
||||
capty.classList.add("h-captcha");
|
||||
|
||||
capty.setAttribute("data-sitekey", response.captcha_sitekey);
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://js.hcaptcha.com/1/api.js";
|
||||
capt!.append(script);
|
||||
capt!.append(capty);
|
||||
}
|
||||
} else {
|
||||
console.log(response);
|
||||
if (response.ticket) {
|
||||
const better = new Dialog("");
|
||||
const form = better.options.addForm(
|
||||
"",
|
||||
(res: any) => {
|
||||
if (res.message) {
|
||||
throw new FormError(ti, res.message);
|
||||
} else {
|
||||
console.warn(res);
|
||||
if (!res.token) return;
|
||||
adduser({
|
||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
|
||||
email: username,
|
||||
token: res.token,
|
||||
}).username = username;
|
||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||
if (redir) {
|
||||
window.location.href = redir;
|
||||
} else {
|
||||
window.location.href = "/channels/@me";
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
fetchURL: api + "/auth/mfa/totp",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
form.addTitle(I18n.getTranslation("2faCode"));
|
||||
const ti = form.addTextInput("", "code");
|
||||
better.show();
|
||||
} else {
|
||||
console.warn(response);
|
||||
if (!response.token) return;
|
||||
adduser({
|
||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo")!),
|
||||
email: username,
|
||||
token: response.token,
|
||||
}).username = username;
|
||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||
if (redir) {
|
||||
window.location.href = redir;
|
||||
} else {
|
||||
window.location.href = "/channels/@me";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error("Error:", error);
|
||||
}
|
||||
}
|
||||
|
||||
async function check(e: SubmitEvent) {
|
||||
e.preventDefault();
|
||||
const target = e.target as HTMLFormElement;
|
||||
const h = await login(
|
||||
(target[1] as HTMLInputElement).value,
|
||||
(target[2] as HTMLInputElement).value,
|
||||
(target[3] as HTMLInputElement).value,
|
||||
);
|
||||
const wrongElement = document.getElementById("wrong");
|
||||
if (wrongElement) {
|
||||
wrongElement.textContent = h;
|
||||
}
|
||||
console.log(h);
|
||||
}
|
||||
if (document.getElementById("form")) {
|
||||
const form = document.getElementById("form");
|
||||
if (form) {
|
||||
form.addEventListener("submit", (e: SubmitEvent) => check(e));
|
||||
}
|
||||
}
|
||||
//this currently does not work, and need to be implemented better at some time.
|
||||
if (!localStorage.getItem("SWMode")) {
|
||||
localStorage.setItem("SWMode", "SWOn");
|
||||
}
|
||||
dialog.show(trasparentBg);
|
||||
|
||||
trimswitcher();
|
||||
const form = opt.addForm(
|
||||
"",
|
||||
(res) => {
|
||||
if ("token" in res && typeof res.token == "string") {
|
||||
adduser({
|
||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
|
||||
email: email.value,
|
||||
token: res.token,
|
||||
}).username = email.value;
|
||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||
if (redir) {
|
||||
window.location.href = redir;
|
||||
} else {
|
||||
window.location.href = "/channels/@me";
|
||||
}
|
||||
}
|
||||
},
|
||||
{
|
||||
submitText: I18n.login.login(),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8",
|
||||
},
|
||||
vsmaller: true,
|
||||
},
|
||||
);
|
||||
const button = form.button.deref();
|
||||
picker.giveButton(button);
|
||||
button?.classList.add("createAccount");
|
||||
|
||||
export {adduser};
|
||||
const email = form.addTextInput(I18n.htmlPages.userField(), "login");
|
||||
form.addTextInput(I18n.htmlPages.pwField(), "password", {password: true});
|
||||
form.addCaptcha();
|
||||
const a = document.createElement("a");
|
||||
a.onclick = () => {
|
||||
dialog.hide();
|
||||
makeRegister(trasparentBg);
|
||||
};
|
||||
a.textContent = I18n.htmlPages.noAccount();
|
||||
const rec = document.createElement("div");
|
||||
form.addHTMLArea(rec);
|
||||
form.addHTMLArea(a);
|
||||
}
|
||||
await I18n.done;
|
||||
if (window.location.pathname.startsWith("/login")) {
|
||||
makeLogin();
|
||||
}
|
||||
|
|
|
@ -22,64 +22,6 @@
|
|||
</style>
|
||||
</head>
|
||||
<body class="no-theme">
|
||||
<div id="logindiv">
|
||||
<h1>Create an account</h1>
|
||||
<form id="register" submit="registertry(e)">
|
||||
<div>
|
||||
<label for="instance" id="instanceField"><b>Instance:</b></label>
|
||||
<p id="verify"></p>
|
||||
<input
|
||||
type="search"
|
||||
list="instances"
|
||||
placeholder="Instance URL"
|
||||
id="instancein"
|
||||
name="instance"
|
||||
value=""
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<label for="uname" id="emailField"><b>Email:</b></label>
|
||||
<input type="text" placeholder="Enter Email" name="uname" id="uname" required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="uname" id="userField"><b>Username:</b></label>
|
||||
<input type="text" placeholder="Enter Username" name="username" id="username" required />
|
||||
</div>
|
||||
<div>
|
||||
<label for="psw" id="pwField"><b>Password:</b></label>
|
||||
<input type="password" placeholder="Enter Password" name="psw" id="psw" required />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="psw2" id="pw2Field"><b>Enter password again:</b></label>
|
||||
<input
|
||||
type="password"
|
||||
placeholder="Enter Password Again"
|
||||
name="psw2"
|
||||
id="psw2"
|
||||
required
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<label for="date" id="dobField"><b>Date of birth:</b></label>
|
||||
<input type="date" id="date" name="date" />
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<b id="TOSbox">I agree to the <a href="" id="TOSa">Terms of Service</a>:</b>
|
||||
<input type="checkbox" id="TOS" name="TOS" />
|
||||
</div>
|
||||
|
||||
<p class="wrongred" id="wrong"></p>
|
||||
<div id="h-captcha"></div>
|
||||
<button type="submit" class="dontgrow" id="createAccount">Create account</button>
|
||||
</form>
|
||||
<a href="/login.html" id="switch" id="alreadyHave">Already have an account?</a>
|
||||
</div>
|
||||
<datalist id="instances"></datalist>
|
||||
<script src="/register.js" type="module"></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,169 +1,100 @@
|
|||
import {I18n} from "./i18n.js";
|
||||
import {checkInstance} from "./utils/utils.js";
|
||||
import {adduser} from "./login.js";
|
||||
import {adduser} from "./utils/utils.js";
|
||||
import {makeLogin} from "./login.js";
|
||||
import {MarkDown} from "./markdown.js";
|
||||
await I18n.done;
|
||||
const registerElement = document.getElementById("register");
|
||||
if (registerElement) {
|
||||
registerElement.addEventListener("submit", registertry);
|
||||
}
|
||||
(async () => {
|
||||
await I18n.done;
|
||||
const userField = document.getElementById("userField");
|
||||
const pw2Field = document.getElementById("pw2Field");
|
||||
const dobField = document.getElementById("dobField");
|
||||
const createAccount = document.getElementById("createAccount");
|
||||
const alreadyHave = document.getElementById("alreadyHave");
|
||||
if (userField && pw2Field && alreadyHave && createAccount && dobField) {
|
||||
userField.textContent = I18n.getTranslation("htmlPages.userField");
|
||||
pw2Field.textContent = I18n.getTranslation("htmlPages.pw2Field");
|
||||
dobField.textContent = I18n.getTranslation("htmlPages.dobField");
|
||||
createAccount.textContent = I18n.getTranslation("htmlPages.createAccount");
|
||||
alreadyHave.textContent = I18n.getTranslation("htmlPages.alreadyHave");
|
||||
}
|
||||
})();
|
||||
async function registertry(e: Event) {
|
||||
e.preventDefault();
|
||||
const elements = (e.target as HTMLFormElement).elements as HTMLFormControlsCollection;
|
||||
const email = (elements[1] as HTMLInputElement).value;
|
||||
const username = (elements[2] as HTMLInputElement).value;
|
||||
const password = (elements[3] as HTMLInputElement).value;
|
||||
const confirmPassword = (elements[4] as HTMLInputElement).value;
|
||||
const dateofbirth = (elements[5] as HTMLInputElement).value;
|
||||
const consent = (elements[6] as HTMLInputElement).checked;
|
||||
const captchaKey = (elements[7] as HTMLInputElement)?.value;
|
||||
import {Dialog, FormError} from "./settings.js";
|
||||
export async function makeRegister(trasparentBg = false, instance = "") {
|
||||
const dialog = new Dialog("");
|
||||
const opt = dialog.options;
|
||||
opt.addTitle(I18n.htmlPages.createAccount());
|
||||
const picker = opt.addInstancePicker(
|
||||
(info) => {
|
||||
const api = info.login + (info.login.startsWith("/") ? "/" : "");
|
||||
form.fetchURL = api + "/auth/register";
|
||||
tosLogic(md);
|
||||
},
|
||||
{instance},
|
||||
);
|
||||
dialog.show(trasparentBg);
|
||||
|
||||
if (password !== confirmPassword) {
|
||||
(document.getElementById("wrong") as HTMLElement).textContent = I18n.getTranslation(
|
||||
"localuser.PasswordsNoMatch",
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||
const apiurl = new URL(instanceInfo.api);
|
||||
let add = "";
|
||||
const token = new URLSearchParams(window.location.search).get("token");
|
||||
if (token) {
|
||||
add = "?" + new URLSearchParams([["token", token]]).toString();
|
||||
}
|
||||
try {
|
||||
const response = await fetch(apiurl + "/auth/register" + add, {
|
||||
body: JSON.stringify({
|
||||
date_of_birth: dateofbirth,
|
||||
email,
|
||||
username,
|
||||
password,
|
||||
consent,
|
||||
captcha_key: captchaKey,
|
||||
}),
|
||||
headers: {
|
||||
"content-type": "application/json",
|
||||
Referrer: window.location.href,
|
||||
},
|
||||
method: "POST",
|
||||
});
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
if (data.captcha_sitekey) {
|
||||
const capt = document.getElementById("h-captcha");
|
||||
if (capt && !capt.children.length) {
|
||||
const capty = document.createElement("div");
|
||||
capty.classList.add("h-captcha");
|
||||
capty.setAttribute("data-sitekey", data.captcha_sitekey);
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://js.hcaptcha.com/1/api.js";
|
||||
capt.append(script);
|
||||
capt.append(capty);
|
||||
} else {
|
||||
eval("hcaptcha.reset()");
|
||||
const form = opt.addForm(
|
||||
"",
|
||||
(res) => {
|
||||
if ("token" in res && typeof res.token == "string") {
|
||||
adduser({
|
||||
serverurls: JSON.parse(localStorage.getItem("instanceinfo") as string),
|
||||
email: email.value,
|
||||
token: res.token,
|
||||
}).username = user.value;
|
||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||
if (redir) {
|
||||
window.location.href = redir;
|
||||
} else {
|
||||
window.location.href = "/channels/@me";
|
||||
}
|
||||
}
|
||||
return;
|
||||
},
|
||||
{
|
||||
submitText: I18n.htmlPages.createAccount(),
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-type": "application/json; charset=UTF-8",
|
||||
},
|
||||
vsmaller: true,
|
||||
},
|
||||
);
|
||||
const button = form.button.deref();
|
||||
picker.giveButton(button);
|
||||
button?.classList.add("createAccount");
|
||||
|
||||
const email = form.addTextInput(I18n.htmlPages.emailField(), "email");
|
||||
const user = form.addTextInput(I18n.htmlPages.userField(), "username");
|
||||
const p1 = form.addTextInput(I18n.htmlPages.pwField(), "password", {password: true});
|
||||
const p2 = form.addTextInput(I18n.htmlPages.pw2Field(), "password2", {password: true});
|
||||
form.addDateInput(I18n.htmlPages.dobField(), "date_of_birth");
|
||||
form.addPreprocessor((e) => {
|
||||
if (p1.value !== p2.value) {
|
||||
throw new FormError(p2, I18n.localuser.PasswordsNoMatch());
|
||||
}
|
||||
//@ts-expect-error it's there
|
||||
delete e.password2;
|
||||
if (!check.checked) throw new FormError(checkbox, I18n.register.tos());
|
||||
//@ts-expect-error it's there
|
||||
e.consent = check.checked;
|
||||
});
|
||||
const toshtml = document.createElement("div");
|
||||
const md = document.createElement("span");
|
||||
const check = document.createElement("input");
|
||||
check.type = "checkbox";
|
||||
|
||||
if (!data.token) {
|
||||
handleErrors(data.errors, elements);
|
||||
} else {
|
||||
adduser({
|
||||
serverurls: instanceInfo,
|
||||
email,
|
||||
token: data.token,
|
||||
}).username = username;
|
||||
localStorage.setItem("token", data.token);
|
||||
const redir = new URLSearchParams(window.location.search).get("goback");
|
||||
window.location.href = redir ? redir : "/channels/@me";
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Registration failed:", error);
|
||||
}
|
||||
toshtml.append(md, check);
|
||||
const checkbox = form.addHTMLArea(toshtml);
|
||||
form.addCaptcha();
|
||||
const a = document.createElement("a");
|
||||
a.onclick = () => {
|
||||
dialog.hide();
|
||||
makeLogin(trasparentBg);
|
||||
};
|
||||
a.textContent = I18n.htmlPages.alreadyHave();
|
||||
form.addHTMLArea(a);
|
||||
}
|
||||
|
||||
function handleErrors(errors: any, elements: HTMLFormControlsCollection) {
|
||||
if (errors.consent) {
|
||||
showError(elements[6] as HTMLElement, errors.consent._errors[0].message);
|
||||
} else if (errors.password) {
|
||||
showError(
|
||||
elements[3] as HTMLElement,
|
||||
I18n.getTranslation("register.passwordError:", errors.password._errors[0].message),
|
||||
);
|
||||
} else if (errors.username) {
|
||||
showError(
|
||||
elements[2] as HTMLElement,
|
||||
I18n.getTranslation("register.usernameError", errors.username._errors[0].message),
|
||||
);
|
||||
} else if (errors.email) {
|
||||
showError(
|
||||
elements[1] as HTMLElement,
|
||||
I18n.getTranslation("register.emailError", errors.email._errors[0].message),
|
||||
);
|
||||
} else if (errors.date_of_birth) {
|
||||
showError(
|
||||
elements[5] as HTMLElement,
|
||||
I18n.getTranslation("register.DOBError", errors.date_of_birth._errors[0].message),
|
||||
);
|
||||
} else {
|
||||
(document.getElementById("wrong") as HTMLElement).textContent =
|
||||
errors[Object.keys(errors)[0]]._errors[0].message;
|
||||
}
|
||||
}
|
||||
|
||||
function showError(element: HTMLElement, message: string) {
|
||||
const parent = element.parentElement!;
|
||||
let errorElement = parent.getElementsByClassName("suberror")[0] as HTMLElement;
|
||||
if (!errorElement) {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("suberror", "suberrora");
|
||||
parent.append(div);
|
||||
errorElement = div;
|
||||
} else {
|
||||
errorElement.classList.remove("suberror");
|
||||
setTimeout(() => {
|
||||
errorElement.classList.add("suberror");
|
||||
}, 100);
|
||||
}
|
||||
errorElement.textContent = message;
|
||||
}
|
||||
|
||||
async function tosLogic() {
|
||||
async function tosLogic(box: HTMLElement) {
|
||||
const instanceInfo = JSON.parse(localStorage.getItem("instanceinfo") ?? "{}");
|
||||
const apiurl = new URL(instanceInfo.api);
|
||||
const urlstr = apiurl.toString();
|
||||
const response = await fetch(urlstr + (urlstr.endsWith("/") ? "" : "/") + "ping");
|
||||
const data = await response.json();
|
||||
const tosPage = data.instance.tosPage;
|
||||
|
||||
if (!box) return;
|
||||
if (tosPage) {
|
||||
const box = document.getElementById("TOSbox");
|
||||
if (!box) return;
|
||||
box.innerHTML = "";
|
||||
box.append(new MarkDown(I18n.getTranslation("register.agreeTOS", tosPage)).makeHTML());
|
||||
} else {
|
||||
document.getElementById("TOSbox")!.textContent = I18n.getTranslation("register.noTOS");
|
||||
box.textContent = I18n.getTranslation("register.noTOS");
|
||||
}
|
||||
console.log(tosPage);
|
||||
}
|
||||
|
||||
tosLogic();
|
||||
|
||||
checkInstance.alt = tosLogic;
|
||||
if (window.location.pathname.startsWith("/register")) {
|
||||
await I18n.done;
|
||||
makeRegister();
|
||||
}
|
||||
|
|
|
@ -1,3 +1,10 @@
|
|||
import {
|
||||
checkInstance,
|
||||
getInstances,
|
||||
getStringURLMapPair,
|
||||
instancefetch,
|
||||
instanceinfo,
|
||||
} from "./utils/utils.js";
|
||||
import {Emoji} from "./emoji.js";
|
||||
import {I18n} from "./i18n.js";
|
||||
import {Localuser} from "./localuser.js";
|
||||
|
@ -166,6 +173,21 @@ class TextInput implements OptionsElement<string> {
|
|||
this.onSubmit(this.value);
|
||||
}
|
||||
}
|
||||
class DateInput extends TextInput {
|
||||
generateHTML(): HTMLDivElement {
|
||||
const div = document.createElement("div");
|
||||
const span = document.createElement("span");
|
||||
span.textContent = this.label;
|
||||
div.append(span);
|
||||
const input = document.createElement("input");
|
||||
input.value = this.value;
|
||||
input.type = "date";
|
||||
input.oninput = this.onChange.bind(this);
|
||||
this.input = new WeakRef(input);
|
||||
div.append(input);
|
||||
return div;
|
||||
}
|
||||
}
|
||||
const mdProm = import("./markdown.js");
|
||||
class SettingsMDText implements OptionsElement<void> {
|
||||
readonly onSubmit!: (str: string) => void;
|
||||
|
@ -306,6 +328,7 @@ class ButtonInput implements OptionsElement<void> {
|
|||
this.onClick = onClick;
|
||||
this.textContent = textContent;
|
||||
}
|
||||
buttonHtml?: HTMLButtonElement;
|
||||
generateHTML(): HTMLDivElement {
|
||||
const div = document.createElement("div");
|
||||
if (this.label) {
|
||||
|
@ -317,6 +340,7 @@ class ButtonInput implements OptionsElement<void> {
|
|||
const button = document.createElement("button");
|
||||
button.textContent = this.textContent;
|
||||
button.onclick = this.onClickEvent.bind(this);
|
||||
this.buttonHtml = button;
|
||||
div.append(button);
|
||||
return div;
|
||||
}
|
||||
|
@ -712,6 +736,7 @@ class Dialog {
|
|||
show(hideOnClick = true) {
|
||||
const background = document.createElement("div");
|
||||
background.classList.add("background");
|
||||
if (!hideOnClick) background.classList.add("solidBackground");
|
||||
const center = this.float.generateHTML();
|
||||
center.classList.add("centeritem", "nonimagecenter");
|
||||
center.classList.remove("titlediv");
|
||||
|
@ -733,7 +758,141 @@ class Dialog {
|
|||
background.remove();
|
||||
}
|
||||
}
|
||||
class InstancePicker implements OptionsElement<instanceinfo | null> {
|
||||
value: instanceinfo | null = null;
|
||||
owner: Options | Form;
|
||||
verify = document.createElement("p");
|
||||
onchange = (_: instanceinfo) => {};
|
||||
instance?: string;
|
||||
watchForChange(func: (arg1: instanceinfo) => void) {
|
||||
this.onchange = func;
|
||||
}
|
||||
constructor(
|
||||
owner: Options | Form,
|
||||
onchange?: InstancePicker["onchange"],
|
||||
button?: HTMLButtonElement,
|
||||
instance?: string,
|
||||
) {
|
||||
this.owner = owner;
|
||||
this.instance = instance;
|
||||
if (onchange) {
|
||||
this.onchange = onchange;
|
||||
}
|
||||
this.button = button;
|
||||
}
|
||||
generateHTML(): HTMLElement {
|
||||
const div = document.createElement("div");
|
||||
const span = document.createElement("span");
|
||||
span.textContent = I18n.htmlPages.instanceField();
|
||||
div.append(span);
|
||||
|
||||
const verify = this.verify;
|
||||
verify.classList.add("verify");
|
||||
div.append(verify);
|
||||
|
||||
const input = this.input;
|
||||
input.type = "search";
|
||||
input.setAttribute("list", "instances");
|
||||
div.append(input);
|
||||
let cur = 0;
|
||||
input.onkeyup = async () => {
|
||||
const thiscur = ++cur;
|
||||
await new Promise((res) => setTimeout(res, 500));
|
||||
if (thiscur !== cur) return;
|
||||
const urls = await checkInstance(input.value, verify, this.button);
|
||||
if (thiscur === cur && urls) {
|
||||
this.onchange(urls);
|
||||
}
|
||||
};
|
||||
|
||||
InstancePicker.picker = this;
|
||||
InstancePicker.genDataList();
|
||||
|
||||
return div;
|
||||
}
|
||||
button?: HTMLButtonElement;
|
||||
input = document.createElement("input");
|
||||
giveButton(button: HTMLButtonElement | undefined) {
|
||||
this.button = button;
|
||||
}
|
||||
static picker?: InstancePicker;
|
||||
static genDataList() {
|
||||
let datalist = document.getElementById("instances");
|
||||
if (!datalist) {
|
||||
datalist = document.createElement("datalist");
|
||||
datalist.setAttribute("id", "instances");
|
||||
document.body.append(datalist);
|
||||
}
|
||||
|
||||
const json = getInstances();
|
||||
|
||||
const [stringURLMap, stringURLsMap] = getStringURLMapPair();
|
||||
if (!json) {
|
||||
instancefetch.then(this.genDataList.bind(this));
|
||||
return;
|
||||
}
|
||||
|
||||
if (json.length !== 0) {
|
||||
let name =
|
||||
this.picker?.instance || new URLSearchParams(window.location.search).get("instance");
|
||||
if (!name) {
|
||||
const l = localStorage.getItem("instanceinfo");
|
||||
if (l) {
|
||||
const json = JSON.parse(l);
|
||||
if (json.value) {
|
||||
name = json.value;
|
||||
} else {
|
||||
name = json.wellknown;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!name) {
|
||||
name = json[0].name;
|
||||
}
|
||||
if (this.picker) {
|
||||
checkInstance(
|
||||
name,
|
||||
this.picker.verify,
|
||||
this.picker.button || document.createElement("button"),
|
||||
).then((e) => {
|
||||
if (e) this.picker?.onchange(e);
|
||||
});
|
||||
this.picker.input.value = name;
|
||||
}
|
||||
}
|
||||
|
||||
if (datalist.childElementCount !== 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (const instance of json) {
|
||||
if (instance.display === false) {
|
||||
continue;
|
||||
}
|
||||
const option = document.createElement("option");
|
||||
option.disabled = !instance.online;
|
||||
option.value = instance.name;
|
||||
if (instance.url) {
|
||||
stringURLMap.set(option.value, instance.url);
|
||||
if (instance.urls) {
|
||||
stringURLsMap.set(instance.url, instance.urls);
|
||||
}
|
||||
} else if (instance.urls) {
|
||||
stringURLsMap.set(option.value, instance.urls);
|
||||
} else {
|
||||
option.disabled = true;
|
||||
}
|
||||
if (instance.description) {
|
||||
option.label = instance.description;
|
||||
} else {
|
||||
option.label = instance.name;
|
||||
}
|
||||
datalist.append(option);
|
||||
}
|
||||
}
|
||||
submit() {}
|
||||
}
|
||||
setTimeout(InstancePicker.genDataList.bind(InstancePicker), 0);
|
||||
export {Dialog};
|
||||
class Options implements OptionsElement<void> {
|
||||
name: string;
|
||||
|
@ -745,16 +904,18 @@ class Options implements OptionsElement<void> {
|
|||
readonly html: WeakMap<OptionsElement<any>, WeakRef<HTMLDivElement>> = new WeakMap();
|
||||
readonly noSubmit: boolean = false;
|
||||
container: WeakRef<HTMLDivElement> = new WeakRef(document.createElement("div"));
|
||||
vsmaller = false;
|
||||
constructor(
|
||||
name: string,
|
||||
owner: Buttons | Options | Form | Float,
|
||||
{ltr = false, noSubmit = false} = {},
|
||||
{ltr = false, noSubmit = false, vsmaller = false} = {},
|
||||
) {
|
||||
this.name = name;
|
||||
this.options = [];
|
||||
this.owner = owner;
|
||||
this.ltr = ltr;
|
||||
this.noSubmit = noSubmit;
|
||||
this.vsmaller = vsmaller;
|
||||
}
|
||||
removeAll() {
|
||||
this.returnFromSub();
|
||||
|
@ -837,6 +998,15 @@ class Options implements OptionsElement<void> {
|
|||
this.generate(emoji);
|
||||
return emoji;
|
||||
}
|
||||
addInstancePicker(
|
||||
onchange?: InstancePicker["onchange"],
|
||||
{button, instance}: {button?: HTMLButtonElement; instance?: string} = {},
|
||||
) {
|
||||
const instacePicker = new InstancePicker(this, onchange, button, instance);
|
||||
this.options.push(instacePicker);
|
||||
this.generate(instacePicker);
|
||||
return instacePicker;
|
||||
}
|
||||
returnFromSub() {
|
||||
this.subOptions = undefined;
|
||||
this.genTop();
|
||||
|
@ -861,6 +1031,14 @@ class Options implements OptionsElement<void> {
|
|||
this.generate(FI);
|
||||
return FI;
|
||||
}
|
||||
addDateInput(label: string, onSubmit: (str: string) => void, {initText = ""} = {}) {
|
||||
const textInput = new DateInput(label, onSubmit, this, {
|
||||
initText,
|
||||
});
|
||||
this.options.push(textInput);
|
||||
this.generate(textInput);
|
||||
return textInput;
|
||||
}
|
||||
addTextInput(
|
||||
label: string,
|
||||
onSubmit: (str: string) => void,
|
||||
|
@ -938,6 +1116,7 @@ class Options implements OptionsElement<void> {
|
|||
headers = {},
|
||||
method = "POST",
|
||||
traditionalSubmit = false,
|
||||
vsmaller = false,
|
||||
} = {},
|
||||
) {
|
||||
const options = new Form(name, this, onSubmit, {
|
||||
|
@ -947,6 +1126,7 @@ class Options implements OptionsElement<void> {
|
|||
headers,
|
||||
method,
|
||||
traditionalSubmit,
|
||||
vsmaller,
|
||||
});
|
||||
this.options.push(options);
|
||||
this.generate(options);
|
||||
|
@ -969,6 +1149,7 @@ class Options implements OptionsElement<void> {
|
|||
generateHTML(): HTMLElement {
|
||||
const div = document.createElement("div");
|
||||
div.classList.add("flexttb", "titlediv");
|
||||
if (this.vsmaller) div.classList.add("vsmaller");
|
||||
if (this.owner instanceof Options) {
|
||||
div.classList.add("optionElement");
|
||||
}
|
||||
|
@ -1106,6 +1287,83 @@ class Options implements OptionsElement<void> {
|
|||
}
|
||||
}
|
||||
}
|
||||
class Captcha implements OptionsElement<string> {
|
||||
owner: Form;
|
||||
value: string = "";
|
||||
constructor(owner: Form) {
|
||||
this.owner = owner;
|
||||
}
|
||||
div?: HTMLElement;
|
||||
generateHTML(): HTMLElement {
|
||||
const div = document.createElement("div");
|
||||
this.div = div;
|
||||
return div;
|
||||
}
|
||||
submit() {}
|
||||
onchange = (_: string) => {};
|
||||
watchForChange(func: (arg1: string) => void) {
|
||||
this.onchange = func;
|
||||
}
|
||||
static hcaptcha?: HTMLDivElement;
|
||||
static async waitForCaptcha(ctype: "hcaptcha") {
|
||||
switch (ctype) {
|
||||
case "hcaptcha":
|
||||
if (!this.hcaptcha) throw Error("no captcha found");
|
||||
const hcaptcha = this.hcaptcha;
|
||||
console.log(hcaptcha);
|
||||
//@ts-expect-error
|
||||
while (!hcaptcha.children[1].children.length || !hcaptcha.children[1].children[1].value) {
|
||||
await new Promise<void>((res) => setTimeout(res, 100));
|
||||
}
|
||||
//@ts-expect-error
|
||||
return hcaptcha.children[1].children[1].value;
|
||||
}
|
||||
}
|
||||
async makeCaptcha({
|
||||
captcha_sitekey,
|
||||
captcha_service,
|
||||
}: {
|
||||
captcha_sitekey: string;
|
||||
captcha_service: "hcaptcha";
|
||||
}): Promise<string> {
|
||||
if (!this.div) throw new Error("Div doesn't exist yet to give catpcha");
|
||||
switch (captcha_service) {
|
||||
case "hcaptcha":
|
||||
if (Captcha.hcaptcha) {
|
||||
this.div.append(Captcha.hcaptcha);
|
||||
Captcha.hcaptcha.setAttribute("data-sitekey", captcha_sitekey);
|
||||
eval("hcaptcha.reset()");
|
||||
return Captcha.waitForCaptcha(captcha_service);
|
||||
} else {
|
||||
const capt = document.createElement("div");
|
||||
const capty = document.createElement("div");
|
||||
capty.classList.add("h-captcha");
|
||||
|
||||
capty.setAttribute("data-sitekey", captcha_sitekey);
|
||||
const script = document.createElement("script");
|
||||
script.src = "https://js.hcaptcha.com/1/api.js";
|
||||
capt.append(script);
|
||||
capt.append(capty);
|
||||
Captcha.hcaptcha = capt;
|
||||
this.div.append(capt);
|
||||
return Captcha.waitForCaptcha(captcha_service);
|
||||
}
|
||||
}
|
||||
}
|
||||
static async makeCaptcha(json: {
|
||||
captcha_sitekey: string;
|
||||
captcha_service: "hcaptcha";
|
||||
}): Promise<string> {
|
||||
const float = new Dialog("", {noSubmit: true});
|
||||
float.options.addTitle(I18n.form.captcha());
|
||||
const cap = float.options.addForm("", () => {}, {traditionalSubmit: true}).addCaptcha();
|
||||
float.show();
|
||||
const ret = cap.makeCaptcha(json);
|
||||
await ret;
|
||||
float.hide();
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
class FormError extends Error {
|
||||
elem: OptionsElement<any>;
|
||||
message: string;
|
||||
|
@ -1115,6 +1373,53 @@ class FormError extends Error {
|
|||
this.elem = elem;
|
||||
}
|
||||
}
|
||||
async function handle2fa(json: any, api: string): Promise<false | any> {
|
||||
if (json.ticket) {
|
||||
return new Promise<boolean>((resolution) => {
|
||||
const better = new Dialog("");
|
||||
const form = better.options.addForm(
|
||||
"",
|
||||
(res: any) => {
|
||||
if (res.message) {
|
||||
throw new FormError(ti, res.message);
|
||||
} else {
|
||||
resolution(res);
|
||||
better.hide();
|
||||
}
|
||||
},
|
||||
{
|
||||
fetchURL: api + "/auth/mfa/totp",
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
},
|
||||
);
|
||||
form.addTitle(I18n.getTranslation("2faCode"));
|
||||
form.addPreprocessor((e) => {
|
||||
//@ts-ignore
|
||||
e.ticket = json.ticket;
|
||||
});
|
||||
const ti = form.addTextInput("", "code");
|
||||
better.show();
|
||||
});
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
async function handleCaptcha(json: any, build: any, cap: Captcha | undefined) {
|
||||
if (json.captcha_sitekey) {
|
||||
let token: string;
|
||||
if (cap) {
|
||||
token = await cap.makeCaptcha(json);
|
||||
} else {
|
||||
token = await Captcha.makeCaptcha(json);
|
||||
}
|
||||
build.captcha_key = token;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
export {FormError};
|
||||
class Form implements OptionsElement<object> {
|
||||
name: string;
|
||||
|
@ -1141,13 +1446,14 @@ class Form implements OptionsElement<object> {
|
|||
headers = {},
|
||||
method = "POST",
|
||||
traditionalSubmit = false,
|
||||
vsmaller = false,
|
||||
} = {},
|
||||
) {
|
||||
this.traditionalSubmit = traditionalSubmit;
|
||||
this.name = name;
|
||||
this.method = method;
|
||||
this.submitText = submitText;
|
||||
this.options = new Options(name, this, {ltr});
|
||||
this.options = new Options(name, this, {ltr, vsmaller});
|
||||
this.owner = owner;
|
||||
this.fetchURL = fetchURL;
|
||||
this.headers = headers;
|
||||
|
@ -1167,6 +1473,15 @@ class Form implements OptionsElement<object> {
|
|||
addHTMLArea(html: (() => HTMLElement) | HTMLElement, onSubmit = () => {}) {
|
||||
return this.options.addHTMLArea(html, onSubmit);
|
||||
}
|
||||
private captcha?: Captcha;
|
||||
addCaptcha() {
|
||||
if (this.captcha) throw new Error("only one captcha is allowed per form");
|
||||
const cap = new Captcha(this);
|
||||
this.options.options.push(cap);
|
||||
this.options.generate(cap);
|
||||
this.captcha = cap;
|
||||
return cap;
|
||||
}
|
||||
addSubForm(
|
||||
name: string,
|
||||
onSubmit: (arg1: object, sent: object) => void,
|
||||
|
@ -1247,7 +1562,16 @@ class Form implements OptionsElement<object> {
|
|||
this.names.set(formName, emoji);
|
||||
return emoji;
|
||||
}
|
||||
|
||||
addDateInput(label: string, formName: string, {initText = "", required = false} = {}) {
|
||||
const dateInput = this.options.addDateInput(label, (_) => {}, {
|
||||
initText,
|
||||
});
|
||||
this.names.set(formName, dateInput);
|
||||
if (required) {
|
||||
this.required.add(dateInput);
|
||||
}
|
||||
return dateInput;
|
||||
}
|
||||
addTextInput(
|
||||
label: string,
|
||||
formName: string,
|
||||
|
@ -1377,7 +1701,7 @@ class Form implements OptionsElement<object> {
|
|||
return;
|
||||
}
|
||||
} else {
|
||||
(build as any)[thing] = thing;
|
||||
(build as any)[key] = thing;
|
||||
}
|
||||
}
|
||||
console.log("middle");
|
||||
|
@ -1444,41 +1768,59 @@ class Form implements OptionsElement<object> {
|
|||
return;
|
||||
}
|
||||
if (this.fetchURL !== "") {
|
||||
fetch(this.fetchURL, {
|
||||
method: this.method,
|
||||
body: JSON.stringify(build),
|
||||
headers: this.headers,
|
||||
})
|
||||
.then((_) => {
|
||||
return _.text();
|
||||
})
|
||||
.then((_) => {
|
||||
if (_ === "") return {};
|
||||
return JSON.parse(_);
|
||||
})
|
||||
.then(async (json) => {
|
||||
if (json.errors) {
|
||||
if (this.errors(json)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
try {
|
||||
await this.onSubmit(json, build);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e instanceof FormError) {
|
||||
this.onFormError(e);
|
||||
const elm = this.options.html.get(e.elem);
|
||||
if (elm) {
|
||||
const html = elm.deref();
|
||||
if (html) {
|
||||
this.makeError(html, e.message);
|
||||
}
|
||||
const onSubmit = async (json: any) => {
|
||||
try {
|
||||
await this.onSubmit(json, build);
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
if (e instanceof FormError) {
|
||||
this.onFormError(e);
|
||||
const elm = this.options.html.get(e.elem);
|
||||
if (elm) {
|
||||
const html = elm.deref();
|
||||
if (html) {
|
||||
this.makeError(html, e.message);
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
});
|
||||
return;
|
||||
}
|
||||
};
|
||||
const doFetch = async () => {
|
||||
fetch(this.fetchURL, {
|
||||
method: this.method,
|
||||
body: JSON.stringify(build),
|
||||
headers: this.headers,
|
||||
})
|
||||
.then((_) => {
|
||||
return _.text();
|
||||
})
|
||||
.then((_) => {
|
||||
if (_ === "") return {};
|
||||
return JSON.parse(_);
|
||||
})
|
||||
.then(async (json) => {
|
||||
if (await handleCaptcha(json, build, this.captcha)) {
|
||||
return await doFetch();
|
||||
}
|
||||
const match = this.fetchURL.match(/https?:\/\/[^\/]*\/api\/v9/gm);
|
||||
if (match) {
|
||||
const tried = await handle2fa(json, match[0]);
|
||||
if (tried) {
|
||||
return await onSubmit(tried);
|
||||
}
|
||||
}
|
||||
if (json.ticket) {
|
||||
}
|
||||
if (json.errors) {
|
||||
if (this.errors(json)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
onSubmit(json);
|
||||
});
|
||||
};
|
||||
doFetch();
|
||||
} else {
|
||||
try {
|
||||
await this.onSubmit(build, build);
|
||||
|
|
|
@ -782,6 +782,9 @@ span.instanceStatus {
|
|||
#verify {
|
||||
color: var(--primary-text-soft);
|
||||
}
|
||||
.verify {
|
||||
color: var(--primary-text-soft);
|
||||
}
|
||||
#TOS {
|
||||
vertical-align: middle;
|
||||
margin-bottom: 4px;
|
||||
|
@ -790,6 +793,15 @@ span.instanceStatus {
|
|||
margin: 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
.createAccount {
|
||||
width: 94%;
|
||||
padding: 8px;
|
||||
margin-bottom: 16px;
|
||||
font-size: 1.15rem;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
#logindiv button {
|
||||
width: 100%;
|
||||
padding: 8px;
|
||||
|
@ -2216,9 +2228,9 @@ img.bigembedimg {
|
|||
}
|
||||
.nonimagecenter,
|
||||
.accountSwitcher {
|
||||
max-height: 80svh;
|
||||
max-height: 95svh;
|
||||
width: 500px;
|
||||
padding: 12px;
|
||||
padding: 6px;
|
||||
margin: 0;
|
||||
background: var(--secondary-bg);
|
||||
border: none;
|
||||
|
@ -2227,7 +2239,6 @@ img.bigembedimg {
|
|||
0 0 24px var(--shadow),
|
||||
0 0 1.5px var(--primary-text);
|
||||
box-sizing: border-box;
|
||||
gap: 8px;
|
||||
overflow-y: auto;
|
||||
}
|
||||
.nonimagecenter & .flexttb,
|
||||
|
@ -2484,6 +2495,11 @@ fieldset input[type="radio"] {
|
|||
max-width: 4in;
|
||||
height: 2in;
|
||||
}
|
||||
.vsmaller {
|
||||
.optionElement {
|
||||
margin-top: 8px;
|
||||
}
|
||||
}
|
||||
.optionElement,
|
||||
.FormSettings > button {
|
||||
margin: 16px 16px 0 16px;
|
||||
|
@ -2501,6 +2517,7 @@ fieldset input[type="radio"] {
|
|||
}
|
||||
.optionElement:has(.optionElement) {
|
||||
margin: 0;
|
||||
position: relative;
|
||||
}
|
||||
.optionElement:has(.Buttons) {
|
||||
height: 100%;
|
||||
|
@ -3056,3 +3073,7 @@ fieldset input[type="radio"] {
|
|||
.stickerMArea {
|
||||
padding-left: 48px;
|
||||
}
|
||||
.solidBackground {
|
||||
background: var(--secondary-bg);
|
||||
opacity: 1;
|
||||
}
|
||||
|
|
|
@ -1,5 +1,24 @@
|
|||
import {I18n} from "../i18n.js";
|
||||
import {Dialog} from "../settings.js";
|
||||
let instances:
|
||||
| {
|
||||
name: string;
|
||||
description?: string;
|
||||
descriptionLong?: string;
|
||||
image?: string;
|
||||
url?: string;
|
||||
display?: boolean;
|
||||
online?: boolean;
|
||||
uptime: {alltime: number; daytime: number; weektime: number};
|
||||
urls: {
|
||||
wellknown: string;
|
||||
api: string;
|
||||
cdn: string;
|
||||
gateway: string;
|
||||
login?: string;
|
||||
};
|
||||
}[]
|
||||
| null = null;
|
||||
setTheme();
|
||||
export function setTheme() {
|
||||
let name = localStorage.getItem("theme");
|
||||
|
@ -172,6 +191,51 @@ export class Specialuser {
|
|||
localStorage.setItem("userinfos", JSON.stringify(info));
|
||||
}
|
||||
}
|
||||
//this currently does not work, and need to be implemented better at some time.
|
||||
if (!localStorage.getItem("SWMode")) {
|
||||
localStorage.setItem("SWMode", "SWOn");
|
||||
}
|
||||
export function trimswitcher() {
|
||||
const json = getBulkInfo();
|
||||
const map = new Map();
|
||||
for (const thing in json.users) {
|
||||
const user = json.users[thing];
|
||||
let wellknown = user.serverurls.wellknown;
|
||||
if (wellknown.at(-1) !== "/") {
|
||||
wellknown += "/";
|
||||
}
|
||||
wellknown = (user.id || user.email) + "@" + wellknown;
|
||||
if (map.has(wellknown)) {
|
||||
const otheruser = map.get(wellknown);
|
||||
if (otheruser[1].serverurls.wellknown.at(-1) === "/") {
|
||||
delete json.users[otheruser[0]];
|
||||
map.set(wellknown, [thing, user]);
|
||||
} else {
|
||||
delete json.users[thing];
|
||||
}
|
||||
} else {
|
||||
map.set(wellknown, [thing, user]);
|
||||
}
|
||||
}
|
||||
for (const thing in json.users) {
|
||||
if (thing.at(-1) === "/") {
|
||||
const user = json.users[thing];
|
||||
delete json.users[thing];
|
||||
json.users[thing.slice(0, -1)] = user;
|
||||
}
|
||||
}
|
||||
localStorage.setItem("userinfos", JSON.stringify(json));
|
||||
console.log(json);
|
||||
}
|
||||
export function adduser(user: typeof Specialuser.prototype.json) {
|
||||
user = new Specialuser(user);
|
||||
const info = getBulkInfo();
|
||||
info.users[user.uid] = user;
|
||||
info.currentuser = user.uid;
|
||||
sessionStorage.setItem("currentuser", user.uid);
|
||||
localStorage.setItem("userinfos", JSON.stringify(info));
|
||||
return user;
|
||||
}
|
||||
class Directory {
|
||||
static home = this.createHome();
|
||||
handle: FileSystemDirectoryHandle;
|
||||
|
@ -257,28 +321,10 @@ export {Directory};
|
|||
const mobile = /iPhone|iPad|iPod|Android/i.test(navigator.userAgent);
|
||||
const iOS = /iPhone|iPad|iPod/i.test(navigator.userAgent);
|
||||
export {mobile, iOS};
|
||||
let instances:
|
||||
| {
|
||||
name: string;
|
||||
description?: string;
|
||||
descriptionLong?: string;
|
||||
image?: string;
|
||||
url?: string;
|
||||
display?: boolean;
|
||||
online?: boolean;
|
||||
uptime: {alltime: number; daytime: number; weektime: number};
|
||||
urls: {
|
||||
wellknown: string;
|
||||
api: string;
|
||||
cdn: string;
|
||||
gateway: string;
|
||||
login?: string;
|
||||
};
|
||||
}[]
|
||||
| null;
|
||||
|
||||
const datalist = document.getElementById("instances");
|
||||
console.warn(datalist);
|
||||
const instancefetch = fetch("/instances.json")
|
||||
export const instancefetch = fetch("/instances.json")
|
||||
.then((res) => res.json())
|
||||
.then(
|
||||
async (
|
||||
|
@ -302,48 +348,6 @@ const instancefetch = fetch("/instances.json")
|
|||
) => {
|
||||
await I18n.done;
|
||||
instances = json;
|
||||
if (datalist) {
|
||||
console.warn(json);
|
||||
const instancein = document.getElementById("instancein") as HTMLInputElement;
|
||||
if (
|
||||
instancein &&
|
||||
instancein.value === "" &&
|
||||
!new URLSearchParams(window.location.search).get("instance")
|
||||
) {
|
||||
instancein.value = json[0].name;
|
||||
}
|
||||
for (const instance of json) {
|
||||
if (instance.display === false) {
|
||||
continue;
|
||||
}
|
||||
const option = document.createElement("option");
|
||||
option.disabled = !instance.online;
|
||||
option.value = instance.name;
|
||||
if (instance.url) {
|
||||
stringURLMap.set(option.value, instance.url);
|
||||
if (instance.urls) {
|
||||
stringURLsMap.set(instance.url, instance.urls);
|
||||
}
|
||||
} else if (instance.urls) {
|
||||
stringURLsMap.set(option.value, instance.urls);
|
||||
} else {
|
||||
option.disabled = true;
|
||||
}
|
||||
if (instance.description) {
|
||||
option.label = instance.description;
|
||||
} else {
|
||||
option.label = instance.name;
|
||||
}
|
||||
datalist.append(option);
|
||||
}
|
||||
if (
|
||||
json.length !== 0 &&
|
||||
!localStorage.getItem("instanceinfo") &&
|
||||
!new URLSearchParams(window.location.search).get("instance")
|
||||
) {
|
||||
checkInstance(json[0].name);
|
||||
}
|
||||
}
|
||||
},
|
||||
);
|
||||
const stringURLMap = new Map<string, string>();
|
||||
|
@ -646,6 +650,14 @@ export function createImg(
|
|||
},
|
||||
});
|
||||
}
|
||||
export interface instanceinfo {
|
||||
wellknown: string;
|
||||
api: string;
|
||||
cdn: string;
|
||||
gateway: string;
|
||||
login: string;
|
||||
value: string;
|
||||
}
|
||||
/**
|
||||
*
|
||||
* This function takes in a string and checks if the string is a valid instance
|
||||
|
@ -653,24 +665,19 @@ export function createImg(
|
|||
* the alt property is something you may fire on success.
|
||||
*/
|
||||
const checkInstance = Object.assign(
|
||||
async function (instance: string) {
|
||||
await instancefetch;
|
||||
const verify = document.getElementById("verify");
|
||||
const loginButton = (document.getElementById("loginButton") ||
|
||||
async function (
|
||||
instance: string,
|
||||
verify = document.getElementById("verify"),
|
||||
loginButton = (document.getElementById("loginButton") ||
|
||||
document.getElementById("createAccount") ||
|
||||
document.createElement("button")) as HTMLButtonElement;
|
||||
document.createElement("button")) as HTMLButtonElement,
|
||||
) {
|
||||
await instancefetch;
|
||||
try {
|
||||
loginButton.disabled = true;
|
||||
verify!.textContent = I18n.getTranslation("login.checking");
|
||||
const instanceValue = instance;
|
||||
const instanceinfo = (await getapiurls(instanceValue)) as {
|
||||
wellknown: string;
|
||||
api: string;
|
||||
cdn: string;
|
||||
gateway: string;
|
||||
login: string;
|
||||
value: string;
|
||||
};
|
||||
const instanceinfo = (await getapiurls(instanceValue)) as instanceinfo;
|
||||
if (instanceinfo) {
|
||||
instanceinfo.value = instanceValue;
|
||||
localStorage.setItem("instanceinfo", JSON.stringify(instanceinfo));
|
||||
|
@ -683,14 +690,17 @@ const checkInstance = Object.assign(
|
|||
console.log(verify!.textContent);
|
||||
verify!.textContent = "";
|
||||
}, 3000);
|
||||
return instanceinfo;
|
||||
} else {
|
||||
verify!.textContent = I18n.getTranslation("login.invalid");
|
||||
loginButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
} catch {
|
||||
console.log("catch");
|
||||
verify!.textContent = I18n.getTranslation("login.invalid");
|
||||
loginButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
},
|
||||
{} as {
|
||||
|
@ -705,9 +715,7 @@ const checkInstance = Object.assign(
|
|||
},
|
||||
);
|
||||
export {checkInstance};
|
||||
export function getInstances() {
|
||||
return instances;
|
||||
}
|
||||
|
||||
export class SW {
|
||||
static worker: undefined | ServiceWorker;
|
||||
static setMode(mode: "false" | "offlineOnly" | "true") {
|
||||
|
@ -727,7 +735,14 @@ export class SW {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
let installPrompt: Event | undefined = undefined;
|
||||
window.addEventListener("beforeinstallprompt", (event) => {
|
||||
event.preventDefault();
|
||||
installPrompt = event;
|
||||
});
|
||||
export function installPGet() {
|
||||
return installPrompt;
|
||||
}
|
||||
if ("serviceWorker" in navigator) {
|
||||
navigator.serviceWorker
|
||||
.register("/service.js", {
|
||||
|
@ -755,3 +770,9 @@ if ("serviceWorker" in navigator) {
|
|||
}
|
||||
});
|
||||
}
|
||||
export function getInstances() {
|
||||
return instances;
|
||||
}
|
||||
export function getStringURLMapPair() {
|
||||
return [stringURLMap, stringURLsMap] as const;
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue