Account Created: "hrfee"
diff --git a/lang/admin/en-us.json b/lang/admin/en-us.json
index c9cffef..75673a9 100644
--- a/lang/admin/en-us.json
+++ b/lang/admin/en-us.json
@@ -55,6 +55,8 @@
"reset": "Reset",
"donate": "Donate",
"unlink": "Unlink Account",
+ "deleted": "Deleted",
+ "disabled": "Disabled",
"sendPWR": "Send Password Reset",
"noResultsFound": "No Results Found",
"contactThrough": "Contact through:",
@@ -131,7 +133,20 @@
"buildTime": "Build Time",
"builtBy": "Built By",
"loginNotAdmin": "Not an Admin?",
- "referrer": "Referrer"
+ "referrer": "Referrer",
+ "accountLinked": "{user}: {contactMethod} linked",
+ "accountUnlinked": "{user}: {contactMethod} removed",
+ "accountResetPassword": "{user} reset their password",
+ "accountChangedPassword": "{user} changed their password",
+ "accountCreated": "Account created: {user}",
+ "accountDeleted": "Account deleted: {user}",
+ "accountDisabled": "Account disabled: {user}",
+ "accountReEnabled": "Account re-enabled: {user}",
+ "accountExpired": "Account expired: {user}",
+ "userDeleted": "User was deleted.",
+ "userDisabled": "User was disabled",
+ "inviteCreated": "Invite created: {invite}",
+ "inviteDeleted": "Invite deleted: {invite}"
},
"notifications": {
"changedEmailAddress": "Changed email address of {n}.",
@@ -168,6 +183,7 @@
"errorApplyUpdate": "Failed to apply update, try manually.",
"errorCheckUpdate": "Failed to check for update.",
"errorNoReferralTemplate": "Profile doesn't contain referral template, add one in settings.",
+ "errorLoadActivities": "Failed to load activities.",
"updateAvailable": "A new update is available, check settings.",
"noUpdatesAvailable": "No new updates available."
},
diff --git a/storage.go b/storage.go
index 5d5af8d..bfee54e 100644
--- a/storage.go
+++ b/storage.go
@@ -53,7 +53,7 @@ type Activity struct {
SourceType ActivitySource
Source string
InviteCode string // Only set for ActivityCreation
- Value string // Used for ActivityContactLinked, "email/discord/telegram/matrix"
+ Value string // Used for ActivityContactLinked, "email/discord/telegram/matrix", and Create/DeleteInvite, where it's the label.
Time time.Time
}
diff --git a/ts/admin.ts b/ts/admin.ts
index 78010d1..1a8109f 100644
--- a/ts/admin.ts
+++ b/ts/admin.ts
@@ -5,6 +5,7 @@ import { Tabs } from "./modules/tabs.js";
import { inviteList, createInvite } from "./modules/invites.js";
import { accountsList } from "./modules/accounts.js";
import { settingsList } from "./modules/settings.js";
+import { activityList } from "./modules/activity.js";
import { ProfileEditor } from "./modules/profiles.js";
import { _get, _post, notificationBox, whichAnimationEvent } from "./modules/common.js";
import { Updater } from "./modules/update.js";
@@ -89,6 +90,8 @@ var inviteCreator = new createInvite();
var accounts = new accountsList();
+var activity = new activityList();
+
window.invites = new inviteList();
var settings = new settingsList();
@@ -122,7 +125,7 @@ const tabs: { url: string, reloader: () => void }[] = [
},
{
url: "activity",
- reloader: () => {console.log("FIXME: Reload Activity")}
+ reloader: activity.reload
},
{
url: "settings",
@@ -171,6 +174,7 @@ const login = new Login(window.modals.login as Modal, "/", window.loginAppearanc
login.onLogin = () => {
console.log("Logged in.");
window.updater = new Updater();
+ // FIXME: Decide whether to autoload activity or not
setInterval(() => { window.invites.reload(); accounts.reload(); }, 30*1000);
const currentTab = window.tabs.current;
switch (currentTab) {
@@ -183,7 +187,9 @@ login.onLogin = () => {
case "settings":
settings.reload();
break;
- // FIXME: Reload activity
+ case "activity": // FIXME: fix URL clash with route
+ activity.reload();
+ break;
}
}
diff --git a/ts/modules/activity.ts b/ts/modules/activity.ts
index 14e7fef..104af07 100644
--- a/ts/modules/activity.ts
+++ b/ts/modules/activity.ts
@@ -1,3 +1,5 @@
+import { _get, toDateString } from "../modules/common.js";
+
export interface activity {
id: string;
type: string;
@@ -28,9 +30,11 @@ export class Activity { // FIXME: Add "implements"
private _card: HTMLElement;
private _title: HTMLElement;
private _time: HTMLElement;
+ private _timeUnix: number;
private _sourceType: HTMLElement;
private _source: HTMLElement;
private _referrer: HTMLElement;
+ private _expiryTypeBadge: HTMLElement;
private _act: activity;
get type(): string { return this._act.type; }
@@ -43,24 +47,87 @@ export class Activity { // FIXME: Add "implements"
if (i-1 == mood) this._card.classList.add(moodColours[i]);
else this._card.classList.remove(moodColours[i]);
}
+
+ if (this.type == "changePassword" || this.type == "resetPassword") {
+ let innerHTML = ``;
+ if (this.type == "changePassword") innerHTML = window.lang.strings("accountChangedPassword");
+ else innerHTML = window.lang.strings("accountResetPassword");
+ innerHTML = innerHTML.replace("{user}", `
FIXME`);
+ this._title.innerHTML = innerHTML;
+ } else if (this.type == "contactLinked" || this.type == "contactUnlinked") {
+ let platform = this._act.type;
+ if (platform == "email") {
+ platform = window.lang.strings("emailAddress");
+ } else {
+ platform = platform.charAt(0).toUpperCase() + platform.slice(1);
+ }
+ let innerHTML = ``;
+ if (this.type == "contactLinked") innerHTML = window.lang.strings("accountLinked");
+ else innerHTML = window.lang.strings("accountUnlinked");
+ innerHTML = innerHTML.replace("{user}", `
FIXME`).replace("{contactMethod}", platform);
+ this._title.innerHTML = innerHTML;
+ } else if (this.type == "creation") {
+ this._title.innerHTML = window.lang.strings("accountCreated").replace("{user}", `
FIXME`);
+ if (this.source_type == "user") {
+ this._referrer.innerHTML = `
${window.lang.strings("referrer")}FIXME`;
+ } else {
+ this._referrer.textContent = ``;
+ }
+ } else if (this.type == "deletion") {
+ if (this.source_type == "daemon") {
+ this._title.innerHTML = window.lang.strings("accountExpired").replace("{user}", `
FIXME`);
+ this._expiryTypeBadge.classList.add("~critical");
+ this._expiryTypeBadge.classList.remove("~warning");
+ this._expiryTypeBadge.textContent = window.lang.strings("deleted");
+ } else {
+ this._title.innerHTML = window.lang.strings("accountDeleted").replace("{user}", `
FIXME`);
+ }
+ } else if (this.type == "enabled") {
+ this._title.innerHTML = window.lang.strings("accountReEnabled").replace("{user}", `
FIXME`);
+ } else if (this.type == "disabled") {
+ if (this.source_type == "daemon") {
+ this._title.innerHTML = window.lang.strings("accountExpired").replace("{user}", `
FIXME`);
+ this._expiryTypeBadge.classList.add("~warning");
+ this._expiryTypeBadge.classList.remove("~critical");
+ this._expiryTypeBadge.textContent = window.lang.strings("disabled");
+ } else {
+ this._title.innerHTML = window.lang.strings("accountDisabled").replace("{user}", `
FIXME`);
+ }
+ } else if (this.type == "createInvite") {
+ this._title.innerHTML = window.lang.strings("inviteCreated").replace("{invite}", `
${this.value || this.invite_code}`);
+ } else if (this.type == "deleteInvite") {
+
+ this._title.innerHTML = window.lang.strings("inviteDeleted").replace("{invite}", this.value || this.invite_code);
+ }
+
+ /*} else if (this.source_type == "admin") {
+ // FIXME: Handle contactLinked/Unlinked, creation/deletion, enable/disable, createInvite/deleteInvite
+ } else if (this.source_type == "anon") {
+ this._referrer.innerHTML = ``;
+ } else if (this.source_type == "daemon") {
+ // FIXME: Handle deleteInvite, disabled, deletion
+ }*/
+ }
+
+ get time(): number { return this._timeUnix; }
+ set time(v: number) {
+ this._timeUnix = v;
+ this._time.textContent = toDateString(new Date(v*1000));
}
get source_type(): string { return this._act.source_type; }
set source_type(v: string) {
this._act.source_type = v;
- if (v == "user") {
- if (this.type == "creation") {
- this._referrer.innerHTML = `
${window.lang.strings("referrer")}FIXME`;
- } else if (this.type == "contactLinked" || this.type == "contactUnlinked" || this.type == "changePassword" || this.type == "resetPassword") {
- // FIXME: Reflect in title
- }
- } else if (v == "admin") {
- // FIXME: Handle contactLinked/Unlinked, creation/deletion, enable/disable, createInvite/deleteInvite
- } else if (v == "anon") {
- this._referrer.innerHTML = ``;
- } else if (v == "daemon") {
- // FIXME: Handle deleteInvite, disabled, deletion
- }
+ }
+
+ get invite_code(): string { return this._act.invite_code; }
+ set invite_code(v: string) {
+ this._act.invite_code = v;
+ }
+
+ get value(): string { return this._act.value; }
+ set value(v: string) {
+ this._act.value = v;
}
constructor(act: activity) {
@@ -69,7 +136,7 @@ export class Activity { // FIXME: Add "implements"
this._card.classList.add("card", "@low");
this._card.innerHTML = `
-
+
@@ -87,6 +154,7 @@ export class Activity { // FIXME: Add "implements"
this._sourceType = this._card.querySelector(".activity-source-type");
this._source = this._card.querySelector(".activity-source");
this._referrer = this._card.querySelector(".activity-referrer");
+ this._expiryTypeBadge = this._card.querySelector(".activity-expiry-type");
this.update(act);
}
@@ -94,9 +162,41 @@ export class Activity { // FIXME: Add "implements"
update = (act: activity) => {
// FIXME
this._act = act;
+ this.source_type = act.source_type;
+ this.invite_code = act.invite_code;
+ this.value = act.value;
this.type = act.type;
}
asElement = () => { return this._card; };
}
+interface ActivitiesDTO {
+ activities: activity[];
+}
+
+export class activityList {
+ private _activityList: HTMLElement;
+
+ reload = () => {
+ _get("/activity", null, (req: XMLHttpRequest) => {
+ if (req.readyState != 4) return;
+ if (req.status != 200) {
+ window.notifications.customError("loadActivitiesError", window.lang.notif("errorLoadActivities"));
+ return;
+ }
+
+ let resp = req.response as ActivitiesDTO;
+ this._activityList.textContent = ``;
+
+ for (let act of resp.activities) {
+ const activity = new Activity(act);
+ this._activityList.appendChild(activity.asElement());
+ }
+ });
+ }
+
+ constructor() {
+ this._activityList = document.getElementById("activity-card-list");
+ }
+}