diff --git a/src/webpage/icons/domain.svg b/src/webpage/icons/domain.svg new file mode 100644 index 0000000..c190182 --- /dev/null +++ b/src/webpage/icons/domain.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/src/webpage/localuser.ts b/src/webpage/localuser.ts index 556cc99..3799688 100644 --- a/src/webpage/localuser.ts +++ b/src/webpage/localuser.ts @@ -2501,9 +2501,11 @@ class Localuser { }; } async getConnections() { - return fetch(this.info.api + "/connections", { + const json = await fetch(this.info.api + "/connections", { headers: this.headers, }).then((r) => r.json() as Promise<{[key: string]: {enabled: boolean; icon_url?: string}}>); + json["domain"] = {enabled: true, icon_url: "/icons/domain.svg"}; + return json; } gifProvideors: {name: string; api_name: string}[] = []; selectedGifProfidor?: {name: string; api_name: string}; @@ -2530,6 +2532,44 @@ class Localuser { this.selectedGifProfidor = this.gifProvideors.find((_) => _.api_name == prefs.gifProvidor) || this.gifProvideors[0]; } + domainVerification() { + const d = new Dialog(I18n.domain.title(), {noSubmit: true}); + const text = d.options.addTextInput(I18n.domain.domain(), () => {}); + d.options.addButtonInput("", I18n.submit(), async () => { + const dom = text.value; + d.options.removeAll(); + console.log(dom); + const res = await fetch(this.info.api + "/users/@me/connections/domain/" + dom, { + method: "POST", + headers: this.headers, + }); + if (res.ok) { + d.hide(); + } else { + const json = (await res.json()) as {code?: number; message: string; proof: string}; + if (json.code === 50187) { + const err = d.options.addErrorBox(); + d.options.addMDText(new MarkDown(I18n.domain.dnsinst(dom, json.proof))); + + d.options.addButtonInput("", I18n.submit(), async () => { + const dom = text.value; + const res = await fetch(this.info.api + "/users/@me/connections/domain/" + dom, { + method: "POST", + headers: this.headers, + }); + if (res.ok) { + d.hide(); + } else { + err.showError((await res.json()).message); + } + }); + } else { + d.options.addText(json.message); + } + } + }); + d.show(); + } async showusersettings() { const prefs = await getPreferences(); const localSettings = getLocalSettings(); @@ -2695,6 +2735,7 @@ class Localuser { serverConnections .filter((_) => !actConMap.has(_)) .forEach((key) => { + if (key === "domain") return; const connection = json[key]; const container = document.createElement("div"); @@ -2725,6 +2766,21 @@ class Localuser { connectionContainer.appendChild(container); }); + //TODO enable this once domain verification is ready within Harmony + if (false as true) { + const container = document.createElement("div"); + + const span = document.createElement("span"); + span.classList.add("conImg", "svgicon"); + span.style.setProperty("mask", `url("/icons/domain.svg")`); + container.append(span); + + container.addEventListener("click", async () => { + this.domainVerification(); + }); + + connectionContainer.appendChild(container); + } serverConnections .filter((_) => actConMap.has(_)) .forEach((_) => { diff --git a/src/webpage/settings.ts b/src/webpage/settings.ts index 3f3e428..88f9dd4 100644 --- a/src/webpage/settings.ts +++ b/src/webpage/settings.ts @@ -263,6 +263,20 @@ class DateInput extends TextInput { super.onChange(); } } +class ErrorBox implements OptionsElement { + div = document.createElement("div"); + value!: void; + generateHTML(): HTMLElement { + this.div.classList = ""; + return this.div; + } + showError(message: string) { + this.div.classList.add("errormsg"); + this.div.textContent = message; + } + submit() {} + watchForChange() {} +} class SettingsMDText implements OptionsElement { readonly onSubmit!: (str: string) => void; value!: void; @@ -1615,6 +1629,12 @@ class Options implements OptionsElement { this.generate(text); return text; } + addErrorBox() { + const box = new ErrorBox(); + this.options.push(box); + this.generate(box); + return box; + } addHR() { const rule = new HorizontalRule(); this.options.push(rule); diff --git a/src/webpage/style.css b/src/webpage/style.css index ba19884..e933c6d 100644 --- a/src/webpage/style.css +++ b/src/webpage/style.css @@ -4120,6 +4120,7 @@ fieldset input[type="radio"] { .SettingsButton { width: auto; border-radius: 3px 3px 1px 1px; + padding-right: 16px; } } .reportMenu { @@ -4363,6 +4364,12 @@ fieldset input[type="radio"] { overflow: hidden; flex-shrink: 0; } +.errormsg { + background: #ff000038; + padding: 4px; + border-radius: 4px; + border: solid #ff54003b; +} .permbox { display: flex; flex-wrap: wrap; @@ -4434,7 +4441,7 @@ fieldset input[type="radio"] { background: var(--primary-text); } .conProfImg { - margin-right: 4px; + margin-right: 12px; } .conProfDiv { margin: 6px; diff --git a/src/webpage/user.ts b/src/webpage/user.ts index 8fd50d0..867768b 100644 --- a/src/webpage/user.ts +++ b/src/webpage/user.ts @@ -1167,6 +1167,9 @@ class User extends SnowFlake { case "steam": { return `https://steamcommunity.com/profiles/${con.external_id}`; } + case "domain": { + return `https://${con.external_id}`; + } } } note?: string; diff --git a/translations/en.json b/translations/en.json index e8999e3..d0b5daa 100644 --- a/translations/en.json +++ b/translations/en.json @@ -988,6 +988,11 @@ "resolving": "resolving user", "unknown": "@unknown-user" }, + "domain":{ + "title":"Verify domain", + "domain":"Domain:", + "dnsinst":"1. Sign into your DNS provider\n\n2. Create a DNS record as follows:\n\nName:\n```\n_harmony.$1\n```\n\nType:\n```\nTXT\n```\n\nContent:\n```\n$2\n```\n\n-# It will likely take a few minutes for the DNS to propagate" + }, "vc": { "joinForStream": "Join the voice channel to watch", "joiningStream": "Joining stream...",