1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2024-11-09 20:00:12 +00:00

Compare commits

..

3 Commits

Author SHA1 Message Date
729552a827
referrals: Show enabled status on account list 2023-09-06 22:46:16 +01:00
cdc8f9af4b
referrals: unlink/disable referrals for profile 2023-09-06 22:12:36 +01:00
9e5034ebab
referrals: enable referral for users & profiles
Enabling for individual users works, as does adding a template to a
profile. Removing/Disabling for both needs to be completed.
2023-09-06 22:00:44 +01:00
13 changed files with 295 additions and 37 deletions

View File

@ -1,9 +1,11 @@
package main
import (
"strconv"
"time"
"github.com/gin-gonic/gin"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
@ -19,13 +21,23 @@ func (app *appContext) GetProfiles(gc *gin.Context) {
DefaultProfile: app.storage.GetDefaultProfile().Name,
Profiles: map[string]profileDTO{},
}
referralsEnabled := app.config.Section("user_page").Key("referrals").MustBool(false)
baseInv := Invite{}
for _, p := range app.storage.GetProfiles() {
out.Profiles[p.Name] = profileDTO{
pdto := profileDTO{
Admin: p.Admin,
LibraryAccess: p.LibraryAccess,
FromUser: p.FromUser,
Ombi: p.Ombi != nil,
ReferralsEnabled: false,
}
if referralsEnabled {
err := app.storage.db.Get(p.ReferralTemplateKey, &baseInv)
if p.ReferralTemplateKey != "" && err == nil {
pdto.ReferralsEnabled = true
}
}
out.Profiles[p.Name] = pdto
}
gc.JSON(200, out)
}
@ -111,3 +123,76 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
app.storage.DeleteProfileKey(name)
respondBool(200, true, gc)
}
// @Summary Enable referrals for a profile, sourced from the given invite by its code.
// @Produce json
// @Param profile path string true "name of profile to enable referrals for."
// @Param invite path string true "invite code to create referral template from."
// @Success 200 {object} boolResponse
// @Failure 400 {object} stringResponse
// @Failure 500 {object} stringResponse
// @Router /profiles/referral/{profile}/{invite} [post]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) EnableReferralForProfile(gc *gin.Context) {
profileName := gc.Param("profile")
invCode := gc.Param("invite")
inv, ok := app.storage.GetInvitesKey(invCode)
if !ok {
respond(400, "Invalid invite code", gc)
app.err.Printf("\"%s\": Failed to enable referrals: invite not found", profileName)
return
}
profile, ok := app.storage.GetProfileKey(profileName)
if !ok {
respond(400, "Invalid profile", gc)
app.err.Printf("\"%s\": Failed to enable referrals: profile not found", profileName)
return
}
// Generate new code for referral template
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
_, err := strconv.Atoi(string(inv.Code[0]))
for err == nil {
inv.Code = shortuuid.New()
_, err = strconv.Atoi(string(inv.Code[0]))
}
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
inv.IsReferral = true
// Since this is a template for multiple users, ReferrerJellyfinID is not set.
// inv.ReferrerJellyfinID = ...
app.storage.SetInvitesKey(inv.Code, inv)
profile.ReferralTemplateKey = inv.Code
app.storage.SetProfileKey(profile.Name, profile)
respondBool(200, true, gc)
}
// @Summary Disable referrals for a profile, and removes the referral template. no-op if not enabled.
// @Produce json
// @Param profile path string true "name of profile to enable referrals for."
// @Success 200 {object} boolResponse
// @Router /profiles/referral/{profile} [delete]
// @Security Bearer
// @tags Profiles & Settings
func (app *appContext) DisableReferralForProfile(gc *gin.Context) {
profileName := gc.Param("profile")
profile, ok := app.storage.GetProfileKey(profileName)
if !ok {
respondBool(200, true, gc)
return
}
app.storage.DeleteInvitesKey(profile.ReferralTemplateKey)
profile.ReferralTemplateKey = ""
app.storage.SetProfileKey(profileName, profile)
respondBool(200, true, gc)
}

View File

@ -643,10 +643,10 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
// If one exists, that means its just for us and so we
// can use it directly.
inv := Invite{}
err := app.storage.db.Find(&inv, badgerhold.Where("ReferrerJellyfinID").Eq(gc.GetString("jfId")))
err := app.storage.db.FindOne(&inv, badgerhold.Where("ReferrerJellyfinID").Eq(gc.GetString("jfId")))
if err != nil {
// 2. Look for a template matching the key found in the user storage
// Since this key is shared between a profile, we make a copy.
// Since this key is shared between users in a profile, we make a copy.
user, ok := app.storage.GetEmailsKey(gc.GetString("jfId"))
err = app.storage.db.Get(user.ReferralTemplateKey, &inv)
if !ok || err != nil {
@ -664,6 +664,7 @@ func (app *appContext) GetMyReferral(gc *gin.Context) {
inv.Created = time.Now()
inv.ValidTill = inv.Created.Add(REFERRAL_EXPIRY_DAYS * 24 * time.Hour)
inv.IsReferral = true
inv.ReferrerJellyfinID = gc.GetString("jfId")
app.storage.SetInvitesKey(inv.Code, inv)
} else if time.Now().After(inv.ValidTill) {
// 3. We found an invite for us, but it's expired.

View File

@ -11,6 +11,7 @@ import (
"github.com/golang-jwt/jwt"
"github.com/hrfee/mediabrowser"
"github.com/lithammer/shortuuid/v3"
"github.com/timshannon/badgerhold/v4"
)
// @Summary Creates a new Jellyfin user without an invite.
@ -657,6 +658,7 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
return
}
app.debug.Printf("Found referral template in profile: %+v\n", profile.ReferralTemplateKey)
} else if mode == "invite" {
// Get the invite, and modify it to turn it into a referral
err := app.storage.db.Get(source, &baseInv)
@ -667,6 +669,10 @@ func (app *appContext) EnableReferralForUsers(gc *gin.Context) {
}
}
for _, u := range req.Users {
// 1. Wipe out any existing referral codes.
app.storage.db.DeleteMatching(Invite{}, badgerhold.Where("ReferrerJellyfinID").Eq(u))
// 2. Generate referral invite.
inv := baseInv
inv.Code = shortuuid.New()
// make sure code doesn't begin with number
@ -887,6 +893,7 @@ func (app *appContext) GetUsers(gc *gin.Context) {
}
adminOnly := app.config.Section("ui").Key("admin_only").MustBool(true)
allowAll := app.config.Section("ui").Key("allow_all").MustBool(false)
referralsEnabled := app.config.Section("user_page").Key("referrals").MustBool(false)
i := 0
for _, jfUser := range users {
user := respUser{
@ -894,6 +901,7 @@ func (app *appContext) GetUsers(gc *gin.Context) {
Name: jfUser.Name,
Admin: jfUser.Policy.IsAdministrator,
Disabled: jfUser.Policy.IsDisabled,
ReferralsEnabled: false,
}
if !jfUser.LastActivityDate.IsZero() {
user.LastActive = jfUser.LastActivityDate.Unix()
@ -922,6 +930,18 @@ func (app *appContext) GetUsers(gc *gin.Context) {
user.DiscordID = dcUser.ID
user.NotifyThroughDiscord = dcUser.Contact
}
// FIXME: Send referral data
referrerInv := Invite{}
if referralsEnabled {
// 1. Directly attached invite.
err := app.storage.db.FindOne(&referrerInv, badgerhold.Where("ReferrerJellyfinID").Eq(jfUser.ID))
if err == nil {
user.ReferralsEnabled = true
// 2. Referrals via profile template. Shallow check, doesn't look for the thing in the database.
} else if email, ok := app.storage.GetEmailsKey(jfUser.ID); ok && email.ReferralTemplateKey != "" {
user.ReferralsEnabled = true
}
}
resp.UserList[i] = user
i++
}

View File

@ -385,7 +385,7 @@
"enabled": {
"name": "Enabled",
"required": false,
"requires_restart": false,
"requires_restart": true,
"type": "bool",
"value": true
},
@ -409,7 +409,7 @@
"referrals": {
"name": "User Referrals",
"required": false,
"requires_restart": false,
"requires_restart": true,
"type": "bool",
"value": true,
"description": "Users are given their own \"invite\" to send to others."

View File

@ -135,6 +135,20 @@
</label>
</form>
</div>
<div id="modal-enable-referrals-profile" class="modal">
<form class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3" id="form-enable-referrals-profile" href="">
<span class="heading"><span id="header-enable-referrals-profile">{{ .strings.enableReferrals }}</span> <span class="modal-close">&times;</span></span>
<p class="content my-4">{{ .strings.enableReferralsProfileDescription }}</p>
<label class="supra" for="enable-referrals-profile-invites">{{ .strings.invite }}</label>
<div class="select ~neutral @low mb-4 mt-2">
<select id="enable-referrals-profile-invites"></select>
</div>
<label>
<input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.apply }}</span>
</label>
</form>
</div>
{{ end }}
<div id="modal-delete-user" class="modal">
<form class="card relative mx-auto my-[10%] w-4/5 lg:w-1/3" id="form-delete-user" href="">
@ -332,6 +346,9 @@
{{ if .ombiEnabled }}
<th>Ombi</th>
{{ end }}
{{ if .referralsEnabled }}
<th>{{ .strings.referrals }}</th>
{{ end }}
<th>{{ .strings.from }}</th>
<th>{{ .strings.userProfilesLibraries }}</th>
<th><span class="button ~neutral @high" id="button-profile-create">{{ .strings.create }}</span></th>
@ -642,7 +659,9 @@
</div>
</div>
<span class="col button ~urge @low center max-w-[20%]" id="accounts-modify-user">{{ .strings.modifySettings }}</span>
{{ if .referralsEnabled }}
<span class="col button ~urge @low center max-w-[20%]" id="accounts-enable-referrals">{{ .strings.enableReferrals }}</span>
{{ end }}
<span class="col button ~warning @low center max-w-[20%]" id="accounts-extend-expiry">{{ .strings.extendExpiry }}</span>
<div id="accounts-disable-enable-dropdown" class="col dropdown manual pb-0i max-w-[20%]" tabindex="0">
<span class="w-100 button ~positive @low center" id="accounts-disable-enable">{{ .strings.disable }}</span>
@ -674,6 +693,9 @@
{{ if .discordEnabled }}
<th class="text-center-i grid gap-4 place-items-stretch accounts-header-discord">Discord</th>
{{ end }}
{{ if .referralsEnabled }}
<th class="text-center-i grid gap-4 place-items-stretch accounts-header-referrals">{{ .strings.referrals }}</th>
{{ end }}
<th class="grid gap-4 place-items-stretch accounts-header-expiry">{{ .strings.expiry }}</th>
<th class="grid gap-4 place-items-stretch accounts-header-last-active">{{ .strings.lastActiveTime }}</th>
</tr>

View File

@ -65,7 +65,8 @@
"modifySettings": "Modify Settings",
"modifySettingsDescription": "Apply settings from an existing profile, or source them directly from a user.",
"enableReferrals": "Enable Referrals",
"enableReferralsDescription": "Give users their a referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an exsiting invite.",
"enableReferralsDescription": "Give users a personal referral link similiar to an invite, to send to friends/family. Can be sourced from a referral template in a profile, or from an existing invite.",
"enableReferralsProfileDescription": "Give users created with this profile a personal referral link similiar to an invite, to send to friends/family. Create an invite with the desired settings, then select it here. Each referral will then be based on this invite. You can delete the invite once complete.",
"applyHomescreenLayout": "Apply homescreen layout",
"sendDeleteNotificationEmail": "Send notification message",
"sendDeleteNotifiationExample": "Your account has been deleted.",
@ -135,6 +136,7 @@
"updateAppliedRefresh": "Update applied, please refresh.",
"telegramVerified": "Telegram account verified.",
"accountConnected": "Account connected.",
"referralsEnabled": "Referrals enabled.",
"errorSettingsAppliedNoHomescreenLayout": "Settings were applied, but applying homescreen layout may have failed.",
"errorHomescreenAppliedNoSettings": "Homescreen layout was applied, but applying settings may have failed.",
"errorSettingsFailed": "Application failed.",

View File

@ -39,7 +39,8 @@
"add": "Add",
"edit": "Edit",
"delete": "Delete",
"myAccount": "My Account"
"myAccount": "My Account",
"referrals": "Referrals"
},
"notifications": {
"errorLoginBlank": "The username and/or password were left blank.",

View File

@ -75,6 +75,7 @@ type profileDTO struct {
LibraryAccess string `json:"libraries" example:"all"` // Number of libraries profile has access to
FromUser string `json:"fromUser" example:"jeff"` // The user the profile is based on
Ombi bool `json:"ombi"` // Whether or not Ombi settings are stored in this profile.
ReferralsEnabled bool `json:"referrals_enabled" example:"true"` // Whether or not the profile has referrals enabled, and has a template invite stored.
}
type getProfilesDTO struct {
@ -150,6 +151,7 @@ type respUser struct {
NotifyThroughMatrix bool `json:"notify_matrix"`
Label string `json:"label"` // Label of user, shown next to their name.
AccountsAdmin bool `json:"accounts_admin"` // Whether or not the user is a jfa-go admin.
ReferralsEnabled bool `json:"referrals_enabled"`
}
type getUsersDTO struct {
@ -424,5 +426,4 @@ type GetMyReferralRespDTO struct {
type EnableDisableReferralDTO struct {
Users []string `json:"users"`
Enabled bool `json:"enabled"`
}

View File

@ -228,6 +228,8 @@ func (app *appContext) loadRoutes(router *gin.Engine) {
api.POST(p+"/matrix/login", app.MatrixLogin)
if app.config.Section("user_page").Key("referrals").MustBool(false) {
api.POST(p+"/users/referral/:mode/:source", app.EnableReferralForUsers)
api.POST(p+"/profiles/referral/:profile/:invite", app.EnableReferralForProfile)
api.DELETE(p+"/profiles/referral/:profile", app.DisableReferralForProfile)
}
if userPageEnabled {

View File

@ -81,6 +81,7 @@ window.availableProfiles = window.availableProfiles || [];
if (window.referralsEnabled) {
window.modals.enableReferralsUser = new Modal(document.getElementById("modal-enable-referrals-user"));
window.modals.enableReferralsProfile = new Modal(document.getElementById("modal-enable-referrals-profile"));
}
})();

View File

@ -23,6 +23,7 @@ interface User {
notify_matrix: boolean;
label: string;
accounts_admin: boolean;
referrals_enabled: boolean;
}
interface getPinResponse {
@ -69,6 +70,8 @@ class user implements User {
private _labelEditButton: HTMLElement;
private _accounts_admin: HTMLInputElement
private _selected: boolean;
private _referralsEnabled: boolean;
private _referralsEnabledCheck: HTMLElement;
lastNotifyMethod = (): string => {
// Telegram, Matrix, Discord
@ -162,6 +165,17 @@ class user implements User {
}
}
get referrals_enabled(): boolean { return this._referralsEnabled; }
set referrals_enabled(v: boolean) {
this._referralsEnabled = v;
if (!window.referralsEnabled) return;
if (!v) {
this._referralsEnabledCheck.textContent = ``;
} else {
this._referralsEnabledCheck.innerHTML = `<i class="ri-check-line" aria-label="${window.lang.strings("enabled")}"></i>`;
}
}
private _constructDropdown = (): HTMLDivElement => {
const el = document.createElement("div") as HTMLDivElement;
const telegram = this._telegramUsername != "";
@ -506,6 +520,11 @@ class user implements User {
<td class="accounts-discord"></td>
`;
}
if (window.referralsEnabled) {
innerHTML += `
<td class="accounts-referrals text-center-i grid gap-4 place-items-stretch"></td>
`;
}
innerHTML += `
<td class="accounts-expiry"></td>
<td class="accounts-last-active whitespace-nowrap"></td>
@ -545,6 +564,10 @@ class user implements User {
};
}
if (window.referralsEnabled) {
this._referralsEnabledCheck = this._row.querySelector(".accounts-referrals");
}
this._notifyDropdown = this._constructDropdown();
const toggleEmailInput = () => {
@ -716,6 +739,7 @@ class user implements User {
this.discord_id = user.discord_id;
this.label = user.label;
this.accounts_admin = user.accounts_admin;
this.referrals_enabled = user.referrals_enabled;
}
asElement = (): HTMLTableRowElement => { return this._row; }
@ -1061,7 +1085,7 @@ export class accountsList {
let attempt: { year?: number, month?: number, day?: number, hour?: number, minute?: number } = dateParser.attempt(split[1]);
// Month in Date objects is 0-based, so make our parsed date that way too
if ("month" in attempt) attempt["month"] -= 1;
if ("month" in attempt) attempt.month -= 1;
let date: Date = (Date as any).fromString(split[1]) as Date;
console.log("Read", attempt, "and", date);
@ -1127,7 +1151,7 @@ export class accountsList {
}
}
}
return result
return result;
};
@ -1693,11 +1717,12 @@ export class accountsList {
}
innerHTML += `<option value="${inv.code}">${name}</option>`;
}
this._enableReferralsInvite.checked = true;
} else {
this._enableReferralsInvite.checked = false;
this._enableReferralsProfile.checked = true;
innerHTML += `<option>${window.lang.strings("inviteNoInvites")}</option>`;
}
this._enableReferralsProfile.checked = !(this._enableReferralsInvite.checked);
this._referralsInviteSelect.innerHTML = innerHTML;
// 2. Profiles
@ -1710,20 +1735,15 @@ export class accountsList {
});
})();
// FIXME: Collect Profiles, Invite
(() => {
})();
const form = document.getElementById("form-enable-referrals-user") as HTMLFormElement;
const button = form.querySelector("span.submit") as HTMLSpanElement;
this._enableReferralsProfile.checked = true;
this._enableReferralsInvite.checked = false;
form.onsubmit = (event: Event) => {
event.preventDefault();
toggleLoader(button);
let send = {
"users": list
};
// console.log("profile:", this._enableReferralsProfile.checked, this._enableReferralsInvite.checked);
if (this._enableReferralsProfile.checked && !this._enableReferralsInvite.checked) {
send["from"] = "profile";
send["profile"] = this._referralsProfileSelect.value;
@ -1731,7 +1751,7 @@ export class accountsList {
send["from"] = "invite";
send["id"] = this._referralsInviteSelect.value;
}
_post("/users/referrals/" + send["from"] + "/" + send["id"], send, (req: XMLHttpRequest) => {
_post("/users/referral/" + send["from"] + "/" + (send["id"] ? send["id"] : send["profile"]), send, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
toggleLoader(button);
if (req.status == 400) {
@ -1744,6 +1764,8 @@ export class accountsList {
}
});
};
this._enableReferralsProfile.checked = true;
this._enableReferralsInvite.checked = false;
window.modals.enableReferralsUser.show();
}
@ -1879,10 +1901,10 @@ export class accountsList {
this._modifySettingsUser.onchange = checkSource;
if (window.referralsEnabled) {
this._enableReferrals.onclick = this.enableReferrals;
const profileSpan = this._enableReferralsProfile.nextElementSibling as HTMLSpanElement;
const inviteSpan = this._enableReferralsInvite.nextElementSibling as HTMLSpanElement;
const checkReferralSource = () => {
console.log("States:", this._enableReferralsProfile.checked, this._enableReferralsInvite.checked);
if (this._enableReferralsProfile.checked) {
this._referralsInviteSelect.parentElement.classList.add("unfocused");
this._referralsProfileSelect.parentElement.classList.remove("unfocused")
@ -1899,9 +1921,20 @@ export class accountsList {
profileSpan.classList.add("@low");
}
};
this._enableReferralsProfile.onchange = checkReferralSource;
this._enableReferralsInvite.onchange = checkReferralSource;
profileSpan.onclick = () => {
this._enableReferralsProfile.checked = true;
this._enableReferralsInvite.checked = false;
checkReferralSource();
};
inviteSpan.onclick = () => {;
this._enableReferralsInvite.checked = true;
this._enableReferralsProfile.checked = false;
checkReferralSource();
};
this._enableReferrals.onclick = () => {
this.enableReferrals();
profileSpan.onclick(null);
};
}
this._deleteUser.onclick = this.deleteUsers;

View File

@ -5,6 +5,7 @@ interface Profile {
libraries: string;
fromUser: string;
ombi: boolean;
referrals_enabled: boolean;
}
class profile implements Profile {
@ -16,6 +17,8 @@ class profile implements Profile {
private _fromUser: HTMLTableDataCellElement;
private _defaultRadio: HTMLInputElement;
private _ombi: boolean;
private _referralsButton: HTMLSpanElement;
private _referralsEnabled: boolean;
get name(): string { return this._name.textContent; }
set name(v: string) { this._name.textContent = v; }
@ -52,6 +55,21 @@ class profile implements Profile {
get fromUser(): string { return this._fromUser.textContent; }
set fromUser(v: string) { this._fromUser.textContent = v; }
get referrals_enabled(): boolean { return this._referralsEnabled; }
set referrals_enabled(v: boolean) {
if (!window.referralsEnabled) return;
this._referralsEnabled = v;
if (v) {
this._referralsButton.textContent = window.lang.strings("delete");
this._referralsButton.classList.add("~critical");
this._referralsButton.classList.remove("~neutral");
} else {
this._referralsButton.textContent = window.lang.strings("add");
this._referralsButton.classList.add("~neutral");
this._referralsButton.classList.remove("~critical");
}
}
get default(): boolean { return this._defaultRadio.checked; }
set default(v: boolean) { this._defaultRadio.checked = v; }
@ -64,6 +82,9 @@ class profile implements Profile {
if (window.ombiEnabled) innerHTML += `
<td><span class="button @low profile-ombi"></span></td>
`;
if (window.referralsEnabled) innerHTML += `
<td><span class="button @low profile-referrals"></span></td>
`;
innerHTML += `
<td class="profile-from ellipsis"></td>
<td class="profile-libraries"></td>
@ -75,6 +96,8 @@ class profile implements Profile {
this._libraries = this._row.querySelector("td.profile-libraries") as HTMLTableDataCellElement;
if (window.ombiEnabled)
this._ombiButton = this._row.querySelector("span.profile-ombi") as HTMLSpanElement;
if (window.referralsEnabled)
this._referralsButton = this._row.querySelector("span.profile-referrals") as HTMLSpanElement;
this._fromUser = this._row.querySelector("td.profile-from") as HTMLTableDataCellElement;
this._defaultRadio = this._row.querySelector("input[type=radio]") as HTMLInputElement;
this._defaultRadio.onclick = () => document.dispatchEvent(new CustomEvent("profiles-default", { detail: this.name }));
@ -89,9 +112,11 @@ class profile implements Profile {
this.fromUser = p.fromUser;
this.libraries = p.libraries;
this.ombi = p.ombi;
this.referrals_enabled = p.referrals_enabled;
}
setOmbiFunc = (ombiFunc: (ombi: boolean) => void) => { this._ombiButton.onclick = () => ombiFunc(this._ombi); }
setReferralFunc = (referralFunc: (enabled: boolean) => void) => { this._referralsButton.onclick = () => referralFunc(this._referralsEnabled); }
remove = () => { document.dispatchEvent(new CustomEvent("profiles-delete", { detail: this._name })); this._row.remove(); }
@ -173,6 +198,14 @@ export class ProfileEditor {
this._ombiProfiles.load(name);
}
});
if (window.referralsEnabled)
this._profiles[name].setReferralFunc((enabled: boolean) => {
if (enabled) {
this.disableReferrals(name);
} else {
this.enableReferrals(name);
}
});
this._table.appendChild(this._profiles[name].asElement());
}
}
@ -185,6 +218,62 @@ export class ProfileEditor {
}
})
disableReferrals = (name: string) => _delete("/profiles/referral/" + name, null, (req: XMLHttpRequest) => {
if (req.readyState != 4) return;
this.load();
});
enableReferrals = (name: string) => {
const referralsInviteSelect = document.getElementById("enable-referrals-profile-invites") as HTMLSelectElement;
_get("/invites", null, (req: XMLHttpRequest) => {
if (req.readyState != 4 || req.status != 200) return;
let innerHTML = "";
let invites = req.response["invites"] as Array<Invite>;
window.availableProfiles = req.response["profiles"];
if (invites) {
for (let inv of invites) {
let name = inv.code;
if (inv.label) {
name = `${inv.label} (${inv.code})`;
}
innerHTML += `<option value="${inv.code}">${name}</option>`;
}
} else {
innerHTML += `<option>${window.lang.strings("inviteNoInvites")}</option>`;
}
referralsInviteSelect.innerHTML = innerHTML;
});
const form = document.getElementById("form-enable-referrals-profile") as HTMLFormElement;
const button = form.querySelector("span.submit") as HTMLSpanElement;
form.onsubmit = (event: Event) => {
event.preventDefault();
toggleLoader(button);
let send = {
"profile": name,
"invite": referralsInviteSelect.value
};
_post("/profiles/referral/" + send["profile"] + "/" + send["invite"], send, (req: XMLHttpRequest) => {
if (req.readyState == 4) {
toggleLoader(button);
if (req.status == 400) {
window.notifications.customError("unknownError", window.lang.notif("errorUnknown"));
} else if (req.status == 200 || req.status == 204) {
window.notifications.customSuccess("enableReferralsSuccess", window.lang.notif("referralsEnabled"));
}
window.modals.enableReferralsProfile.close();
this.load();
}
});
};
window.modals.profiles.close();
window.modals.enableReferralsProfile.show();
};
constructor() {
(document.getElementById('setting-profiles') as HTMLSpanElement).onclick = this.load;
document.addEventListener("profiles-default", (event: CustomEvent) => {

View File

@ -115,6 +115,7 @@ declare interface Modals {
logs: Modal;
email?: Modal;
enableReferralsUser?: Modal;
enableReferralsProfile?: Modal;
}
interface Invite {