mirror of
https://github.com/hrfee/jfa-go.git
synced 2024-12-23 01:20:11 +00:00
accounts: add ability to label users
press the pen icon next to their username to add a nick-name to remind you who they are.
This commit is contained in:
parent
3294b27029
commit
4024334c0c
39
api.go
39
api.go
@ -1469,6 +1469,7 @@ func (app *appContext) GetUsers(gc *gin.Context) {
|
|||||||
if email, ok := app.storage.emails[jfUser.ID]; ok {
|
if email, ok := app.storage.emails[jfUser.ID]; ok {
|
||||||
user.Email = email.Addr
|
user.Email = email.Addr
|
||||||
user.NotifyThroughEmail = email.Contact
|
user.NotifyThroughEmail = email.Contact
|
||||||
|
user.Label = email.Label
|
||||||
}
|
}
|
||||||
expiry, ok := app.storage.users[jfUser.ID]
|
expiry, ok := app.storage.users[jfUser.ID]
|
||||||
if ok {
|
if ok {
|
||||||
@ -1579,6 +1580,44 @@ func (app *appContext) DeleteOmbiProfile(gc *gin.Context) {
|
|||||||
respondBool(204, true, gc)
|
respondBool(204, true, gc)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// @Summary Modify user's labels, which show next to their name in the accounts tab.
|
||||||
|
// @Produce json
|
||||||
|
// @Param modifyEmailsDTO body modifyEmailsDTO true "Map of userIDs to labels"
|
||||||
|
// @Success 204 {object} boolResponse
|
||||||
|
// @Failure 500 {object} boolResponse
|
||||||
|
// @Router /users/labels [post]
|
||||||
|
// @Security Bearer
|
||||||
|
// @tags Users
|
||||||
|
func (app *appContext) ModifyLabels(gc *gin.Context) {
|
||||||
|
var req modifyEmailsDTO
|
||||||
|
gc.BindJSON(&req)
|
||||||
|
app.debug.Println("Label modification requested")
|
||||||
|
users, status, err := app.jf.GetUsers(false)
|
||||||
|
if !(status == 200 || status == 204) || err != nil {
|
||||||
|
app.err.Printf("Failed to get users from Jellyfin (%d): %v", status, err)
|
||||||
|
respond(500, "Couldn't get users", gc)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for _, jfUser := range users {
|
||||||
|
id := jfUser.ID
|
||||||
|
if label, ok := req[id]; ok {
|
||||||
|
addr := ""
|
||||||
|
contact := true
|
||||||
|
if oldEmail, ok := app.storage.emails[id]; ok {
|
||||||
|
addr = oldEmail.Addr
|
||||||
|
contact = oldEmail.Contact
|
||||||
|
}
|
||||||
|
app.storage.emails[id] = EmailAddress{Addr: addr, Contact: contact, Label: label}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if err := app.storage.storeEmails(); err != nil {
|
||||||
|
app.err.Printf("Failed to store email list: %v", err)
|
||||||
|
respondBool(500, false, gc)
|
||||||
|
}
|
||||||
|
app.info.Println("Email list modified")
|
||||||
|
respondBool(204, true, gc)
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Modify user's email addresses.
|
// @Summary Modify user's email addresses.
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param modifyEmailsDTO body modifyEmailsDTO true "Map of userIDs to email addresses"
|
// @Param modifyEmailsDTO body modifyEmailsDTO true "Map of userIDs to email addresses"
|
||||||
|
@ -340,15 +340,15 @@
|
|||||||
<div class="modal-content wide card">
|
<div class="modal-content wide card">
|
||||||
<span class="heading">{{ .strings.updates }} <span class="modal-close">×</span></span>
|
<span class="heading">{{ .strings.updates }} <span class="modal-close">×</span></span>
|
||||||
<p class="content">
|
<p class="content">
|
||||||
<h2>
|
<h2 class="mt-2">
|
||||||
<a id="update-version"></a> (<span class="font-mono bg-inherit" id="update-commit"></span>)
|
<a id="update-version"></a> (<span class="font-mono bg-inherit" id="update-commit"></span>)
|
||||||
</h2>
|
</h2>
|
||||||
<p class="content" id="update-description"></p>
|
<p class="content mt-2" id="update-description"></p>
|
||||||
<p class="support" id="update-date"></p>
|
<p class="support mt-2" id="update-date"></p>
|
||||||
<div class="content markdown-box" id="update-changelog"></div>
|
<div class="content markdown-box mt-2" id="update-changelog"></div>
|
||||||
</p>
|
</p>
|
||||||
<span class="button ~info @low full-width center" id="update-download">{{ .strings.download }}</span>
|
<span class="button ~info @low full-width center mt-2" id="update-download">{{ .strings.download }}</span>
|
||||||
<span class="button ~urge @low full-width center" id="update-update">{{ .strings.update }}</span>
|
<span class="button ~urge @low full-width center mt-2" id="update-update">{{ .strings.update }}</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{{ if .telegramEnabled }}
|
{{ if .telegramEnabled }}
|
||||||
|
@ -145,6 +145,7 @@ type respUser struct {
|
|||||||
NotifyThroughDiscord bool `json:"notify_discord"`
|
NotifyThroughDiscord bool `json:"notify_discord"`
|
||||||
Matrix string `json:"matrix"` // Matrix ID (if known)
|
Matrix string `json:"matrix"` // Matrix ID (if known)
|
||||||
NotifyThroughMatrix bool `json:"notify_matrix"`
|
NotifyThroughMatrix bool `json:"notify_matrix"`
|
||||||
|
Label string `json:"label"` // Label of user, shown next to their name.
|
||||||
}
|
}
|
||||||
|
|
||||||
type getUsersDTO struct {
|
type getUsersDTO struct {
|
||||||
|
@ -160,6 +160,7 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
|
|||||||
api.DELETE(p+"/profiles", app.DeleteProfile)
|
api.DELETE(p+"/profiles", app.DeleteProfile)
|
||||||
api.POST(p+"/invites/notify", app.SetNotify)
|
api.POST(p+"/invites/notify", app.SetNotify)
|
||||||
api.POST(p+"/users/emails", app.ModifyEmails)
|
api.POST(p+"/users/emails", app.ModifyEmails)
|
||||||
|
api.POST(p+"/users/labels", app.ModifyLabels)
|
||||||
// api.POST(p + "/setDefaults", app.SetDefaults)
|
// api.POST(p + "/setDefaults", app.SetDefaults)
|
||||||
api.POST(p+"/users/settings", app.ApplySettings)
|
api.POST(p+"/users/settings", app.ApplySettings)
|
||||||
api.POST(p+"/users/announce", app.Announce)
|
api.POST(p+"/users/announce", app.Announce)
|
||||||
|
@ -52,6 +52,7 @@ type DiscordUser struct {
|
|||||||
|
|
||||||
type EmailAddress struct {
|
type EmailAddress struct {
|
||||||
Addr string
|
Addr string
|
||||||
|
Label string // User Label.
|
||||||
Contact bool
|
Contact bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -20,6 +20,7 @@ interface User {
|
|||||||
discord_id: string;
|
discord_id: string;
|
||||||
matrix: string;
|
matrix: string;
|
||||||
notify_matrix: boolean;
|
notify_matrix: boolean;
|
||||||
|
label: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface getPinResponse {
|
interface getPinResponse {
|
||||||
@ -60,6 +61,9 @@ class user implements User {
|
|||||||
private _lastActive: HTMLTableDataCellElement;
|
private _lastActive: HTMLTableDataCellElement;
|
||||||
private _lastActiveUnix: number;
|
private _lastActiveUnix: number;
|
||||||
private _notifyDropdown: HTMLDivElement;
|
private _notifyDropdown: HTMLDivElement;
|
||||||
|
private _label: HTMLInputElement;
|
||||||
|
private _userLabel: string;
|
||||||
|
private _labelEditButton: HTMLElement;
|
||||||
id = "";
|
id = "";
|
||||||
private _selected: boolean;
|
private _selected: boolean;
|
||||||
|
|
||||||
@ -380,6 +384,19 @@ class user implements User {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get label(): string { return this._userLabel; }
|
||||||
|
set label(l: string) {
|
||||||
|
console.log(l);
|
||||||
|
this._userLabel = l ? l : "";
|
||||||
|
this._label.innerHTML = l ? l : "";
|
||||||
|
this._labelEditButton.classList.add("ri-edit-line");
|
||||||
|
this._labelEditButton.classList.remove("ri-check-line");
|
||||||
|
if (!l) {
|
||||||
|
this._label.classList.remove("chip", "~gray");
|
||||||
|
} else {
|
||||||
|
this._label.classList.add("chip", "~gray", "mr-2");
|
||||||
|
}
|
||||||
|
}
|
||||||
private _checkEvent = new CustomEvent("accountCheckEvent");
|
private _checkEvent = new CustomEvent("accountCheckEvent");
|
||||||
private _uncheckEvent = new CustomEvent("accountUncheckEvent");
|
private _uncheckEvent = new CustomEvent("accountUncheckEvent");
|
||||||
|
|
||||||
@ -387,7 +404,7 @@ class user implements User {
|
|||||||
this._row = document.createElement("tr") as HTMLTableRowElement;
|
this._row = document.createElement("tr") as HTMLTableRowElement;
|
||||||
let innerHTML = `
|
let innerHTML = `
|
||||||
<td><input type="checkbox" value=""></td>
|
<td><input type="checkbox" value=""></td>
|
||||||
<td><div class="table-inline"><span class="accounts-username py-2"></span> <span class="accounts-admin"></span> <span class="accounts-disabled"></span></span></td>
|
<td><div class="table-inline"><span class="accounts-username py-2 mr-2"></span><span class="accounts-label-container ml-2"></span> <i class="icon ri-edit-line accounts-label-edit"></i> <span class="accounts-admin"></span> <span class="accounts-disabled"></span></span></div></td>
|
||||||
<td><div class="table-inline"><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-2"></span></div></td>
|
<td><div class="table-inline"><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-2"></span></div></td>
|
||||||
`;
|
`;
|
||||||
if (window.telegramEnabled) {
|
if (window.telegramEnabled) {
|
||||||
@ -411,6 +428,7 @@ class user implements User {
|
|||||||
`;
|
`;
|
||||||
this._row.innerHTML = innerHTML;
|
this._row.innerHTML = innerHTML;
|
||||||
const emailEditor = `<input type="email" class="input ~neutral @low stealth-input">`;
|
const emailEditor = `<input type="email" class="input ~neutral @low stealth-input">`;
|
||||||
|
const labelEditor = `<input type="text" class="field ~neutral @low stealth-input">`;
|
||||||
this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement;
|
this._check = this._row.querySelector("input[type=checkbox]") as HTMLInputElement;
|
||||||
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
|
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
|
||||||
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
|
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
|
||||||
@ -422,11 +440,13 @@ class user implements User {
|
|||||||
this._matrix = this._row.querySelector(".accounts-matrix") as HTMLTableDataCellElement;
|
this._matrix = this._row.querySelector(".accounts-matrix") as HTMLTableDataCellElement;
|
||||||
this._expiry = this._row.querySelector(".accounts-expiry") as HTMLTableDataCellElement;
|
this._expiry = this._row.querySelector(".accounts-expiry") as HTMLTableDataCellElement;
|
||||||
this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement;
|
this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement;
|
||||||
|
this._label = this._row.querySelector(".accounts-label-container") as HTMLInputElement;
|
||||||
|
this._labelEditButton = this._row.querySelector(".accounts-label-edit") as HTMLElement;
|
||||||
this._check.onchange = () => { this.selected = this._check.checked; }
|
this._check.onchange = () => { this.selected = this._check.checked; }
|
||||||
|
|
||||||
this._notifyDropdown = this._constructDropdown();
|
this._notifyDropdown = this._constructDropdown();
|
||||||
|
|
||||||
const toggleStealthInput = () => {
|
const toggleEmailInput = () => {
|
||||||
if (this._emailEditButton.classList.contains("ri-edit-line")) {
|
if (this._emailEditButton.classList.contains("ri-edit-line")) {
|
||||||
this._email.innerHTML = emailEditor;
|
this._email.innerHTML = emailEditor;
|
||||||
this._email.querySelector("input").value = this._emailAddress;
|
this._email.querySelector("input").value = this._emailAddress;
|
||||||
@ -438,21 +458,52 @@ class user implements User {
|
|||||||
this._emailEditButton.classList.toggle("ri-check-line");
|
this._emailEditButton.classList.toggle("ri-check-line");
|
||||||
this._emailEditButton.classList.toggle("ri-edit-line");
|
this._emailEditButton.classList.toggle("ri-edit-line");
|
||||||
};
|
};
|
||||||
const outerClickListener = (event: Event) => {
|
const emailClickListener = (event: Event) => {
|
||||||
if (!(event.target instanceof HTMLElement && (this._email.contains(event.target) || this._emailEditButton.contains(event.target)))) {
|
if (!(event.target instanceof HTMLElement && (this._email.contains(event.target) || this._emailEditButton.contains(event.target)))) {
|
||||||
toggleStealthInput();
|
toggleEmailInput();
|
||||||
this.email = this.email;
|
this.email = this.email;
|
||||||
document.removeEventListener("click", outerClickListener);
|
document.removeEventListener("click", emailClickListener);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
this._emailEditButton.onclick = () => {
|
this._emailEditButton.onclick = () => {
|
||||||
if (this._emailEditButton.classList.contains("ri-edit-line")) {
|
if (this._emailEditButton.classList.contains("ri-edit-line")) {
|
||||||
document.addEventListener('click', outerClickListener);
|
document.addEventListener('click', emailClickListener);
|
||||||
} else {
|
} else {
|
||||||
this._updateEmail();
|
this._updateEmail();
|
||||||
document.removeEventListener('click', outerClickListener);
|
document.removeEventListener('click', emailClickListener);
|
||||||
}
|
}
|
||||||
toggleStealthInput();
|
toggleEmailInput();
|
||||||
|
};
|
||||||
|
|
||||||
|
const toggleLabelInput = () => {
|
||||||
|
if (this._labelEditButton.classList.contains("ri-edit-line")) {
|
||||||
|
this._label.innerHTML = labelEditor;
|
||||||
|
const input = this._label.querySelector("input");
|
||||||
|
input.value = this._userLabel;
|
||||||
|
input.placeholder = window.lang.strings("label");
|
||||||
|
this._label.classList.remove("ml-2");
|
||||||
|
this._labelEditButton.classList.add("ri-check-line");
|
||||||
|
this._labelEditButton.classList.remove("ri-edit-line");
|
||||||
|
} else {
|
||||||
|
this._updateLabel();
|
||||||
|
this._email.classList.add("ml-2");
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const labelClickListener = (event: Event) => {
|
||||||
|
if (!(event.target instanceof HTMLElement && (this._label.contains(event.target) || this._labelEditButton.contains(event.target)))) {
|
||||||
|
toggleLabelInput();
|
||||||
|
document.removeEventListener("click", labelClickListener);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
this._labelEditButton.onclick = () => {
|
||||||
|
if (this._labelEditButton.classList.contains("ri-edit-line")) {
|
||||||
|
document.addEventListener('click', labelClickListener);
|
||||||
|
} else {
|
||||||
|
document.removeEventListener('click', labelClickListener);
|
||||||
|
}
|
||||||
|
toggleLabelInput();
|
||||||
};
|
};
|
||||||
|
|
||||||
this.update(user);
|
this.update(user);
|
||||||
@ -463,6 +514,21 @@ class user implements User {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private _updateLabel = () => {
|
||||||
|
let oldLabel = this.label;
|
||||||
|
this.label = this._label.querySelector("input").value;
|
||||||
|
let send = {};
|
||||||
|
send[this.id] = this.label;
|
||||||
|
_post("/users/labels", send, (req: XMLHttpRequest) => {
|
||||||
|
if (req.readyState == 4) {
|
||||||
|
if (req.status != 204) {
|
||||||
|
this.label = oldLabel;
|
||||||
|
window.notifications.customError("labelChanged", window.lang.notif("errorUnknown"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
private _updateEmail = () => {
|
private _updateEmail = () => {
|
||||||
let oldEmail = this.email;
|
let oldEmail = this.email;
|
||||||
this.email = this._email.querySelector("input").value;
|
this.email = this._email.querySelector("input").value;
|
||||||
@ -544,6 +610,7 @@ class user implements User {
|
|||||||
this.notify_matrix = user.notify_matrix;
|
this.notify_matrix = user.notify_matrix;
|
||||||
this.notify_email = user.notify_email;
|
this.notify_email = user.notify_email;
|
||||||
this.discord_id = user.discord_id;
|
this.discord_id = user.discord_id;
|
||||||
|
this.label = user.label;
|
||||||
}
|
}
|
||||||
|
|
||||||
asElement = (): HTMLTableRowElement => { return this._row; }
|
asElement = (): HTMLTableRowElement => { return this._row; }
|
||||||
|
Loading…
Reference in New Issue
Block a user