From 59ebf52fe2ffc9c3fc37167aaf08c71e79e712e0 Mon Sep 17 00:00:00 2001 From: Harvey Tindall Date: Sun, 30 May 2021 00:05:46 +0100 Subject: [PATCH] Matrix: Show matrix on accounts page --- api.go | 62 ++++++--- html/admin.html | 4 + models.go | 3 + ts/modules/accounts.ts | 276 +++++++++++++++++++++++------------------ views.go | 1 + 5 files changed, 207 insertions(+), 139 deletions(-) diff --git a/api.go b/api.go index 6dd10c7..5dfbdcb 100644 --- a/api.go +++ b/api.go @@ -1277,10 +1277,14 @@ func (app *appContext) GetUsers(gc *gin.Context) { user.Telegram = tgUser.Username user.NotifyThroughTelegram = tgUser.Contact } - if dc, ok := app.storage.discord[jfUser.ID]; ok { - user.Discord = dc.Username + "#" + dc.Discriminator - user.DiscordID = dc.ID - user.NotifyThroughDiscord = dc.Contact + if mxUser, ok := app.storage.matrix[jfUser.ID]; ok { + user.Matrix = mxUser.UserID + user.NotifyThroughMatrix = mxUser.Contact + } + if dcUser, ok := app.storage.discord[jfUser.ID]; ok { + user.Discord = dcUser.Username + "#" + dcUser.Discriminator + user.DiscordID = dcUser.ID + user.NotifyThroughDiscord = dcUser.Contact } resp.UserList[i] = user i++ @@ -2127,6 +2131,7 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { return } if tgUser, ok := app.storage.telegram[req.ID]; ok { + change := tgUser.Contact != req.Telegram tgUser.Contact = req.Telegram app.storage.telegram[req.ID] = tgUser if err := app.storage.storeTelegramUsers(); err != nil { @@ -2134,13 +2139,16 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { app.err.Printf("Telegram: Failed to store users: %v", err) return } - msg := "" - if !req.Telegram { - msg = " not" + if change { + msg := "" + if !req.Telegram { + msg = " not" + } + app.debug.Printf("Telegram: User \"%s\" will%s be notified through Telegram.", tgUser.Username, msg) } - app.debug.Printf("Telegram: User \"%s\" will%s be notified through Telegram.", tgUser.Username, msg) } if dcUser, ok := app.storage.discord[req.ID]; ok { + change := dcUser.Contact != req.Discord dcUser.Contact = req.Discord app.storage.discord[req.ID] = dcUser if err := app.storage.storeDiscordUsers(); err != nil { @@ -2148,13 +2156,33 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { app.err.Printf("Discord: Failed to store users: %v", err) return } - msg := "" - if !req.Discord { - msg = " not" + if change { + msg := "" + if !req.Discord { + msg = " not" + } + app.debug.Printf("Discord: User \"%s\" will%s be notified through Discord.", dcUser.Username, msg) + } + } + if mxUser, ok := app.storage.matrix[req.ID]; ok { + change := mxUser.Contact != req.Matrix + mxUser.Contact = req.Matrix + app.storage.matrix[req.ID] = mxUser + if err := app.storage.storeMatrixUsers(); err != nil { + respondBool(500, false, gc) + app.err.Printf("Matrix: Failed to store users: %v", err) + return + } + if change { + msg := "" + if !req.Matrix { + msg = " not" + } + app.debug.Printf("Matrix: User \"%s\" will%s be notified through Matrix.", mxUser.UserID, msg) } - app.debug.Printf("Discord: User \"%s\" will%s be notified through Discord.", dcUser.Username, msg) } if email, ok := app.storage.emails[req.ID]; ok { + change := email.Contact != req.Email email.Contact = req.Email app.storage.emails[req.ID] = email if err := app.storage.storeEmails(); err != nil { @@ -2162,11 +2190,13 @@ func (app *appContext) SetContactMethods(gc *gin.Context) { app.err.Printf("Failed to store emails: %v", err) return } - msg := "" - if !req.Email { - msg = " not" + if change { + msg := "" + if !req.Email { + msg = " not" + } + app.debug.Printf("\"%s\" will%s be notified via Email.", email.Addr, msg) } - app.debug.Printf("\"%s\" will%s be notified via Email.", email.Addr, msg) } respondBool(200, true, gc) } diff --git a/html/admin.html b/html/admin.html index 31db2b2..7591ed4 100644 --- a/html/admin.html +++ b/html/admin.html @@ -8,6 +8,7 @@ window.emailEnabled = {{ .email_enabled }}; window.telegramEnabled = {{ .telegram_enabled }}; window.discordEnabled = {{ .discord_enabled }}; + window.matrixEnabled = {{ .matrix_enabled }}; window.ombiEnabled = {{ .ombiEnabled }}; window.usernameEnabled = {{ .username }}; window.langFile = JSON.parse({{ .language }}); @@ -545,6 +546,9 @@ {{ if .telegram_enabled }} Telegram {{ end }} + {{ if .matrix_enabled }} + Matrix + {{ end }} {{ if .discord_enabled }} Discord {{ end }} diff --git a/models.go b/models.go index 950ccaa..f75eefb 100644 --- a/models.go +++ b/models.go @@ -139,6 +139,8 @@ type respUser struct { Discord string `json:"discord"` // Discord username (if known) DiscordID string `json:"discord_id"` // Discord user ID for creating links. NotifyThroughDiscord bool `json:"notify_discord"` + Matrix string `json:"matrix"` // Matrix ID (if known) + NotifyThroughMatrix bool `json:"notify_matrix"` } type getUsersDTO struct { @@ -262,6 +264,7 @@ type SetContactMethodsDTO struct { Email bool `json:"email"` Discord bool `json:"discord"` Telegram bool `json:"telegram"` + Matrix bool `json:"matrix"` } type DiscordUserDTO struct { diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index b00ef97..33a1b14 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -18,6 +18,8 @@ interface User { discord: string; notify_discord: boolean; discord_id: string; + matrix: string; + notify_matrix: boolean; } interface getPinResponse { @@ -44,13 +46,27 @@ class user implements User { private _discordUsername: string; private _discordID: string; private _notifyDiscord: boolean; + private _matrix: HTMLTableDataCellElement; + private _matrixID: string; + private _notifyMatrix: boolean; private _expiry: HTMLTableDataCellElement; private _expiryUnix: number; private _lastActive: HTMLTableDataCellElement; private _lastActiveUnix: number; + private _notifyDropdown: HTMLDivElement; id = ""; private _selected: boolean; + private _lastNotifyMethod = (): string => { + // Telegram, Matrix, Discord + const telegram = this._telegramUsername && this._telegramUsername != ""; + const discord = this._discordUsername && this._discordUsername != ""; + const matrix = this._matrixID && this._matrixID != ""; + if (discord) return "discord"; + if (matrix) return "matrix"; + if (telegram) return "telegram"; + } + get selected(): boolean { return this._selected; } set selected(state: boolean) { this._selected = state; @@ -96,105 +112,146 @@ class user implements User { get notify_email(): boolean { return this._notifyEmail; } set notify_email(s: boolean) { - this._notifyEmail = s; - if (window.telegramEnabled && this._telegramUsername != "") { - const email = this._telegram.getElementsByClassName("accounts-contact-email")[0] as HTMLInputElement; - if (email) { - email.checked = s; + if (this._notifyDropdown) { + (this._notifyDropdown.querySelector(".accounts-contact-email") as HTMLInputElement).checked = s; + } + } + + private _constructDropdown = (): HTMLDivElement => { + const el = document.createElement("div") as HTMLDivElement; + const telegram = this._telegramUsername != ""; + const discord = this._discordUsername != ""; + const matrix = this._matrixID != ""; + if (!telegram && !discord && !matrix) return; + let innerHTML = ` + + + `; + el.innerHTML = innerHTML; + const button = el.querySelector("i"); + const dropdown = el.querySelector("div.dropdown") as HTMLDivElement; + const checks = el.querySelectorAll("input") as NodeListOf; + for (let i = 0; i < checks.length; i++) { + checks[i].onclick = () => this._setNotifyMethod(); + } + + button.onclick = () => { + dropdown.classList.add("selected"); + document.addEventListener("click", outerClickListener); + }; + const outerClickListener = (event: Event) => { + if (!(event.target instanceof HTMLElement && (el.contains(event.target) || button.contains(event.target)))) { + dropdown.classList.remove("selected"); + document.removeEventListener("click", outerClickListener); + } + }; + return el; + } + + get matrix(): string { return this._matrixID; } + set matrix(u: string) { + if (!window.matrixEnabled) { + this._notifyDropdown.querySelector(".accounts-area-matrix").classList.add("unfocused"); + return; + } + const lastNotifyMethod = this._lastNotifyMethod() == "matrix"; + this._matrixID = u; + if (!u) { + this._notifyDropdown.querySelector(".accounts-area-matrix").classList.add("unfocused"); + this._matrix.innerHTML = `${window.lang.strings("add")}`; + // (this._matrix.querySelector("span") as HTMLSpanElement).onclick = this._addMatrix; + } else { + this._notifyDropdown.querySelector(".accounts-area-matrix").classList.remove("unfocused"); + this._matrix.innerHTML = ` +
+ ${u} +
+ `; + if (lastNotifyMethod) { + (this._matrix.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown); } } - if (window.discordEnabled && this._discordUsername) { - const email = this._discord.getElementsByClassName("accounts-contact-email")[0] as HTMLInputElement; - email.checked = s; + } + + get notify_matrix(): boolean { return this._notifyMatrix; } + set notify_matrix(s: boolean) { + if (this._notifyDropdown) { + (this._notifyDropdown.querySelector(".accounts-contact-matrix") as HTMLInputElement).checked = s; } } get telegram(): string { return this._telegramUsername; } set telegram(u: string) { - if (!window.telegramEnabled) return; + if (!window.telegramEnabled) { + this._notifyDropdown.querySelector(".accounts-area-telegram").classList.add("unfocused"); + return; + } + const lastNotifyMethod = this._lastNotifyMethod() == "telegram"; this._telegramUsername = u; - if (u == "") { - this._telegram.innerHTML = `Add`; + if (!u) { + this._notifyDropdown.querySelector(".accounts-area-telegram").classList.add("unfocused"); + this._telegram.innerHTML = `${window.lang.strings("add")}`; (this._telegram.querySelector("span") as HTMLSpanElement).onclick = this._addTelegram; } else { - let innerHTML = ` + this._notifyDropdown.querySelector(".accounts-area-telegram").classList.remove("unfocused"); + this._telegram.innerHTML = `
@${u} +
`; - if (!window.discordEnabled || !this._discordUsername) { - innerHTML += ` - - - `; - } - innerHTML += ""; - this._telegram.innerHTML = innerHTML; - if (!window.discordEnabled || !this._discordUsername) { - // Javascript is necessary as including the button inside the dropdown would make it too wide to display next to the username. - const button = this._telegram.querySelector("i"); - const dropdown = this._telegram.querySelector("div.dropdown") as HTMLDivElement; - const checks = this._telegram.querySelectorAll("input") as NodeListOf; - for (let i = 0; i < checks.length; i++) { - checks[i].onclick = () => this._setNotifyMethod("telegram"); - } - - button.onclick = () => { - dropdown.classList.add("selected"); - document.addEventListener("click", outerClickListener); - }; - const outerClickListener = (event: Event) => { - if (!(event.target instanceof HTMLElement && (this._telegram.contains(event.target) || button.contains(event.target)))) { - dropdown.classList.remove("selected"); - document.removeEventListener("click", outerClickListener); - } - }; + if (lastNotifyMethod) { + (this._telegram.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown); } } } get notify_telegram(): boolean { return this._notifyTelegram; } set notify_telegram(s: boolean) { - if (!window.telegramEnabled || !this._telegramUsername) return; - this._notifyTelegram = s; - const telegram = this._telegram.getElementsByClassName("accounts-contact-telegram")[0] as HTMLInputElement; - if (telegram) { - telegram.checked = s; - } - if (window.discordEnabled && this._discordUsername) { - const telegram = this._discord.getElementsByClassName("accounts-contact-telegram")[0] as HTMLInputElement; - telegram.checked = s; + if (this._notifyDropdown) { + (this._notifyDropdown.querySelector(".accounts-contact-telegram") as HTMLInputElement).checked = s; } } - private _setNotifyMethod = (mode: string = "telegram") => { - let el: HTMLElement; - if (mode == "telegram") { el = this._telegram } - else if (mode == "discord") { el = this._discord } - const email = el.getElementsByClassName("accounts-contact-email")[0] as HTMLInputElement; + private _setNotifyMethod = () => { + const email = this._notifyDropdown.getElementsByClassName("accounts-contact-email")[0] as HTMLInputElement; let send = { id: this.id, email: email.checked } if (window.telegramEnabled && this._telegramUsername) { - const telegram = el.getElementsByClassName("accounts-contact-telegram")[0] as HTMLInputElement; + const telegram = this._notifyDropdown.getElementsByClassName("accounts-contact-telegram")[0] as HTMLInputElement; send["telegram"] = telegram.checked; } if (window.discordEnabled && this._discordUsername) { - const discord = el.getElementsByClassName("accounts-contact-discord")[0] as HTMLInputElement; + const discord = this._notifyDropdown.getElementsByClassName("accounts-contact-discord")[0] as HTMLInputElement; send["discord"] = discord.checked; } _post("/users/contact", send, (req: XMLHttpRequest) => { @@ -219,62 +276,26 @@ class user implements User { get discord(): string { return this._discordUsername; } set discord(u: string) { - if (!window.discordEnabled) return; + if (!window.discordEnabled) { + this._notifyDropdown.querySelector(".accounts-area-discord").classList.add("unfocused"); + return; + } + const lastNotifyMethod = this._lastNotifyMethod() == "discord"; this._discordUsername = u; - if (u == "") { + if (!u) { this._discord.innerHTML = `Add`; (this._discord.querySelector("span") as HTMLSpanElement).onclick = () => addDiscord(this.id); + this._notifyDropdown.querySelector(".accounts-area-discord").classList.add("unfocused"); } else { - let innerHTML = ` + this._notifyDropdown.querySelector(".accounts-area-discord").classList.remove("unfocused"); + this._discord.innerHTML = `
${u} - -
`; - this._discord.innerHTML = innerHTML; - // Javascript is necessary as including the button inside the dropdown would make it too wide to display next to the username. - const button = this._discord.querySelector("i"); - const dropdown = this._discord.querySelector("div.dropdown") as HTMLDivElement; - const checks = this._discord.querySelectorAll("input") as NodeListOf; - for (let i = 0; i < checks.length; i++) { - checks[i].onclick = () => this._setNotifyMethod("discord"); + if (lastNotifyMethod) { + (this._discord.querySelector(".table-inline") as HTMLDivElement).appendChild(this._notifyDropdown); } - - button.onclick = () => { - dropdown.classList.add("selected"); - document.addEventListener("click", outerClickListener); - }; - const outerClickListener = (event: Event) => { - if (!(event.target instanceof HTMLElement && (this._discord.contains(event.target) || button.contains(event.target)))) { - dropdown.classList.remove("selected"); - document.removeEventListener("click", outerClickListener); - } - }; } } @@ -288,13 +309,8 @@ class user implements User { get notify_discord(): boolean { return this._notifyDiscord; } set notify_discord(s: boolean) { - if (!window.discordEnabled || !this._discordUsername) return; - this._notifyDiscord = s; - const discord = this._discord.getElementsByClassName("accounts-contact-discord")[0] as HTMLInputElement; - discord.checked = s; - if (window.telegramEnabled && this._telegramUsername != "") { - const discord = this._discord.getElementsByClassName("accounts-contact-discord")[0] as HTMLInputElement; - discord.checked = s; + if (this._notifyDropdown) { + (this._notifyDropdown.querySelector(".accounts-contact-discord") as HTMLInputElement).checked = s; } } @@ -333,6 +349,11 @@ class user implements User { `; } + if (window.matrixEnabled) { + innerHTML += ` + + `; + } if (window.discordEnabled) { innerHTML += ` @@ -352,10 +373,13 @@ class user implements User { this._emailEditButton = this._row.querySelector(".accounts-email-edit") as HTMLElement; this._telegram = this._row.querySelector(".accounts-telegram") as HTMLTableDataCellElement; this._discord = this._row.querySelector(".accounts-discord") as HTMLTableDataCellElement; + this._matrix = this._row.querySelector(".accounts-matrix") as HTMLTableDataCellElement; this._expiry = this._row.querySelector(".accounts-expiry") as HTMLTableDataCellElement; this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement; this._check.onchange = () => { this.selected = this._check.checked; } + this._notifyDropdown = this._constructDropdown(); + const toggleStealthInput = () => { if (this._emailEditButton.classList.contains("ri-edit-line")) { this._email.innerHTML = emailEditor; @@ -458,14 +482,20 @@ class user implements User { this.id = user.id; this.name = user.name; this.email = user.email || ""; + // Little hack to get settings cogs to appear on first load + this._discordUsername = user.discord; + this._telegramUsername = user.telegram; + this._matrixID = user.matrix; this.discord = user.discord; this.telegram = user.telegram; + this.matrix = user.matrix; this.last_active = user.last_active; this.admin = user.admin; this.disabled = user.disabled; this.expiry = user.expiry; this.notify_discord = user.notify_discord; this.notify_telegram = user.notify_telegram; + this.notify_matrix = user.notify_matrix; this.notify_email = user.notify_email; this.discord_id = user.discord_id; } diff --git a/views.go b/views.go index f463fd9..044e744 100644 --- a/views.go +++ b/views.go @@ -123,6 +123,7 @@ func (app *appContext) AdminPage(gc *gin.Context) { "email_enabled": emailEnabled, "telegram_enabled": telegramEnabled, "discord_enabled": discordEnabled, + "matrix_enabled": matrixEnabled, "notifications": notificationsEnabled, "version": version, "commit": commit,