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

Compare commits

...

2 Commits

Author SHA1 Message Date
b08527bce2
userpage: cleanup referral code
moved to its own class, like the expiry card.
2023-09-07 20:42:40 +01:00
41c092f578
referrals: show referrer username on form 2023-09-07 20:19:25 +01:00
4 changed files with 126 additions and 53 deletions

View File

@ -43,7 +43,7 @@
<div id="notification-box"></div> <div id="notification-box"></div>
<div class="page-container"> <div class="page-container">
<div class="card dark:~d_neutral @low"> <div class="card dark:~d_neutral @low">
<div class="flex flex-col md:flex-row gap-3 inline align-baseline"> <div class="flex flex-col justify-between md:flex-row gap-3 items-baseline mb-2">
<span class="heading mr-5"> <span class="heading mr-5">
{{ if .passwordReset }} {{ if .passwordReset }}
{{ .strings.passwordReset }} {{ .strings.passwordReset }}
@ -53,11 +53,14 @@
</span> </span>
<span class="subheading"> <span class="subheading">
{{ if .passwordReset }} {{ if .passwordReset }}
{{ .strings.enterYourPassword }} {{ .strings.enterYourPassword }}
{{ else }} {{ else }}
{{ .helpMessage }} {{ .helpMessage }}
{{ end }} {{ end }}
</span> </span>
{{ if .fromUser }}
<span class="badge ~positive text-lg p-1 px-2 self-center">{{ .strings.invitedBy }} {{ .fromUser }}</span>
{{ end }}
</div> </div>
<div class="flex flex-col md:flex-row gap-3"> <div class="flex flex-col md:flex-row gap-3">
<div class="flex-1"> <div class="flex-1">

View File

@ -36,7 +36,8 @@
"resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.", "resetSentDescription": "If an account with the given username/contact method exists, a password reset link has been sent via all contact methods available. The code will expire in 30 minutes.",
"changePassword": "Change Password", "changePassword": "Change Password",
"referralsDescription": "Invite friends & family to Jellyfin with this link. Come back here for a new one if it expires.", "referralsDescription": "Invite friends & family to Jellyfin with this link. Come back here for a new one if it expires.",
"copyReferral": "Copy Link" "copyReferral": "Copy Link",
"invitedBy": "Invited By"
}, },
"notifications": { "notifications": {
"errorUserExists": "User already exists.", "errorUserExists": "User already exists.",
@ -78,4 +79,4 @@
"plural": "Must have at least {n} special characters" "plural": "Must have at least {n} special characters"
} }
} }
} }

View File

@ -113,7 +113,7 @@ interface MyDetails {
interface MyReferral { interface MyReferral {
code: string; code: string;
remaining_uses: string; remaining_uses: number;
no_limit: boolean; no_limit: boolean;
expiry: number; expiry: number;
} }
@ -246,6 +246,107 @@ class ContactMethods {
}; };
} }
class ReferralCard {
private _card: HTMLElement;
private _code: string;
private _url: string;
private _expiry: Date;
private _expiryUnix: number;
private _remainingUses: number;
private _noLimit: boolean;
private _button: HTMLButtonElement;
private _infoArea: HTMLDivElement;
private _remainingUsesEl: HTMLSpanElement;
private _expiryEl: HTMLSpanElement;
get code(): string { return this._code; }
set code(c: string) {
this._code = c;
let url = window.location.href;
for (let split of ["#", "?", "account", "my"]) {
url = url.split(split)[0];
}
if (url.slice(-1) != "/") { url += "/"; }
url = url + "invite/" + this._code;
this._url = url;
}
get remaining_uses(): number { return this._remainingUses; }
set remaining_uses(v: number) {
this._remainingUses = v;
if (v > 0 && !(this._noLimit))
this._remainingUsesEl.textContent = `${v}`;
}
get no_limit(): boolean { return this._noLimit; }
set no_limit(v: boolean) {
this._noLimit = v;
if (v)
this._remainingUsesEl.textContent = ``;
else
this._remainingUsesEl.textContent = `${this._remainingUses}`;
}
get expiry(): Date { return this._expiry; };
set expiry(expiryUnix: number) {
this._expiryUnix = expiryUnix;
this._expiry = new Date(expiryUnix * 1000);
this._expiryEl.textContent = toDateString(this._expiry);
}
constructor(card: HTMLElement) {
this._card = card;
this._button = this._card.querySelector(".user-referrals-button") as HTMLButtonElement;
this._infoArea = this._card.querySelector(".user-referrals-info") as HTMLDivElement;
this._infoArea.innerHTML = `
<div class="row my-3">
<div class="inline baseline">
<span class="text-2xl referral-remaining-uses"></span> <span class="text-gray-400 text-lg">${window.lang.strings("inviteRemainingUses")}</span>
</div>
</div>
<div class="row my-3">
<div class="inline baseline">
<span class="text-gray-400 text-lg">${window.lang.strings("expiry")}</span> <span class="text-2xl referral-expiry"></span>
<div>
</div>
`;
this._remainingUsesEl = this._infoArea.querySelector(".referral-remaining-uses") as HTMLSpanElement;
this._expiryEl = this._infoArea.querySelector(".referral-expiry") as HTMLSpanElement;
document.addEventListener("timefmt-change", () => {
this.expiry = this._expiryUnix;
});
this._button.addEventListener("click", () => {
toClipboard(this._url);
const content = this._button.innerHTML;
this._button.innerHTML = `
${window.lang.strings("copied")} <i class="ri-check-line ml-2"></i>
`;
this._button.classList.add("~positive");
this._button.classList.remove("~info");
setTimeout(() => {
this._button.classList.add("~info");
this._button.classList.remove("~positive");
this._button.innerHTML = content;
}, 2000);
});
}
hide = () => this._card.classList.add("unfocused");
update = (referral: MyReferral) => {
this.code = referral.code;
this.remaining_uses = referral.remaining_uses;
this.no_limit = referral.no_limit;
this.expiry = referral.expiry;
this._card.classList.remove("unfocused");
};
}
class ExpiryCard { class ExpiryCard {
private _card: HTMLElement; private _card: HTMLElement;
private _expiry: Date; private _expiry: Date;
@ -327,6 +428,9 @@ class ExpiryCard {
var expiryCard = new ExpiryCard(statusCard); var expiryCard = new ExpiryCard(statusCard);
var referralCard: ReferralCard;
if (window.referralsEnabled) referralCard = new ReferralCard(document.getElementById("card-referrals"));
var contactMethodList = new ContactMethods(contactCard); var contactMethodList = new ContactMethods(contactCard);
const addEditEmail = (add: boolean): void => { const addEditEmail = (add: boolean): void => {
@ -523,59 +627,15 @@ document.addEventListener("details-reload", () => {
setBestRowSpan(passwordCard, true); setBestRowSpan(passwordCard, true);
} }
let referralCard = document.getElementById("card-referrals"); if (window.referralsEnabled) {
if (window.referralsEnabled && typeof(referralCard) != "undefined" && referralCard != null) {
if (details.has_referrals) { if (details.has_referrals) {
_get("/my/referral", null, (req: XMLHttpRequest) => { _get("/my/referral", null, (req: XMLHttpRequest) => {
if (req.readyState != 4 || req.status != 200) return; if (req.readyState != 4 || req.status != 200) return;
const referral: MyReferral = req.response as MyReferral; const referral: MyReferral = req.response as MyReferral;
const infoArea = referralCard.querySelector(".user-referrals-info") as HTMLDivElement; referralCard.update(referral);
infoArea.innerHTML = `
<div class="row my-3">
<div class="inline baseline">
<span class="text-2xl">${referral.no_limit ? "∞" : referral.remaining_uses}</span> <span class="text-gray-400 text-lg">${window.lang.strings("inviteRemainingUses")}</span>
</div>
</div>
<div class="row my-3">
<div class="inline baseline">
<span class="text-gray-400 text-lg">${window.lang.strings("expiry")}</span> <span class="text-2xl">${toDateString(new Date(referral.expiry * 1000))}</span>
<div>
</div>
`;
const linkButton = referralCard.querySelector(".user-referrals-button") as HTMLButtonElement;
let codeLink = window.location.href;
for (let split of ["#", "?", "account", "my"]) {
codeLink = codeLink.split(split)[0];
}
if (codeLink.slice(-1) != "/") { codeLink += "/"; }
codeLink = codeLink + "invite/" + referral.code;
linkButton.addEventListener("click", () => {
toClipboard(codeLink);
const content = linkButton.innerHTML;
linkButton.innerHTML = `
${window.lang.strings("copied")} <i class="ri-check-line ml-2"></i>
`;
linkButton.classList.add("~positive");
linkButton.classList.remove("~info");
setTimeout(() => {
linkButton.classList.add("~info");
linkButton.classList.remove("~positive");
linkButton.innerHTML = content;
}, 2000);
});
referralCard.classList.remove("unfocused");
}); });
} else { } else {
referralCard.classList.add("unfocused"); referralCard.hide();
} }
} }
} }

View File

@ -619,6 +619,14 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
} }
userPageAddress += "/my/account" userPageAddress += "/my/account"
fromUser := ""
if inv.ReferrerJellyfinID != "" {
sender, status, err := app.jf.UserByID(inv.ReferrerJellyfinID, false)
if status == 200 && err == nil {
fromUser = sender.Name
}
}
data := gin.H{ data := gin.H{
"urlBase": app.getURLBase(gc), "urlBase": app.getURLBase(gc),
"cssClass": app.cssClass, "cssClass": app.cssClass,
@ -654,6 +662,7 @@ func (app *appContext) InviteProxy(gc *gin.Context) {
"reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""), "reCAPTCHASiteKey": app.config.Section("captcha").Key("recaptcha_site_key").MustString(""),
"userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false), "userPageEnabled": app.config.Section("user_page").Key("enabled").MustBool(false),
"userPageAddress": userPageAddress, "userPageAddress": userPageAddress,
"fromUser": fromUser,
} }
if telegram { if telegram {
data["telegramPIN"] = app.telegram.NewAuthToken() data["telegramPIN"] = app.telegram.NewAuthToken()