diff --git a/html/accounts-table-header.txt b/html/accounts-table-header.txt new file mode 100644 index 0000000..1e02c69 --- /dev/null +++ b/html/accounts-table-header.txt @@ -0,0 +1,24 @@ + + + + {{ .strings.username }} + {{ if .jellyfinLogin }} + {{ .strings.accessJFA }} + {{ end }} + {{ .strings.emailAddress }} + {{ if .telegramEnabled }} + Telegram + {{ end }} + {{ if .matrixEnabled }} + Matrix + {{ end }} + {{ if .discordEnabled }} + Discord + {{ end }} + {{ if .referralsEnabled }} + {{ .strings.referrals }} + {{ end }} + {{ .strings.expiry }} + {{ .strings.lastActiveTime }} + + diff --git a/html/admin.html b/html/admin.html index a21c34b..df1879c 100644 --- a/html/admin.html +++ b/html/admin.html @@ -86,16 +86,7 @@ ×

{{ .strings.modifySettingsDescription }}

-
- - -
+
@@ -134,16 +125,7 @@
×

{{ .strings.enableReferralsDescription }}

-
- - -
+
@@ -804,30 +786,7 @@
- - - - - {{ if .jellyfinLogin }} - - {{ end }} - - {{ if .telegramEnabled }} - - {{ end }} - {{ if .matrixEnabled }} - - {{ end }} - {{ if .discordEnabled }} - - {{ end }} - {{ if .referralsEnabled }} - - {{ end }} - - - - + {{ template "accounts-table-header.txt" . }}
{{ .strings.username }}{{ .strings.accessJFA }}{{ .strings.emailAddress }}TelegramMatrixDiscord{{ .strings.referrals }}{{ .strings.expiry }}{{ .strings.lastActiveTime }}
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json index ada5b45..7f7800a 100644 --- a/lang/admin/en-us.json +++ b/lang/admin/en-us.json @@ -7,6 +7,8 @@ "invite": "Invite", "accounts": "Accounts", "activity": "Activity", + "activityFromJF": "Activity (from Jellyfin)", + "activityFromJfa": "Activity (from jfa-go)", "settings": "Settings", "inviteMonths": "Months", "inviteDays": "Days", diff --git a/ts/admin.ts b/ts/admin.ts index 9226417..ba0e6d9 100644 --- a/ts/admin.ts +++ b/ts/admin.ts @@ -159,6 +159,7 @@ window.notifications = new notificationBox(document.getElementById("notification // Determine if url references an invite or account let isInviteURL = window.invites.isInviteURL(); let isAccountURL = accounts.isAccountURL(); +let isActivityURL = activity.isActivityURL(); // load tabs const tabs: { id: string; url: string; reloader: () => void; unloader?: () => void }[] = [ @@ -183,6 +184,8 @@ const tabs: { id: string; url: string; reloader: () => void; unloader?: () => vo accounts.loadAccountURL(); // Don't keep loading the same item on every tab refresh isAccountURL = false; + // Since accounts and activity accept ?user=x, wipe the other one. + isActivityURL = false; } accounts.bindPageEvents(); }), @@ -192,7 +195,15 @@ const tabs: { id: string; url: string; reloader: () => void; unloader?: () => vo id: "activity", url: "activity", reloader: () => { - activity.reload(); + activity.reload(() => { + if (isActivityURL) { + activity.loadActivityURL(); + // Don't keep loading the same item on every tab refresh + isActivityURL = false; + // Since accounts and activity accept ?user=x, wipe the other one. + isAccountURL = false; + } + }); activity.bindPageEvents(); }, unloader: activity.unbindPageEvents, diff --git a/ts/modules/accounts.ts b/ts/modules/accounts.ts index baba53a..c09ee28 100644 --- a/ts/modules/accounts.ts +++ b/ts/modules/accounts.ts @@ -14,7 +14,7 @@ import { Marked } from "@ts-stack/markdown"; import { stripMarkdown } from "../modules/stripmd"; import { DiscordUser, newDiscordSearch } from "../modules/discord"; import { SearchConfiguration, QueryType, SearchableItem, SearchableItemDataAttribute } from "../modules/search"; -import { HiddenInputField } from "./ui"; +import { HiddenInputField, RadioBasedTabSelector } from "./ui"; import { PaginatedList } from "./list"; import { TableRow } from "./row"; @@ -211,6 +211,8 @@ class User extends TableRow implements UserDTO, SearchableItem { private _referralsEnabled: boolean; private _referralsEnabledCheck: HTMLElement; + notInList: boolean = false; + focus = () => this._row.scrollIntoView({ behavior: "smooth", block: "center" }); lastNotifyMethod = (): string => { @@ -244,7 +246,7 @@ class User extends TableRow implements UserDTO, SearchableItem { setSelected(state: boolean, dispatchEvent: boolean) { this._selected = state; this._check.checked = state; - if (dispatchEvent) + if (dispatchEvent && !(this.notInList)) state ? document.dispatchEvent(this._checkEvent()) : document.dispatchEvent(this._uncheckEvent()); } @@ -931,7 +933,7 @@ class User extends TableRow implements UserDTO, SearchableItem { }; remove = () => { - if (this.selected) { + if (this.selected && !(this.notInList)) { document.dispatchEvent(this._uncheckEvent()); } super.remove(); @@ -984,11 +986,13 @@ export class accountsList extends PaginatedList { private _enableExpiryNotify = document.getElementById("expiry-extend-enable") as HTMLInputElement; private _enableExpiryReason = document.getElementById("textarea-extend-enable") as HTMLTextAreaElement; private _modifySettings = document.getElementById("accounts-modify-user") as HTMLSpanElement; - private _modifySettingsProfile = document.getElementById("radio-use-profile") as HTMLInputElement; - private _modifySettingsUser = document.getElementById("radio-use-user") as HTMLInputElement; + /*private _modifySettingsProfile = document.getElementById("radio-use-profile") as HTMLInputElement; + private _modifySettingsUser = document.getElementById("radio-use-user") as HTMLInputElement;*/ + private _modifySettingsProfileSource: RadioBasedTabSelector; private _enableReferrals = document.getElementById("accounts-enable-referrals") as HTMLSpanElement; - private _enableReferralsProfile = document.getElementById("radio-referrals-use-profile") as HTMLInputElement; - private _enableReferralsInvite = document.getElementById("radio-referrals-use-invite") as HTMLInputElement; + /*private _enableReferralsProfile = document.getElementById("radio-referrals-use-profile") as HTMLInputElement; + private _enableReferralsInvite = document.getElementById("radio-referrals-use-invite") as HTMLInputElement;*/ + private _enableReferralsSource: RadioBasedTabSelector; private _sendPWR = document.getElementById("accounts-send-pwr") as HTMLSpanElement; private _profileSelect = document.getElementById("modify-user-profiles") as HTMLSelectElement; private _userSelect = document.getElementById("modify-user-users") as HTMLSelectElement; @@ -1052,8 +1056,7 @@ export class accountsList extends PaginatedList { this.unbindPageEvents(); console.debug("Loading details for ", username, jfId); this._details.load( - username, - jfId, + this.users.get(jfId), () => { this.unbindPageEvents(); this._inDetails = true; @@ -1164,9 +1167,6 @@ export class accountsList extends PaginatedList { this.initSearch(searchConfig); - // FIXME: Remove! - (window as any).accs = this; - this._selectAll.checked = false; this._selectAllState = SelectAllState.None; this._selectAll.onchange = () => this.cycleSelectAll(); @@ -1195,66 +1195,47 @@ export class accountsList extends PaginatedList { this._modifySettings.onclick = this.modifyUsers; this._modifySettings.classList.add("unfocused"); - const checkSource = () => { - const profileSpan = this._modifySettingsProfile.nextElementSibling as HTMLSpanElement; - const userSpan = this._modifySettingsUser.nextElementSibling as HTMLSpanElement; - if (this._modifySettingsProfile.checked) { - this._userSelect.parentElement.classList.add("unfocused"); - this._profileSelect.parentElement.classList.remove("unfocused"); - profileSpan.classList.add("@high"); - profileSpan.classList.remove("@low"); - userSpan.classList.remove("@high"); - userSpan.classList.add("@low"); - this._applyOmbi?.parentElement.classList.remove("unfocused"); - this._applyJellyseerr?.parentElement.classList.remove("unfocused"); - } else { - this._userSelect.parentElement.classList.remove("unfocused"); - this._profileSelect.parentElement.classList.add("unfocused"); - userSpan.classList.add("@high"); - userSpan.classList.remove("@low"); - profileSpan.classList.remove("@high"); - profileSpan.classList.add("@low"); - this._applyOmbi?.parentElement.classList.add("unfocused"); - this._applyJellyseerr?.parentElement.classList.add("unfocused"); - } - }; - this._modifySettingsProfile.onchange = checkSource; - this._modifySettingsUser.onchange = checkSource; + this._modifySettingsProfileSource = new RadioBasedTabSelector( + document.getElementById("modify-user-profile-source"), + "modify-user-profile-source", + { + name: window.lang.strings("profile"), + id: "profile", + content: this._profileSelect.parentElement, + onShow: () => { + this._applyOmbi?.parentElement.classList.remove("unfocused"); + this._applyJellyseerr?.parentElement.classList.remove("unfocused"); + }, + }, + { + name: window.lang.strings("user"), + id: "user", + content: this._userSelect.parentElement, + onShow: () => { + this._applyOmbi?.parentElement.classList.add("unfocused"); + this._applyJellyseerr?.parentElement.classList.add("unfocused"); + }, + }, + ) if (window.referralsEnabled) { - const profileSpan = this._enableReferralsProfile.nextElementSibling as HTMLSpanElement; - const inviteSpan = this._enableReferralsInvite.nextElementSibling as HTMLSpanElement; - const checkReferralSource = () => { - console.debug("States:", this._enableReferralsProfile.checked, this._enableReferralsInvite.checked); - if (this._enableReferralsProfile.checked) { - this._referralsInviteSelect.parentElement.classList.add("unfocused"); - this._referralsProfileSelect.parentElement.classList.remove("unfocused"); - profileSpan.classList.add("@high"); - profileSpan.classList.remove("@low"); - inviteSpan.classList.remove("@high"); - inviteSpan.classList.add("@low"); - } else { - this._referralsInviteSelect.parentElement.classList.remove("unfocused"); - this._referralsProfileSelect.parentElement.classList.add("unfocused"); - inviteSpan.classList.add("@high"); - inviteSpan.classList.remove("@low"); - profileSpan.classList.remove("@high"); - profileSpan.classList.add("@low"); - } - }; - profileSpan.onclick = () => { - this._enableReferralsProfile.checked = true; - this._enableReferralsInvite.checked = false; - checkReferralSource(); - }; - inviteSpan.onclick = () => { - this._enableReferralsInvite.checked = true; - this._enableReferralsProfile.checked = false; - checkReferralSource(); - }; + this._enableReferralsSource = new RadioBasedTabSelector( + document.getElementById("enable-referrals-user-source"), + "enable-referrals-user-source", + { + name: window.lang.strings("profile"), + id: "profile", + content: this._referralsProfileSelect.parentElement, + }, + { + name: window.lang.strings("invite"), + id: "invite", + content: this._referralsInviteSelect.parentElement, + }, + ); this._enableReferrals.onclick = () => { this.enableReferrals(); - profileSpan.onclick(null); + this._enableReferralsSource.selected = 0; }; } @@ -2125,8 +2106,7 @@ export class accountsList extends PaginatedList { const form = document.getElementById("form-modify-user") as HTMLFormElement; const button = form.querySelector("span.submit") as HTMLSpanElement; - this._modifySettingsProfile.checked = true; - this._modifySettingsUser.checked = false; + this._modifySettingsProfileSource.selected = 0; form.onsubmit = (event: Event) => { event.preventDefault(); toggleLoader(button); @@ -2141,10 +2121,10 @@ export class accountsList extends PaginatedList { if (window.jellyseerrEnabled) { send["jellyseerr"] = this._applyJellyseerr.checked; } - if (this._modifySettingsProfile.checked && !this._modifySettingsUser.checked) { + if (this._modifySettingsProfileSource.selected == "profile") { send["from"] = "profile"; send["profile"] = this._profileSelect.value; - } else if (this._modifySettingsUser.checked && !this._modifySettingsProfile.checked) { + } else if (this._modifySettingsProfileSource.selected == "user") { send["from"] = "user"; send["id"] = this._userSelect.value; } @@ -2216,12 +2196,11 @@ export class accountsList extends PaginatedList { } innerHTML += ``; } - this._enableReferralsInvite.checked = true; + this._enableReferralsSource.selected = "invite"; } else { - this._enableReferralsInvite.checked = false; + this._enableReferralsSource.selected = "profile"; innerHTML += ``; } - this._enableReferralsProfile.checked = !this._enableReferralsInvite.checked; this._referralsInviteSelect.innerHTML = innerHTML; // 2. Profiles @@ -2243,10 +2222,10 @@ export class accountsList extends PaginatedList { users: list, }; // console.log("profile:", this._enableReferralsProfile.checked, this._enableReferralsInvite.checked); - if (this._enableReferralsProfile.checked && !this._enableReferralsInvite.checked) { + if (this._enableReferralsSource.selected == "profile") { send["from"] = "profile"; send["profile"] = this._referralsProfileSelect.value; - } else if (this._enableReferralsInvite.checked && !this._enableReferralsProfile.checked) { + } else if (this._enableReferralsSource.selected == "invite") { send["from"] = "invite"; send["id"] = this._referralsInviteSelect.value; } @@ -2278,8 +2257,7 @@ export class accountsList extends PaginatedList { }, ); }; - this._enableReferralsProfile.checked = true; - this._enableReferralsInvite.checked = false; + this._enableReferralsSource.selected = 0; this._referralsExpiry.checked = false; window.modals.enableReferralsUser.show(); }; @@ -2856,7 +2834,9 @@ class UserInfo extends PaginatedList { private _hidden: boolean = true; private _table: HTMLElement; private _card: HTMLElement; + private _rowArea: HTMLElement; private _noResults: HTMLElement; + private _link: HTMLAnchorElement; get entries(): Map { return this._search.items as Map; } @@ -2867,20 +2847,14 @@ class UserInfo extends PaginatedList { card.classList.add("unfocused"); card.innerHTML = `
-
-
-
-
-
-
- - + + + +
+
+
+

${window.lang.strings("activityFromJF")}

+ ${window.lang.strings("activityFromJfa")}
@@ -2962,11 +2936,18 @@ class UserInfo extends PaginatedList { }); this._hidden = true; this._card = card; + this._rowArea = this._card.getElementsByClassName("user-info-row")[0] as HTMLElement; + const realHead = document.getElementById("accounts-table-header") as HTMLElement; + const cloneHead = realHead.cloneNode(true) as HTMLElement; + // Remove "select all" check from header + cloneHead.querySelector("input[type=checkbox]").remove(); + const head = this._card.getElementsByClassName("user-info-table-header")[0] as HTMLElement; + head.replaceWith(cloneHead); this._table = this._card.getElementsByClassName("jf-activity-table")[0] as HTMLElement; this._container = this._table.getElementsByClassName("jf-activity-table-content")[0] as HTMLElement; this._back = document.getElementById("user-details-back") as HTMLButtonElement; this._noResults = this._card.getElementsByClassName("jf-activity-no-activity")[0] as HTMLElement; - + this._link = this._card.getElementsByClassName("jf-activity-jfa-link")[0] as HTMLAnchorElement; let searchConfig: SearchConfiguration = { queries: {}, setVisibility: null, @@ -2995,9 +2976,16 @@ class UserInfo extends PaginatedList { this._reload(callback); }; - load = (username: string, jfId: string, onLoad?: () => void, onBack?: () => void) => { - this.username = username; - this.jfId = jfId; + load = (user: User, onLoad?: () => void, onBack?: () => void) => { + this.username = user.name; + this.jfId = user.id; + const clone = new User(user); + clone.notInList = true; + clone.setSelected(true, false) + this._rowArea.replaceChildren(clone.asElement()); + this._link.setAttribute("data-id", this.jfId) + this._link.href = `${window.pages.Base}${window.pages.Admin}/activity?user=${this.username}` + if (onBack) { this._back.classList.remove("unfocused"); this._back.onclick = () => onBack(); diff --git a/ts/modules/activity.ts b/ts/modules/activity.ts index c0d1fbc..d7d6b79 100644 --- a/ts/modules/activity.ts +++ b/ts/modules/activity.ts @@ -687,4 +687,18 @@ export class activityList extends PaginatedList { this._keepSearchingDescription.classList.add("unfocused"); } };*/ + + isActivityURL = () => { + const urlParams = new URLSearchParams(window.location.search); + const username = urlParams.get("user"); + return Boolean(username); + }; + + loadActivityURL = () => { + const urlParams = new URLSearchParams(window.location.search); + const username = urlParams.get("user"); + this._c.searchBox.value = `user:"${username}"`; + this._search.onSearchBoxChange(); + this._search.onServerSearch(); + }; }