mirror of
https://github.com/hrfee/jfa-go.git
synced 2026-01-18 16:47:42 +01:00
ivnites: use actual inviteDTO for DOMInvite
no intermediary parsing step. Also, moved the date -> duration (3mo6d3h sorta thing) to the web, there's now a ValidTill field with a unix timestamp. Used the new Temporal api with a polyfill. Bumped api version (although it still isn't semver).
This commit is contained in:
@@ -359,21 +359,22 @@ func (app *appContext) GetInviteUsedCount(gc *gin.Context) {
|
|||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
// @tags Invites,Statistics
|
// @tags Invites,Statistics
|
||||||
func (app *appContext) GetInvites(gc *gin.Context) {
|
func (app *appContext) GetInvites(gc *gin.Context) {
|
||||||
currentTime := time.Now()
|
// currentTime := time.Now()
|
||||||
app.checkInvites()
|
app.checkInvites()
|
||||||
var invites []inviteDTO
|
var invites []inviteDTO
|
||||||
for _, inv := range app.storage.GetInvites() {
|
for _, inv := range app.storage.GetInvites() {
|
||||||
if inv.IsReferral {
|
if inv.IsReferral {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
years, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
|
// years, months, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
|
||||||
months += years * 12
|
// months += years * 12
|
||||||
invite := inviteDTO{
|
invite := inviteDTO{
|
||||||
Code: inv.Code,
|
Code: inv.Code,
|
||||||
Months: months,
|
ValidTill: inv.ValidTill.Unix(),
|
||||||
Days: days,
|
// Months: months,
|
||||||
Hours: hours,
|
// Days: days,
|
||||||
Minutes: minutes,
|
// Hours: hours,
|
||||||
|
// Minutes: minutes,
|
||||||
UserExpiry: inv.UserExpiry,
|
UserExpiry: inv.UserExpiry,
|
||||||
UserMonths: inv.UserMonths,
|
UserMonths: inv.UserMonths,
|
||||||
UserDays: inv.UserDays,
|
UserDays: inv.UserDays,
|
||||||
|
|||||||
@@ -180,7 +180,7 @@ func TestInvite(t *testing.T) {
|
|||||||
Created: time.Now(),
|
Created: time.Now(),
|
||||||
ValidTill: time.Now().Add(30 * time.Minute),
|
ValidTill: time.Now().Add(30 * time.Minute),
|
||||||
}
|
}
|
||||||
msg, err := e.constructInvite(inv, false)
|
msg, err := e.constructInvite(&inv, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatalf("failed construct: %+v", err)
|
t.Fatalf("failed construct: %+v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
2
main.go
2
main.go
@@ -715,7 +715,7 @@ func flagPassed(name string) (found bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// @title jfa-go internal API
|
// @title jfa-go internal API
|
||||||
// @version 0.6.0
|
// @version 0.6.1
|
||||||
// @description API for the jfa-go frontend
|
// @description API for the jfa-go frontend
|
||||||
// @contact.name Harvey Tindall
|
// @contact.name Harvey Tindall
|
||||||
// @contact.email hrfee@hrfee.dev
|
// @contact.email hrfee@hrfee.dev
|
||||||
|
|||||||
25
models.go
25
models.go
@@ -116,25 +116,22 @@ type newProfileDTO struct {
|
|||||||
|
|
||||||
type inviteDTO struct {
|
type inviteDTO struct {
|
||||||
Code string `json:"code" example:"sajdlj23423j23"` // Invite code
|
Code string `json:"code" example:"sajdlj23423j23"` // Invite code
|
||||||
Months int `json:"months" example:"1"` // Number of months till expiry
|
ValidTill int64 `json:"valid_till" example:"1617737207510"` // Unix timestamp of expiry
|
||||||
Days int `json:"days" example:"1"` // Number of days till expiry
|
UserExpiry bool `json:"user_expiry"` // Whether or not user expiry is enabled
|
||||||
Hours int `json:"hours" example:"2"` // Number of hours till expiry
|
UserMonths int `json:"user_months,omitempty" example:"1"` // Number of months till user expiry
|
||||||
Minutes int `json:"minutes" example:"3"` // Number of minutes till expiry
|
UserDays int `json:"user_days,omitempty" example:"1"` // Number of days till user expiry
|
||||||
UserExpiry bool `json:"user-expiry"` // Whether or not user expiry is enabled
|
UserHours int `json:"user_hours,omitempty" example:"2"` // Number of hours till user expiry
|
||||||
UserMonths int `json:"user-months,omitempty" example:"1"` // Number of months till user expiry
|
UserMinutes int `json:"user_minutes,omitempty" example:"3"` // Number of minutes till user expiry
|
||||||
UserDays int `json:"user-days,omitempty" example:"1"` // Number of days till user expiry
|
|
||||||
UserHours int `json:"user-hours,omitempty" example:"2"` // Number of hours till user expiry
|
|
||||||
UserMinutes int `json:"user-minutes,omitempty" example:"3"` // Number of minutes till user expiry
|
|
||||||
Created int64 `json:"created" example:"1617737207510"` // Date of creation
|
Created int64 `json:"created" example:"1617737207510"` // Date of creation
|
||||||
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
Profile string `json:"profile" example:"DefaultProfile"` // Profile used on this invite
|
||||||
UsedBy map[string]int64 `json:"used-by,omitempty"` // Users who have used this invite mapped to their creation time in Epoch/Unix time
|
UsedBy map[string]int64 `json:"used_by,omitempty"` // Users who have used this invite mapped to their creation time in Epoch/Unix time
|
||||||
NoLimit bool `json:"no-limit,omitempty"` // If true, invite can be used any number of times
|
NoLimit bool `json:"no_limit"` // If true, invite can be used any number of times
|
||||||
RemainingUses int `json:"remaining-uses,omitempty"` // Remaining number of uses (if applicable)
|
RemainingUses int `json:"remaining_uses,omitempty"` // Remaining number of uses (if applicable)
|
||||||
SendTo string `json:"send_to,omitempty"` // DEPRECATED Email/Discord username the invite was sent to (if applicable)
|
SendTo string `json:"send_to,omitempty"` // DEPRECATED Email/Discord username the invite was sent to (if applicable)
|
||||||
SentTo SentToList `json:"sent_to,omitempty"` // Email/Discord usernames attempts were made to send this invite to, and a failure reason if failed.
|
SentTo SentToList `json:"sent_to,omitempty"` // Email/Discord usernames attempts were made to send this invite to, and a failure reason if failed.
|
||||||
|
|
||||||
NotifyExpiry bool `json:"notify-expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
NotifyExpiry bool `json:"notify_expiry,omitempty"` // Whether to notify the requesting user of expiry or not
|
||||||
NotifyCreation bool `json:"notify-creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
NotifyCreation bool `json:"notify_creation,omitempty"` // Whether to notify the requesting user of account creation or not
|
||||||
Label string `json:"label,omitempty" example:"For Friends"` // Optional label for the invite
|
Label string `json:"label,omitempty" example:"For Friends"` // Optional label for the invite
|
||||||
UserLabel string `json:"user_label,omitempty" example:"Friend"` // Label to apply to users created w/ this invite.
|
UserLabel string `json:"user_label,omitempty" example:"Friend"` // Label to apply to users created w/ this invite.
|
||||||
}
|
}
|
||||||
|
|||||||
16
package-lock.json
generated
16
package-lock.json
generated
@@ -30,6 +30,7 @@
|
|||||||
"remixicon": "^4.3.0",
|
"remixicon": "^4.3.0",
|
||||||
"remove-markdown": "^0.5.0",
|
"remove-markdown": "^0.5.0",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
|
"temporal-polyfill": "^0.3.0",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
"uncss": "^0.17.3"
|
"uncss": "^0.17.3"
|
||||||
},
|
},
|
||||||
@@ -6755,6 +6756,21 @@
|
|||||||
"node": ">=8.0"
|
"node": ">=8.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"node_modules/temporal-polyfill": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/temporal-polyfill/-/temporal-polyfill-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-qNsTkX9K8hi+FHDfHmf22e/OGuXmfBm9RqNismxBrnSmZVJKegQ+HYYXT+R7Ha8F/YSm2Y34vmzD4cxMu2u95g==",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"temporal-spec": "0.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"node_modules/temporal-spec": {
|
||||||
|
"version": "0.3.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/temporal-spec/-/temporal-spec-0.3.0.tgz",
|
||||||
|
"integrity": "sha512-n+noVpIqz4hYgFSMOSiINNOUOMFtV5cZQNCmmszA6GiVFVRt3G7AqVyhXjhCSmowvQn+NsGn+jMDMKJYHd3bSQ==",
|
||||||
|
"license": "ISC"
|
||||||
|
},
|
||||||
"node_modules/terser": {
|
"node_modules/terser": {
|
||||||
"version": "5.43.1",
|
"version": "5.43.1",
|
||||||
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
"resolved": "https://registry.npmjs.org/terser/-/terser-5.43.1.tgz",
|
||||||
|
|||||||
@@ -38,6 +38,7 @@
|
|||||||
"remixicon": "^4.3.0",
|
"remixicon": "^4.3.0",
|
||||||
"remove-markdown": "^0.5.0",
|
"remove-markdown": "^0.5.0",
|
||||||
"tailwindcss": "^3.3.2",
|
"tailwindcss": "^3.3.2",
|
||||||
|
"temporal-polyfill": "^0.3.0",
|
||||||
"typescript": "^5.1.3",
|
"typescript": "^5.1.3",
|
||||||
"uncss": "^0.17.3"
|
"uncss": "^0.17.3"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
declare var window: GlobalWindow;
|
declare var window: GlobalWindow;
|
||||||
import dateParser from "any-date-parser";
|
import dateParser from "any-date-parser";
|
||||||
|
import { Temporal } from 'temporal-polyfill';
|
||||||
|
|
||||||
export function toDateString(date: Date): string {
|
export function toDateString(date: Date): string {
|
||||||
const locale = window.language || (window as any).navigator.userLanguage || window.navigator.language;
|
const locale = window.language || (window as any).navigator.userLanguage || window.navigator.language;
|
||||||
@@ -41,6 +42,36 @@ export const parseDateString = (value: string): ParsedDate => {
|
|||||||
return out;
|
return out;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// DateCountdown sets the given el's textContent to the time till the given date (unixSeconds), updating
|
||||||
|
// every minute. It returns the timeout, so it can be later removed with clearTimeout if desired.
|
||||||
|
export function DateCountdown(el: HTMLElement, unixSeconds: number): ReturnType<typeof setTimeout> {
|
||||||
|
let then = Temporal.Instant.fromEpochMilliseconds(unixSeconds * 1000);
|
||||||
|
const toString = (): string => {
|
||||||
|
let out = "";
|
||||||
|
let now = Temporal.Now.instant();
|
||||||
|
let nowPlain = Temporal.Now.plainDateTimeISO();
|
||||||
|
let diff = now.until(then).round({
|
||||||
|
largestUnit: "years",
|
||||||
|
smallestUnit: "minutes",
|
||||||
|
relativeTo: nowPlain
|
||||||
|
});
|
||||||
|
// FIXME: I'd really like this to be localized, but don't know of any nice solutions.
|
||||||
|
const fields = [diff.years, diff.months, diff.days, diff.hours, diff.minutes];
|
||||||
|
const abbrevs = ["y", "mo", "d", "h", "m"];
|
||||||
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
if (fields[i]) {
|
||||||
|
out += ""+fields[i] + abbrevs[i] + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return out.slice(0, -1);
|
||||||
|
};
|
||||||
|
const update = () => {
|
||||||
|
el.textContent = toString();
|
||||||
|
};
|
||||||
|
update();
|
||||||
|
return setTimeout(update, 60000);
|
||||||
|
}
|
||||||
|
|
||||||
export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => {
|
export const _get = (url: string, data: Object, onreadystatechange: (req: XMLHttpRequest) => void, noConnectionError: boolean = false): void => {
|
||||||
let req = new XMLHttpRequest();
|
let req = new XMLHttpRequest();
|
||||||
if (window.pages) { url = window.pages.Base + url; }
|
if (window.pages) { url = window.pages.Base + url; }
|
||||||
|
|||||||
@@ -1,9 +1,11 @@
|
|||||||
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString, SetupCopyButton, addLoader, removeLoader } from "../modules/common.js";
|
import { _get, _post, _delete, toClipboard, toggleLoader, toDateString, SetupCopyButton, addLoader, removeLoader, DateCountdown } from "../modules/common.js";
|
||||||
import { DiscordSearch, DiscordUser, newDiscordSearch } from "../modules/discord.js";
|
import { DiscordSearch, DiscordUser, newDiscordSearch } from "../modules/discord.js";
|
||||||
import { reloadProfileNames } from "../modules/profiles.js";
|
import { reloadProfileNames } from "../modules/profiles.js";
|
||||||
|
|
||||||
declare var window: GlobalWindow;
|
declare var window: GlobalWindow;
|
||||||
|
|
||||||
|
const INF = "∞";
|
||||||
|
|
||||||
export const generateCodeLink = (code: string): string => {
|
export const generateCodeLink = (code: string): string => {
|
||||||
// let codeLink = window.pages.Base + window.pages.Form + "/" + code;
|
// let codeLink = window.pages.Base + window.pages.Form + "/" + code;
|
||||||
let codeLink = window.pages.ExternalURI + window.pages.Form + "/" + code;
|
let codeLink = window.pages.ExternalURI + window.pages.Form + "/" + code;
|
||||||
@@ -15,11 +17,11 @@ class DOMInvite implements Invite {
|
|||||||
let state: { [code: string]: { [type: string]: boolean } } = {};
|
let state: { [code: string]: { [type: string]: boolean } } = {};
|
||||||
let revertChanges: () => void;
|
let revertChanges: () => void;
|
||||||
if (checkbox.classList.contains("inv-notify-expiry")) {
|
if (checkbox.classList.contains("inv-notify-expiry")) {
|
||||||
revertChanges = () => { this.notifyExpiry = !this.notifyExpiry };
|
revertChanges = () => { this.notify_expiry = !this.notify_expiry };
|
||||||
state[this.code] = { "notify-expiry": this.notifyExpiry };
|
state[this.code] = { "notify-expiry": this.notify_expiry };
|
||||||
} else {
|
} else {
|
||||||
revertChanges = () => { this.notifyCreation = !this.notifyCreation };
|
revertChanges = () => { this.notify_creation = !this.notify_creation };
|
||||||
state[this.code] = { "notify-creation": this.notifyCreation };
|
state[this.code] = { "notify-creation": this.notify_creation };
|
||||||
}
|
}
|
||||||
_post("/invites/notify", state, (req: XMLHttpRequest) => {
|
_post("/invites/notify", state, (req: XMLHttpRequest) => {
|
||||||
if (req.readyState == 4 && !(req.status == 200 || req.status == 204)) {
|
if (req.readyState == 4 && !(req.status == 200 || req.status == 204)) {
|
||||||
@@ -78,44 +80,92 @@ class DOMInvite implements Invite {
|
|||||||
}
|
}
|
||||||
private _codeLink: string;
|
private _codeLink: string;
|
||||||
|
|
||||||
private _expiresIn: string;
|
private _validTill: number;
|
||||||
get expiresIn(): string { return this._expiresIn }
|
private _validTillUpdater: ReturnType<typeof setTimeout> = null;
|
||||||
set expiresIn(expiry: string) {
|
get valid_till(): number { return this._validTill; }
|
||||||
this._expiresIn = expiry;
|
set valid_till(v: number) {
|
||||||
this._codeArea.querySelector("span.inv-duration").textContent = expiry;
|
this._validTill = v;
|
||||||
|
if (this._validTillUpdater) clearTimeout(this._validTillUpdater);
|
||||||
|
this._validTillUpdater = DateCountdown(this._codeArea.querySelector("span.inv-duration"), v);
|
||||||
}
|
}
|
||||||
|
|
||||||
private _userExpiry: string;
|
private _userExpiryEnabled: boolean;
|
||||||
get userExpiryTime(): string { return this._userExpiry; }
|
get user_expiry(): boolean { return this._userExpiryEnabled; }
|
||||||
set userExpiryTime(d: string) {
|
set user_expiry(v: boolean) { this._userExpiryEnabled = v; }
|
||||||
|
private _userExpiry = { months: 0, days: 0, hours: 0, minutes: 0 };
|
||||||
|
private _userExpiryString: string;
|
||||||
|
get user_months(): number { return this._userExpiry.months; }
|
||||||
|
get user_days(): number { return this._userExpiry.days; }
|
||||||
|
get user_hours(): number { return this._userExpiry.hours; }
|
||||||
|
get user_minutes(): number { return this._userExpiry.minutes; }
|
||||||
|
set user_months(v: number) {
|
||||||
|
this._userExpiry.months = v;
|
||||||
|
this._updateUserExpiry();
|
||||||
|
}
|
||||||
|
set user_days(v: number) {
|
||||||
|
this._userExpiry.days = v;
|
||||||
|
this._updateUserExpiry();
|
||||||
|
}
|
||||||
|
set user_hours(v: number) {
|
||||||
|
this._userExpiry.hours = v;
|
||||||
|
this._updateUserExpiry();
|
||||||
|
}
|
||||||
|
set user_minutes(v: number) {
|
||||||
|
this._userExpiry.minutes = v;
|
||||||
|
this._updateUserExpiry();
|
||||||
|
}
|
||||||
|
set user_expiry_time(v: { months: number, days: number, hours: number, minutes: number }) {
|
||||||
|
this._userExpiry = v;
|
||||||
|
this._updateUserExpiry()
|
||||||
|
}
|
||||||
|
private _updateUserExpiry() {
|
||||||
const expiry = this._middle.querySelector("span.user-expiry") as HTMLSpanElement;
|
const expiry = this._middle.querySelector("span.user-expiry") as HTMLSpanElement;
|
||||||
if (!d) {
|
this._userExpiryString = "";
|
||||||
|
if (!(this._userExpiry.months || this._userExpiry.days || this._userExpiry.hours || this._userExpiry.minutes)) {
|
||||||
expiry.textContent = "";
|
expiry.textContent = "";
|
||||||
expiry.parentElement.classList.add("unfocused");
|
expiry.parentElement.classList.add("unfocused");
|
||||||
} else {
|
} else {
|
||||||
expiry.textContent = window.lang.strings("userExpiry");
|
expiry.textContent = window.lang.strings("userExpiry");
|
||||||
expiry.parentElement.classList.remove("unfocused");
|
expiry.parentElement.classList.remove("unfocused");
|
||||||
|
const fields = ["months", "days", "hours", "minutes"].map((v) => this._userExpiry[v]);
|
||||||
|
const abbrevs = ["mo", "d", "h", "m"];
|
||||||
|
for (let i = 0; i < fields.length; i++) {
|
||||||
|
if (fields[i]) {
|
||||||
|
this._userExpiryString += ""+fields[i] + abbrevs[i] + " ";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._userExpiryString = this._userExpiryString.slice(0, -1);
|
||||||
}
|
}
|
||||||
this._userExpiry = d;
|
this._middle.querySelector("strong.user-expiry-time").textContent = this._userExpiryString;
|
||||||
this._middle.querySelector("strong.user-expiry-time").textContent = d;
|
}
|
||||||
|
|
||||||
|
private _noLimit: boolean = false;
|
||||||
|
get no_limit(): boolean { return this._noLimit; }
|
||||||
|
set no_limit(v: boolean) {
|
||||||
|
this._noLimit = v;
|
||||||
|
const remaining = this._middle.querySelector("strong.inv-remaining") as HTMLElement;
|
||||||
|
if (!this.no_limit) remaining.textContent = ""+this._remainingUses;
|
||||||
|
else remaining.textContent = INF;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _remainingUses: string = "1";
|
private _remainingUses: number = 1;
|
||||||
get remainingUses(): string { return this._remainingUses; }
|
get remaining_uses(): number { return this._remainingUses; }
|
||||||
set remainingUses(remaining: string) {
|
set remaining_uses(v: number) {
|
||||||
this._remainingUses = remaining;
|
this._remainingUses = v;
|
||||||
this._middle.querySelector("strong.inv-remaining").textContent = remaining;
|
const remaining = this._middle.querySelector("strong.inv-remaining") as HTMLElement;
|
||||||
|
if (!this.no_limit) remaining.textContent = ""+this._remainingUses;
|
||||||
|
else remaining.textContent = INF;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _send_to: string = "";
|
private _send_to: string = "";
|
||||||
get send_to(): string { return this._send_to };
|
get send_to(): string { return this._send_to };
|
||||||
set send_to(address: string) {
|
set send_to(address: string | null) {
|
||||||
this._send_to = address;
|
this._send_to = address;
|
||||||
const container = this._infoArea.querySelector(".tooltip") as HTMLDivElement;
|
const container = this._infoArea.querySelector(".tooltip") as HTMLDivElement;
|
||||||
const icon = container.querySelector("i");
|
const icon = container.querySelector("i");
|
||||||
const chip = container.querySelector("span.inv-email-chip");
|
const chip = container.querySelector("span.inv-email-chip");
|
||||||
const tooltip = container.querySelector("span.content") as HTMLSpanElement;
|
const tooltip = container.querySelector("span.content") as HTMLSpanElement;
|
||||||
if (address == "") {
|
if (!address) {
|
||||||
icon.classList.remove("ri-mail-line");
|
icon.classList.remove("ri-mail-line");
|
||||||
icon.classList.remove("ri-mail-close-line");
|
icon.classList.remove("ri-mail-close-line");
|
||||||
chip.classList.remove("~neutral");
|
chip.classList.remove("~neutral");
|
||||||
@@ -177,10 +227,10 @@ class DOMInvite implements Invite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _usedBy: { [name: string]: number };
|
private _usedBy: { [name: string]: number };
|
||||||
get usedBy(): { [name: string]: number } { return this._usedBy; }
|
get used_by(): { [name: string]: number } { return this._usedBy; }
|
||||||
set usedBy(uB: { [name: string]: number }) {
|
set used_by(uB: { [name: string]: number } | null) {
|
||||||
this._usedBy = uB;
|
this._usedBy = uB;
|
||||||
if (Object.keys(uB).length == 0) {
|
if (!uB || Object.keys(uB).length == 0) {
|
||||||
this._right.classList.add("empty");
|
this._right.classList.add("empty");
|
||||||
this._userTable.innerHTML = `<p class="content">${window.lang.strings("inviteNoUsersCreated")}</p>`;
|
this._userTable.innerHTML = `<p class="content">${window.lang.strings("inviteNoUsersCreated")}</p>`;
|
||||||
return;
|
return;
|
||||||
@@ -226,15 +276,15 @@ class DOMInvite implements Invite {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private _notifyExpiry: boolean = false;
|
private _notifyExpiry: boolean = false;
|
||||||
get notifyExpiry(): boolean { return this._notifyExpiry }
|
get notify_expiry(): boolean { return this._notifyExpiry }
|
||||||
set notifyExpiry(state: boolean) {
|
set notify_expiry(state: boolean) {
|
||||||
this._notifyExpiry = state;
|
this._notifyExpiry = state;
|
||||||
(this._left.querySelector("input.inv-notify-expiry") as HTMLInputElement).checked = state;
|
(this._left.querySelector("input.inv-notify-expiry") as HTMLInputElement).checked = state;
|
||||||
}
|
}
|
||||||
|
|
||||||
private _notifyCreation: boolean = false;
|
private _notifyCreation: boolean = false;
|
||||||
get notifyCreation(): boolean { return this._notifyCreation }
|
get notify_creation(): boolean { return this._notifyCreation }
|
||||||
set notifyCreation(state: boolean) {
|
set notify_creation(state: boolean) {
|
||||||
this._notifyCreation = state;
|
this._notifyCreation = state;
|
||||||
(this._left.querySelector("input.inv-notify-creation") as HTMLInputElement).checked = state;
|
(this._left.querySelector("input.inv-notify-creation") as HTMLInputElement).checked = state;
|
||||||
}
|
}
|
||||||
@@ -366,7 +416,7 @@ class DOMInvite implements Invite {
|
|||||||
<a class="invite-link text-black dark:text-white font-mono bg-inherit truncate" href=""></a>
|
<a class="invite-link text-black dark:text-white font-mono bg-inherit truncate" href=""></a>
|
||||||
<button class="invite-copy-button"></button>
|
<button class="invite-copy-button"></button>
|
||||||
</div>
|
</div>
|
||||||
<span class="inv-duration"></span>
|
<span>${window.lang.var("strings", "inviteExpiresInTime", "<span class=\"inv-duration\"></span>")}</span>
|
||||||
`;
|
`;
|
||||||
const copyButton = this._codeArea.getElementsByClassName("invite-copy-button")[0] as HTMLButtonElement;
|
const copyButton = this._codeArea.getElementsByClassName("invite-copy-button")[0] as HTMLButtonElement;
|
||||||
SetupCopyButton(copyButton, this._codeLink);
|
SetupCopyButton(copyButton, this._codeLink);
|
||||||
@@ -476,30 +526,39 @@ class DOMInvite implements Invite {
|
|||||||
document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);
|
document.addEventListener("profileLoadEvent", () => { this.loadProfiles(); }, false);
|
||||||
document.addEventListener("timefmt-change", () => {
|
document.addEventListener("timefmt-change", () => {
|
||||||
this.created = this.created;
|
this.created = this.created;
|
||||||
this.usedBy = this.usedBy;
|
this.used_by = this.used_by;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
update = (invite: Invite) => {
|
update = (invite: Invite) => {
|
||||||
this.code = invite.code;
|
this.code = invite.code;
|
||||||
|
this.valid_till = invite.valid_till;
|
||||||
|
if (invite.user_expiry) {
|
||||||
|
this.user_expiry = invite.user_expiry;
|
||||||
|
this.user_expiry_time = {
|
||||||
|
months: invite.user_months,
|
||||||
|
days: invite.user_days,
|
||||||
|
hours: invite.user_hours,
|
||||||
|
minutes: invite.user_minutes
|
||||||
|
};
|
||||||
|
}
|
||||||
this.created = invite.created;
|
this.created = invite.created;
|
||||||
|
this.profile = invite.profile;
|
||||||
|
this.used_by = invite.used_by;
|
||||||
|
this.no_limit = invite.no_limit ? invite.no_limit : false;
|
||||||
|
this.remaining_uses = invite.remaining_uses;
|
||||||
this.send_to = invite.send_to;
|
this.send_to = invite.send_to;
|
||||||
this.sent_to = invite.sent_to;
|
this.sent_to = invite.sent_to;
|
||||||
this.expiresIn = invite.expiresIn;
|
|
||||||
if (window.notificationsEnabled) {
|
if (window.notificationsEnabled) {
|
||||||
this.notifyCreation = invite.notifyCreation;
|
this.notify_creation = invite.notify_creation;
|
||||||
this.notifyExpiry = invite.notifyExpiry;
|
this.notify_expiry = invite.notify_expiry;
|
||||||
}
|
}
|
||||||
this.profile = invite.profile;
|
|
||||||
this.remainingUses = invite.remainingUses;
|
|
||||||
this.usedBy = invite.usedBy;
|
|
||||||
if (invite.label) {
|
if (invite.label) {
|
||||||
this.label = invite.label;
|
this.label = invite.label;
|
||||||
}
|
}
|
||||||
if (invite.user_label) {
|
if (invite.user_label) {
|
||||||
this.user_label = invite.user_label;
|
this.user_label = invite.user_label;
|
||||||
}
|
}
|
||||||
this.userExpiryTime = invite.userExpiryTime || "";
|
|
||||||
this._sendToDialog = new SendToDialog(this._middle.getElementsByClassName("invite-send-to-dialog")[0] as HTMLElement, invite, () => {
|
this._sendToDialog = new SendToDialog(this._middle.getElementsByClassName("invite-send-to-dialog")[0] as HTMLElement, invite, () => {
|
||||||
const needsUpdatingEvent = new CustomEvent("inviteNeedsUpdating", { detail: this.code });
|
const needsUpdatingEvent = new CustomEvent("inviteNeedsUpdating", { detail: this.code });
|
||||||
document.dispatchEvent(needsUpdatingEvent);
|
document.dispatchEvent(needsUpdatingEvent);
|
||||||
@@ -605,8 +664,7 @@ export class DOMInviteList implements InviteList {
|
|||||||
// at end delete all remaining in list from dom
|
// at end delete all remaining in list from dom
|
||||||
let invitesOnDOM: { [code: string]: boolean } = {};
|
let invitesOnDOM: { [code: string]: boolean } = {};
|
||||||
for (let code in this.invites) { invitesOnDOM[code] = true; }
|
for (let code in this.invites) { invitesOnDOM[code] = true; }
|
||||||
for (let inv of (data["invites"] as Array<any>)) {
|
for (let invite of (data["invites"] as Array<Invite>)) {
|
||||||
const invite = parseInvite(inv);
|
|
||||||
if (invite.code in this.invites) {
|
if (invite.code in this.invites) {
|
||||||
this.invites[invite.code].update(invite);
|
this.invites[invite.code].update(invite);
|
||||||
delete invitesOnDOM[invite.code];
|
delete invitesOnDOM[invite.code];
|
||||||
@@ -626,47 +684,6 @@ export class DOMInviteList implements InviteList {
|
|||||||
|
|
||||||
export const inviteURLEvent = (id: string) => { return new CustomEvent(DOMInviteList._inviteURLEvent, {"detail": id}) };
|
export const inviteURLEvent = (id: string) => { return new CustomEvent(DOMInviteList._inviteURLEvent, {"detail": id}) };
|
||||||
|
|
||||||
// FIXME: Please, i beg you, get rid of this horror!
|
|
||||||
function parseInvite(invite: { [f: string]: string | number | { [name: string]: number } | boolean | SentToList }): Invite {
|
|
||||||
let parsed: Invite = {};
|
|
||||||
parsed.code = invite["code"] as string;
|
|
||||||
parsed.send_to = invite["send_to"] as string || "";
|
|
||||||
parsed.sent_to = invite["sent_to"] as SentToList || null;
|
|
||||||
parsed.label = invite["label"] as string || "";
|
|
||||||
parsed.user_label = invite["user_label"] as string || "";
|
|
||||||
let time = "";
|
|
||||||
let userExpiryTime = "";
|
|
||||||
const fields = ["months", "days", "hours", "minutes"];
|
|
||||||
let prefixes = [""];
|
|
||||||
if (invite["user-expiry"] as boolean) { prefixes.push("user-"); }
|
|
||||||
for (let i = 0; i < fields.length; i++) {
|
|
||||||
for (let j = 0; j < prefixes.length; j++) {
|
|
||||||
if (invite[prefixes[j]+fields[i]]) {
|
|
||||||
let abbreviation = fields[i][0];
|
|
||||||
if (fields[i] == "months") {
|
|
||||||
abbreviation += fields[i][1];
|
|
||||||
}
|
|
||||||
let text = `${invite[prefixes[j]+fields[i]]}${abbreviation} `;
|
|
||||||
if (prefixes[j] == "user-") {
|
|
||||||
userExpiryTime += text;
|
|
||||||
} else {
|
|
||||||
time += text;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
parsed.expiresIn = window.lang.var("strings", "inviteExpiresInTime", time.slice(0, -1));
|
|
||||||
parsed.userExpiry = invite["user-expiry"] as boolean;
|
|
||||||
parsed.userExpiryTime = userExpiryTime.slice(0, -1);
|
|
||||||
parsed.remainingUses = invite["no-limit"] ? "∞" : String(invite["remaining-uses"])
|
|
||||||
parsed.usedBy = invite["used-by"] as { [name: string]: number } || {} ;
|
|
||||||
parsed.created = invite["created"] as number || 0;
|
|
||||||
parsed.profile = invite["profile"] as string || "";
|
|
||||||
parsed.notifyExpiry = invite["notify-expiry"] as boolean || false;
|
|
||||||
parsed.notifyCreation = invite["notify-creation"] as boolean || false;
|
|
||||||
return parsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
export class createInvite {
|
export class createInvite {
|
||||||
private _sendTo: SendToDialog;
|
private _sendTo: SendToDialog;
|
||||||
private _userExpiryToggle = document.getElementById("create-user-expiry-enabled") as HTMLInputElement;
|
private _userExpiryToggle = document.getElementById("create-user-expiry-enabled") as HTMLInputElement;
|
||||||
|
|||||||
@@ -71,20 +71,25 @@ declare interface InviteList {
|
|||||||
}
|
}
|
||||||
|
|
||||||
declare interface Invite {
|
declare interface Invite {
|
||||||
code?: string;
|
code: string; // Invite code
|
||||||
expiresIn?: string;
|
valid_till: number; // Unix timestamp of expiry
|
||||||
remainingUses?: string;
|
user_expiry: boolean; // Whether or not user expiry is enabled
|
||||||
send_to?: string; // DEPRECATED: use sent_to instead.
|
user_months?: number; // Number of months till user expiry
|
||||||
sent_to?: SentToList;
|
user_days?: number; // Number of days till user expiry
|
||||||
usedBy?: { [name: string]: number };
|
user_hours?: number; // Number of hours till user expiry
|
||||||
created?: number;
|
user_minutes?: number; // Number of minutes till user expiry
|
||||||
notifyExpiry?: boolean;
|
created: number; // Date of creation (unix timestamp)
|
||||||
notifyCreation?: boolean;
|
profile: string; // Profile used on this invite
|
||||||
profile?: string;
|
used_by?: { [user: string]: number }; // Users who have used this invite mapped to their creation time in Epoch/Unix time
|
||||||
label?: string;
|
no_limit: boolean; // If true, invite can be used any number of times
|
||||||
user_label?: string;
|
remaining_uses?: number; // Remaining number of uses (if applicable)
|
||||||
userExpiry?: boolean;
|
send_to?: string; // DEPRECATED Email/Discord username the invite was sent to (if applicable)
|
||||||
userExpiryTime?: string;
|
sent_to?: SentToList; // Email/Discord usernames attempts were made to send this invite to, and a failure reason if failed.
|
||||||
|
|
||||||
|
notify_expiry?: boolean; // Whether to notify the requesting user of expiry or not
|
||||||
|
notify_creation?: boolean; // Whether to notify the requesting user of account creation or not
|
||||||
|
label?: string; // Optional label for the invite
|
||||||
|
user_label?: string; // Label to apply to users created w/ this invite.
|
||||||
}
|
}
|
||||||
|
|
||||||
declare interface SendFailure {
|
declare interface SendFailure {
|
||||||
|
|||||||
Reference in New Issue
Block a user