1
0
mirror of https://github.com/hrfee/jfa-go.git synced 2025-04-11 05:32:52 +00:00

Compare commits

..

No commits in common. "a1612949bf59b69e14ac852a64d722548ffbfbee" and "6347495b5b69150dae1b6dc46ce38486b7e32bd4" have entirely different histories.

7 changed files with 120 additions and 169 deletions

View File

@ -1,5 +1,6 @@
.PHONY: configuration email typescript swagger copy compile compress inline-css variants-html install clean npm config-description config-default precompile .PHONY: configuration email typescript swagger copy compile compress inline-css variants-html install clean npm config-description config-default precompile
.DEFAULT_GOAL := all
all: compile
GOESBUILD ?= off GOESBUILD ?= off
ifeq ($(GOESBUILD), on) ifeq ($(GOESBUILD), on)
@ -34,11 +35,9 @@ TAGS := -tags "
ifeq ($(INTERNAL), on) ifeq ($(INTERNAL), on)
DATA := data DATA := data
COMPDEPS := $(BUILDDEPS)
else else
DATA := build/data DATA := build/data
TAGS := $(TAGS) external TAGS := $(TAGS) external
COMPDEPS :=
endif endif
ifeq ($(TRAY), on) ifeq ($(TRAY), on)
@ -142,6 +141,7 @@ $(TYPESCRIPT_TARGET): $(TYPESCRIPT_FULLSRC) ts/tsconfig.json
scripts/dark-variant.sh tempts/modules scripts/dark-variant.sh tempts/modules
$(info compiling typescript) $(info compiling typescript)
$(foreach tempsrc,$(TYPESCRIPT_TEMPSRC),$(ESBUILD) --target=es6 --bundle $(tempsrc) $(SOURCEMAP) --outfile=$(patsubst %.ts,%.js,$(subst tempts/,./$(DATA)/web/js/,$(tempsrc))) $(MINIFY);) $(foreach tempsrc,$(TYPESCRIPT_TEMPSRC),$(ESBUILD) --target=es6 --bundle $(tempsrc) $(SOURCEMAP) --outfile=$(patsubst %.ts,%.js,$(subst tempts/,./$(DATA)/web/js/,$(tempsrc))) $(MINIFY);)
mv $(DATA)/web/js/crash.js $(DATA)/
$(COPYTS) $(COPYTS)
SWAGGER_SRC = $(wildcard api*.go) $(wildcard *auth.go) views.go SWAGGER_SRC = $(wildcard api*.go) $(wildcard *auth.go) views.go
@ -206,27 +206,21 @@ $(COPY_TARGET): $(INLINE_TARGET) $(STATIC_SRC) $(LANG_SRC)
cp -r lang $(DATA)/ cp -r lang $(DATA)/
cp LICENSE $(DATA)/ cp LICENSE $(DATA)/
BUILDDEPS := $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) precompile: $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET)
precompile: $(BUILDDEPS)
COMPDEPS =
ifeq ($(INTERNAL), on)
COMPDEPS = $(BUILDDEPS)
endif
GO_SRC = $(shell find ./ -name "*.go") GO_SRC = $(shell find ./ -name "*.go")
GO_TARGET = build/jfa-go GO_TARGET = build/jfa-go
$(GO_TARGET): $(COMPDEPS) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum $(GO_TARGET): $(DATA) $(CONFIG_DEFAULT) $(EMAIL_TARGET) $(COPY_TARGET) $(SWAGGER_TARGET) $(GO_SRC) go.mod go.sum
$(info Downloading deps) $(info Downloading deps)
$(GOBINARY) mod download $(GOBINARY) mod download
$(info Building) $(info Building)
mkdir -p build mkdir -p build
$(GOBINARY) build $(RACEDETECTOR) -ldflags="$(LDFLAGS)" $(TAGS) -o $(GO_TARGET) $(GOBINARY) build $(RACEDETECTOR) -ldflags="$(LDFLAGS)" $(TAGS) -o $(GO_TARGET)
all: $(BUILDDEPS) $(GO_TARGET) compile: $(GO_TARGET)
compress: compress:
upx --lzma $(GO_TARGET) upx --lzma build/jfa-go
install: install:
cp -r build $(DESTDIR)/jfa-go cp -r build $(DESTDIR)/jfa-go

View File

@ -962,7 +962,7 @@ func (app *appContext) ModifyLabels(gc *gin.Context) {
emailStore = oldEmail emailStore = oldEmail
} }
emailStore.Label = label emailStore.Label = label
app.debug.Printf(lm.UserLabelAdjusted, id, label) app.debug.Println(lm.UserLabelAdjusted, id, label)
app.storage.SetEmailsKey(id, emailStore) app.storage.SetEmailsKey(id, emailStore)
} }
} }

View File

@ -27,18 +27,16 @@
<body class="max-w-full overflow-x-hidden section"> <body class="max-w-full overflow-x-hidden section">
{{ template "login-modal.html" . }} {{ template "login-modal.html" . }}
<div id="modal-add-user" class="modal"> <div id="modal-add-user" class="modal">
<form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3 flex flex-col gap-2" id="form-add-user" href=""> <form class="card relative mx-auto my-[10%] w-11/12 sm:w-4/5 lg:w-1/3" id="form-add-user" href="">
<span class="heading">{{ .strings.newUser }} <span class="modal-close">&times;</span></span> <span class="heading">{{ .strings.newUser }} <span class="modal-close">&times;</span></span>
<input type="text" class="field input ~neutral @high" placeholder="{{ .strings.username }}" id="add-user-user"> <input type="text" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.username }}" id="add-user-user">
<input type="email" class="field input ~neutral @high" placeholder="{{ .strings.emailAddress }}"> <input type="email" class="field input ~neutral @high mt-4 mb-2" placeholder="{{ .strings.emailAddress }}">
<input type="password" class="field input ~neutral @high" placeholder="{{ .strings.password }}" id="add-user-password"> <input type="password" class="field input ~neutral @high mb-4" placeholder="{{ .strings.password }}" id="add-user-password">
<label class="label flex flex-col gap-2"> <label class="label supra">{{ .strings.profile }}</label>
<span class="supra">{{ .strings.profile }}</span> <div class="select ~neutral @low mb-2 mt-4">
<div class="select ~neutral @low"> <select id="add-user-profile">
<select id="add-user-profile"> </select>
</select> </div>
</div>
</label>
<label> <label>
<input type="submit" class="unfocused"> <input type="submit" class="unfocused">
<span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span> <span class="button ~urge @low full-width center supra submit">{{ .strings.create }}</span>
@ -802,7 +800,7 @@
<span class="button ~critical @low center " id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span> <span class="button ~critical @low center " id="accounts-delete-user">{{ .quantityStrings.deleteUser.Singular }}</span>
</div> </div>
<div class="card @low accounts-header table-responsive mt-2"> <div class="card @low accounts-header table-responsive mt-2">
<table class="table text-base leading-5"> <table class="table text-base leading-4">
<thead> <thead>
<tr> <tr>
<th><input type="checkbox" value="" id="accounts-select-all"></th> <th><input type="checkbox" value="" id="accounts-select-all"></th>

View File

@ -1,7 +1,7 @@
<!doctype html> <!doctype html>
<html lang="en"> <html lang="en">
<head> <head>
<link inline rel="stylesheet" type="text/css" href="web/css/v3bundle.css"> <link inline rel="stylesheet" type="text/css" href="bundle.css">
{{ template "header.html" . }} {{ template "header.html" . }}
<title>Crash report</title> <title>Crash report</title>
</head> </head>
@ -40,6 +40,6 @@
</section> </section>
</div> </div>
</div> </div>
<script inline src="web/js/crash.js"></script> <script inline src="crash.js"></script>
</body> </body>
</html> </html>

View File

@ -4,8 +4,6 @@ import { Marked } from "@ts-stack/markdown";
import { stripMarkdown } from "../modules/stripmd.js"; import { stripMarkdown } from "../modules/stripmd.js";
import { DiscordUser, newDiscordSearch } from "../modules/discord.js"; import { DiscordUser, newDiscordSearch } from "../modules/discord.js";
import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modules/search.js"; import { Search, SearchConfiguration, QueryType, SearchableItem } from "../modules/search.js";
import { HiddenInputField } from "./ui.js";
const dateParser = require("any-date-parser"); const dateParser = require("any-date-parser");
interface User { interface User {
@ -50,9 +48,9 @@ class user implements User, SearchableItem {
private _admin: HTMLSpanElement; private _admin: HTMLSpanElement;
private _disabled: HTMLSpanElement; private _disabled: HTMLSpanElement;
private _email: HTMLInputElement; private _email: HTMLInputElement;
private _emailEditor: HiddenInputField;
private _notifyEmail: boolean; private _notifyEmail: boolean;
private _emailAddress: string; private _emailAddress: string;
private _emailEditButton: HTMLElement;
private _telegram: HTMLTableDataCellElement; private _telegram: HTMLTableDataCellElement;
private _telegramUsername: string; private _telegramUsername: string;
private _notifyTelegram: boolean; private _notifyTelegram: boolean;
@ -69,8 +67,8 @@ class user implements User, SearchableItem {
private _lastActiveUnix: number; private _lastActiveUnix: number;
private _notifyDropdown: HTMLDivElement; private _notifyDropdown: HTMLDivElement;
private _label: HTMLInputElement; private _label: HTMLInputElement;
private _labelEditor: HiddenInputField;
private _userLabel: string; private _userLabel: string;
private _labelEditButton: HTMLElement;
private _accounts_admin: HTMLInputElement private _accounts_admin: HTMLInputElement
private _selected: boolean; private _selected: boolean;
private _referralsEnabled: boolean; private _referralsEnabled: boolean;
@ -112,10 +110,10 @@ class user implements User, SearchableItem {
get admin(): boolean { return this._admin.classList.contains("chip"); } get admin(): boolean { return this._admin.classList.contains("chip"); }
set admin(state: boolean) { set admin(state: boolean) {
if (state) { if (state) {
this._admin.classList.remove("hidden") this._admin.classList.add("chip", "~info", "ml-4");
this._admin.textContent = window.lang.strings("admin"); this._admin.textContent = window.lang.strings("admin");
} else { } else {
this._admin.classList.add("hidden") this._admin.classList.remove("chip", "~info", "ml-4");
this._admin.textContent = ""; this._admin.textContent = "";
} }
} }
@ -135,10 +133,10 @@ class user implements User, SearchableItem {
get disabled(): boolean { return this._disabled.classList.contains("chip"); } get disabled(): boolean { return this._disabled.classList.contains("chip"); }
set disabled(state: boolean) { set disabled(state: boolean) {
if (state) { if (state) {
this._disabled.classList.remove("hidden") this._disabled.classList.add("chip", "~warning", "ml-4");
this._disabled.textContent = window.lang.strings("disabled"); this._disabled.textContent = window.lang.strings("disabled");
} else { } else {
this._disabled.classList.add("hidden") this._disabled.classList.remove("chip", "~warning", "ml-4");
this._disabled.textContent = ""; this._disabled.textContent = "";
} }
} }
@ -146,7 +144,12 @@ class user implements User, SearchableItem {
get email(): string { return this._emailAddress; } get email(): string { return this._emailAddress; }
set email(value: string) { set email(value: string) {
this._emailAddress = value; this._emailAddress = value;
this._emailEditor.value = value; const input = this._email.querySelector("input");
if (input) {
input.value = value;
} else {
this._email.textContent = value;
}
const lastNotifyMethod = this.lastNotifyMethod() == "email"; const lastNotifyMethod = this.lastNotifyMethod() == "email";
if (!value) { if (!value) {
this._notifyDropdown.querySelector(".accounts-area-email").classList.add("unfocused"); this._notifyDropdown.querySelector(".accounts-area-email").classList.add("unfocused");
@ -185,7 +188,7 @@ class user implements User, SearchableItem {
if (!telegram && !discord && !matrix && !email) return; if (!telegram && !discord && !matrix && !email) return;
let innerHTML = ` let innerHTML = `
<i class="icon ri-settings-2-line ml-2 dropdown-button"></i> <i class="icon ri-settings-2-line ml-2 dropdown-button"></i>
<div class="dropdown manual over-top"> <div class="dropdown manual">
<div class="dropdown-display lg"> <div class="dropdown-display lg">
<div class="card ~neutral @low"> <div class="card ~neutral @low">
<div class="supra sm mb-2">${window.lang.strings("contactThrough")}</div> <div class="supra sm mb-2">${window.lang.strings("contactThrough")}</div>
@ -465,7 +468,14 @@ class user implements User, SearchableItem {
get label(): string { return this._userLabel; } get label(): string { return this._userLabel; }
set label(l: string) { set label(l: string) {
this._userLabel = l ? l : ""; this._userLabel = l ? l : "";
this._labelEditor.value = l ? l : ""; this._label.innerHTML = l ? l : "";
this._labelEditButton.classList.add("ri-edit-line");
this._labelEditButton.classList.remove("ri-check-line");
if (!l) {
this._label.classList.remove("chip", "~gray");
} else {
this._label.classList.add("chip", "~gray", "mr-2");
}
} }
matchesSearch = (query: string): boolean => { matchesSearch = (query: string): boolean => {
@ -486,17 +496,9 @@ class user implements User, SearchableItem {
constructor(user: User) { constructor(user: User) {
this._row = document.createElement("tr") as HTMLTableRowElement; this._row = document.createElement("tr") as HTMLTableRowElement;
this._row.classList.add("border-b", "border-dashed", "dark:border-dotted", "dark:border-stone-700");
let innerHTML = ` let innerHTML = `
<td><input type="checkbox" class="accounts-select-user" value=""></td> <td><input type="checkbox" class="accounts-select-user" value=""></td>
<td><div class="flex flex-row gap-2 items-center"> <td><div class="table-inline"><span class="accounts-username py-2 mr-2"></span><span class="accounts-label-container ml-2"></span> <i class="icon ri-edit-line accounts-label-edit"></i> <span class="accounts-admin"></span> <span class="accounts-disabled"></span></span></div></td>
<span class="accounts-username"></span>
<div class="flex flex-row gap-2 items-baseline">
<span class="accounts-label-container" title="${window.lang.strings("label")}"></span>
<span class="accounts-admin chip ~info hidden"></span>
<span class="accounts-disabled chip ~warning hidden"></span></span>
</div>
</div></td>
`; `;
if (window.jellyfinLogin) { if (window.jellyfinLogin) {
innerHTML += ` innerHTML += `
@ -504,9 +506,7 @@ class user implements User, SearchableItem {
`; `;
} }
innerHTML += ` innerHTML += `
<td><div class="flex flex-row gap-2 items-baseline"> <td><div class="table-inline"><i class="icon ri-edit-line accounts-email-edit"></i><span class="accounts-email-container ml-2"></span></div></td>
<span class="accounts-email-container" title="${window.lang.strings("emailAddress")}"></span>
</div></td>
`; `;
if (window.telegramEnabled) { if (window.telegramEnabled) {
innerHTML += ` innerHTML += `
@ -534,33 +534,21 @@ class user implements User, SearchableItem {
`; `;
this._row.innerHTML = innerHTML; this._row.innerHTML = innerHTML;
const emailEditor = `<input type="email" class="input ~neutral @low stealth-input">`; const emailEditor = `<input type="email" class="input ~neutral @low stealth-input">`;
const labelEditor = `<input type="text" class="field ~neutral @low stealth-input">`;
this._check = this._row.querySelector("input[type=checkbox].accounts-select-user") as HTMLInputElement; this._check = this._row.querySelector("input[type=checkbox].accounts-select-user") as HTMLInputElement;
this._accounts_admin = this._row.querySelector("input[type=checkbox].accounts-access-jfa") as HTMLInputElement; this._accounts_admin = this._row.querySelector("input[type=checkbox].accounts-access-jfa") as HTMLInputElement;
this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement; this._username = this._row.querySelector(".accounts-username") as HTMLSpanElement;
this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement; this._admin = this._row.querySelector(".accounts-admin") as HTMLSpanElement;
this._disabled = this._row.querySelector(".accounts-disabled") as HTMLSpanElement; this._disabled = this._row.querySelector(".accounts-disabled") as HTMLSpanElement;
this._email = this._row.querySelector(".accounts-email-container") as HTMLInputElement; this._email = this._row.querySelector(".accounts-email-container") as HTMLInputElement;
this._emailEditor = new HiddenInputField({ this._emailEditButton = this._row.querySelector(".accounts-email-edit") as HTMLElement;
container: this._email,
onSet: this._updateEmail,
customContainerHTML: `<span class="hidden-input-content"></span>`,
buttonOnLeft: true,
clickAwayShouldSave: false,
});
this._telegram = this._row.querySelector(".accounts-telegram") as HTMLTableDataCellElement; this._telegram = this._row.querySelector(".accounts-telegram") as HTMLTableDataCellElement;
this._discord = this._row.querySelector(".accounts-discord") as HTMLTableDataCellElement; this._discord = this._row.querySelector(".accounts-discord") as HTMLTableDataCellElement;
this._matrix = this._row.querySelector(".accounts-matrix") as HTMLTableDataCellElement; this._matrix = this._row.querySelector(".accounts-matrix") as HTMLTableDataCellElement;
this._expiry = this._row.querySelector(".accounts-expiry") as HTMLTableDataCellElement; this._expiry = this._row.querySelector(".accounts-expiry") as HTMLTableDataCellElement;
this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement; this._lastActive = this._row.querySelector(".accounts-last-active") as HTMLTableDataCellElement;
this._label = this._row.querySelector(".accounts-label-container") as HTMLInputElement; this._label = this._row.querySelector(".accounts-label-container") as HTMLInputElement;
this._labelEditor = new HiddenInputField({ this._labelEditButton = this._row.querySelector(".accounts-label-edit") as HTMLElement;
container: this._label,
onSet: this._updateLabel,
customContainerHTML: `<span class="chip ~gray hidden-input-content"></span>`,
buttonOnLeft: true,
clickAwayShouldSave: false,
});
this._check.onchange = () => { this.selected = this._check.checked; } this._check.onchange = () => { this.selected = this._check.checked; }
if (window.jellyfinLogin) { if (window.jellyfinLogin) {
@ -585,6 +573,66 @@ class user implements User, SearchableItem {
this._notifyDropdown = this._constructDropdown(); this._notifyDropdown = this._constructDropdown();
const toggleEmailInput = () => {
if (this._emailEditButton.classList.contains("ri-edit-line")) {
this._email.innerHTML = emailEditor;
this._email.querySelector("input").value = this._emailAddress;
this._email.classList.remove("ml-2");
} else {
this._email.textContent = this._emailAddress;
this._email.classList.add("ml-2");
}
this._emailEditButton.classList.toggle("ri-check-line");
this._emailEditButton.classList.toggle("ri-edit-line");
};
const emailClickListener = (event: Event) => {
if (!(event.target instanceof HTMLElement && (this._email.contains(event.target) || this._emailEditButton.contains(event.target)))) {
toggleEmailInput();
this.email = this.email;
document.removeEventListener("click", emailClickListener);
}
};
this._emailEditButton.onclick = () => {
if (this._emailEditButton.classList.contains("ri-edit-line")) {
document.addEventListener('click', emailClickListener);
} else {
this._updateEmail();
document.removeEventListener('click', emailClickListener);
}
toggleEmailInput();
};
const toggleLabelInput = () => {
if (this._labelEditButton.classList.contains("ri-edit-line")) {
this._label.innerHTML = labelEditor;
const input = this._label.querySelector("input");
input.value = this._userLabel;
input.placeholder = window.lang.strings("label");
this._label.classList.remove("ml-2");
this._labelEditButton.classList.add("ri-check-line");
this._labelEditButton.classList.remove("ri-edit-line");
} else {
this._updateLabel();
this._email.classList.add("ml-2");
}
};
const labelClickListener = (event: Event) => {
if (!(event.target instanceof HTMLElement && (this._label.contains(event.target) || this._labelEditButton.contains(event.target)))) {
toggleLabelInput();
document.removeEventListener("click", labelClickListener);
}
};
this._labelEditButton.onclick = () => {
if (this._labelEditButton.classList.contains("ri-edit-line")) {
document.addEventListener('click', labelClickListener);
} else {
document.removeEventListener('click', labelClickListener);
}
toggleLabelInput();
};
this.update(user); this.update(user);
document.addEventListener("timefmt-change", () => { document.addEventListener("timefmt-change", () => {
@ -594,12 +642,14 @@ class user implements User, SearchableItem {
} }
private _updateLabel = () => { private _updateLabel = () => {
let oldLabel = this.label;
this.label = this._label.querySelector("input").value;
let send = {}; let send = {};
send[this.id] = this._labelEditor.value; send[this.id] = this.label;
_post("/users/labels", send, (req: XMLHttpRequest) => { _post("/users/labels", send, (req: XMLHttpRequest) => {
if (req.readyState == 4) { if (req.readyState == 4) {
if (req.status != 204) { if (req.status != 204) {
this.label = this._labelEditor.previous; this.label = oldLabel;
window.notifications.customError("labelChanged", window.lang.notif("errorUnknown")); window.notifications.customError("labelChanged", window.lang.notif("errorUnknown"));
} }
} }
@ -607,14 +657,16 @@ class user implements User, SearchableItem {
}; };
private _updateEmail = () => { private _updateEmail = () => {
let oldEmail = this.email;
this.email = this._email.querySelector("input").value;
let send = {}; let send = {};
send[this.id] = this._emailEditor.value; send[this.id] = this.email;
_post("/users/emails", send, (req: XMLHttpRequest) => { _post("/users/emails", send, (req: XMLHttpRequest) => {
if (req.readyState == 4) { if (req.readyState == 4) {
if (req.status == 200) { if (req.status == 200) {
window.notifications.customSuccess("emailChanged", window.lang.var("notifications", "changedEmailAddress", `"${this.name}"`)); window.notifications.customSuccess("emailChanged", window.lang.var("notifications", "changedEmailAddress", `"${this.name}"`));
} else { } else {
this.email = this._emailEditor.previous; this.email = oldEmail;
window.notifications.customError("emailChanged", window.lang.var("notifications", "errorChangedEmailAddress", `"${this.name}"`)); window.notifications.customError("emailChanged", window.lang.var("notifications", "errorChangedEmailAddress", `"${this.name}"`));
} }
} }

View File

@ -46,16 +46,14 @@ export class Activity implements activity, SearchableItem {
private _delete: HTMLElement; private _delete: HTMLElement;
private _ip: HTMLElement; private _ip: HTMLElement;
private _act: activity; private _act: activity;
/* private _urlBase: string = ((): string => { private _urlBase: string = ((): string => {
let link = window.location.href; let link = window.location.href;
for (let split of ["#", "?", "/activity"]) { for (let split of ["#", "?", "/activity"]) {
link = link.split(split)[0]; link = link.split(split)[0];
} }
if (link.slice(-1) != "/") { link += "/"; } if (link.slice(-1) != "/") { link += "/"; }
// FIXME: I should probably just be using window.URLBase, but incase thats not right, i'll put this warning here
if (link != window.URLBase) console.error(`URL Bases don't match: "${link}" != "${window.URLBase}"`);
return link; return link;
})(); */ })();
_genUserText = (): string => { _genUserText = (): string => {
return `<span class="font-medium">${this._act.username || this._act.user_id.substring(0, 5)}</span>`; return `<span class="font-medium">${this._act.username || this._act.user_id.substring(0, 5)}</span>`;
@ -66,17 +64,17 @@ export class Activity implements activity, SearchableItem {
} }
_genUserLink = (): string => { _genUserLink = (): string => {
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.URLBase}/accounts?user=${this._act.user_id}">${this._genUserText()}</a>`; return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${this._urlBase}accounts?user=${this._act.user_id}">${this._genUserText()}</a>`;
} }
_genSrcUserLink = (): string => { _genSrcUserLink = (): string => {
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${window.URLBase}/accounts?user=${this._act.source}">${this._genSrcUserText()}</a>`; return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-user" data-id="${this._act.user_id}" href="${this._urlBase}accounts?user=${this._act.source}">${this._genSrcUserText()}</a>`;
} }
private _renderInvText = (): string => { return `<span class="font-medium font-mono">${this.value || this.invite_code || "???"}</span>`; } private _renderInvText = (): string => { return `<span class="font-medium font-mono">${this.value || this.invite_code || "???"}</span>`; }
private _genInvLink = (): string => { private _genInvLink = (): string => {
return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" href="${window.URLBase}/?invite=${this.invite_code}">${this._renderInvText()}</a>`; return `<a role="link" tabindex="0" class="hover:underline cursor-pointer activity-pseudo-link-invite" data-id="${this.invite_code}" href="${this._urlBase}?invite=${this.invite_code}">${this._renderInvText()}</a>`;
} }

View File

@ -1,91 +0,0 @@
export interface HiddenInputConf {
container: HTMLElement;
onSet: () => void;
buttonOnLeft?: boolean;
customContainerHTML?: string;
input?: string;
clickAwayShouldSave?: boolean;
};
export class HiddenInputField {
public static editClass = "ri-edit-line";
public static saveClass = "ri-check-line";
private _c: HiddenInputConf;
private _input: HTMLInputElement;
private _content: HTMLElement
private _toggle: HTMLElement;
previous: string;
constructor(c: HiddenInputConf) {
this._c = c;
if (!(this._c.customContainerHTML)) {
this._c.customContainerHTML = `<span class="hidden-input-content"></span>`;
}
if (!(this._c.input)) {
this._c.input = `<input type="text" class="field ~neutral @low max-w-24 hidden-input-input">`;
}
this._c.container.innerHTML = `
<div class="flex flex-row gap-2 items-baseline">
${this._c.buttonOnLeft ? "" : this._c.input}
${this._c.buttonOnLeft ? "" : this._c.customContainerHTML}
<i class="hidden-input-toggle"></i>
${this._c.buttonOnLeft ? this._c.input : ""}
${this._c.buttonOnLeft ? this._c.customContainerHTML : ""}
</div>
`;
this._input = this._c.container.querySelector(".hidden-input-input") as HTMLInputElement;
this._input.classList.add("py-0.5", "px-1", "hidden");
this._toggle = this._c.container.querySelector(".hidden-input-toggle");
this._content = this._c.container.querySelector(".hidden-input-content");
this._toggle.onclick = () => {
this.editing = !this.editing;
};
this.setEditing(false, true);
}
// FIXME: not working
outerClickListener = ((event: Event) => {
if (!(event.target instanceof HTMLElement && (this._input.contains(event.target) || this._toggle.contains(event.target)))) {
this.toggle(!(this._c.clickAwayShouldSave));
}
}).bind(this);
get editing(): boolean { return this._toggle.classList.contains(HiddenInputField.saveClass); }
set editing(e: boolean) { this.setEditing(e); }
setEditing(e: boolean, noEvent: boolean = false, noSave: boolean = false) {
if (e) {
document.addEventListener("click", this.outerClickListener);
this.previous = this.value;
this._input.value = this.value;
this._toggle.classList.add(HiddenInputField.saveClass);
this._toggle.classList.remove(HiddenInputField.editClass);
this._input.classList.remove("hidden");
this._content.classList.add("hidden");
} else {
document.removeEventListener("click", this.outerClickListener);
this.value = noSave ? this.previous : this._input.value;
this._toggle.classList.add(HiddenInputField.editClass);
this._toggle.classList.remove(HiddenInputField.saveClass);
// done by set value()
// this._content.classList.remove("hidden");
this._input.classList.add("hidden");
if (this.value != this.previous && !noEvent && !noSave) this._c.onSet()
}
}
get value(): string { return this._content.textContent; };
set value(v: string) {
this._content.textContent = v;
this._input.value = v;
if (!v) this._content.classList.add("hidden");
else this._content.classList.remove("hidden");
}
toggle(noSave: boolean = false) { this.setEditing(!this.editing, false, noSave); }
}