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...",