Skip to content

Commit

Permalink
Rework typescript to use modules
Browse files Browse the repository at this point in the history
web UI now uses modules, and relies less on bodge to make things work.
Also fixes an issue where invites where "failed to send to xx" appeared
in invite form.
  • Loading branch information
hrfee committed Oct 22, 2020
1 parent 2d6b171 commit 301f502
Show file tree
Hide file tree
Showing 29 changed files with 988 additions and 846 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ data/static/*.css
data/static/*.js
data/static/*.js.map
data/static/ts/
data/static/modules/
!data/static/setup.js
data/config-base.json
data/config-default.ini
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,15 @@ email:

typescript:
$(info Compiling typescript)
npx esbuild ts/* --outdir=data/static --minify
npx esbuild ts/*.ts ts/modules/*.ts --outdir=data/static --minify
-rm -r data/static/ts
-rm -r data/static/typings
-rm data/static/*.map

ts-debug:
-npx tsc -p ts/ --sourceMap
-rm -r data/static/ts
-rm -r data/static/typings
cp -r ts data/static/

swagger:
Expand Down Expand Up @@ -51,3 +54,4 @@ install:
cp -r build $(DESTDIR)/jfa-go

all: configuration sass email version typescript swagger compile copy
debug: configuration sass email version ts-debug swagger compile copy
4 changes: 2 additions & 2 deletions api.go
Original file line number Diff line number Diff line change
Expand Up @@ -626,12 +626,12 @@ func (app *appContext) DeleteProfile(gc *gin.Context) {
// @tags Invites
func (app *appContext) GetInvites(gc *gin.Context) {
app.debug.Println("Invites requested")
current_time := time.Now()
currentTime := time.Now()
app.storage.loadInvites()
app.checkInvites()
var invites []inviteDTO
for code, inv := range app.storage.invites {
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, current_time)
_, _, days, hours, minutes, _ := timeDiff(inv.ValidTill, currentTime)
invite := inviteDTO{
Code: code,
Days: days,
Expand Down
30 changes: 11 additions & 19 deletions data/templates/admin.html
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,11 @@
return "";
}
{{ if .bs5 }}
var bsVersion = 5;
window.bsVersion = 5;
{{ else }}
var bsVersion = 4;
window.bsVersion = 4;
{{ end }}
var cssFile = "{{ .cssFile }}";
window.cssFile = "{{ .cssFile }}";
var css = document.createElement('link');
css.setAttribute('rel', 'stylesheet');
css.setAttribute('type', 'text/css');
Expand Down Expand Up @@ -465,27 +465,19 @@ <h2><a id="settingsTabButton" class="nl nav-link">Settings</a></h2>
<p>{{ .contactMessage }}</p>
</div>
</div>
<script src="common.js"></script>
<script>
var availableProfiles = [];
<script>
window.bs5 = {{ .bs5 }};
window.availableProfiles = [];
{{ if .notifications }}
var notifications_enabled = true;
window.notifications_enabled = true;
{{ else }}
var notifications_enabled = false;
window.notifications_enabled = false;
{{ end }}
</script>
{{ if .bs5 }}
<script src="bs5.js"></script>
{{ else }}
<script src="bs4.js"></script>
{{ end }}
<script src="animation.js"></script>
<script src="accounts.js"></script>
<script src="invites.js"></script>
<script src="admin.js"></script>
<script src="settings.js"></script>
<script src="admin.js" type="module"></script>
<script src="invites.js" type="module"></script>
{{ if .ombiEnabled }}
<script src="ombi.js"></script>
<script src="ombi.js" type="module"></script>
{{ end }}
</body>
</html>
7 changes: 7 additions & 0 deletions data/templates/form-base.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{{ define "form-base" }}
<script>
window.bs5 = {{ .bs5 }};
window.usernameEnabled = {{ .username }};
</script>
<script src="form.js" type="module"></script>
{{ end }}
1 change: 1 addition & 0 deletions data/templates/form-loader.html
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{{ template "form.html" . }}
21 changes: 10 additions & 11 deletions data/templates/form.html
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,11 @@

<!-- Bootstrap CSS -->
<link rel="stylesheet" type="text/css" href="{{ .cssFile }}">
{{ if not .bs5 }}
{{ if not .settings.bs5 }}
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js" integrity="sha384-DfXdz2htPH0lsSSs5nCTpuj/zy4C+OGpamoFVy38MVBnE+IbbVYUew+OrCXaRkfj" crossorigin="anonymous"></script>
{{ end }}
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js" integrity="sha384-Q6E9RHvbIyZFJoft+2mJbHaEWldlvI9IOYy5n3zV9zzTtmI3UksdQRVvoxMfooAo" crossorigin="anonymous"></script>
{{ if .bs5 }}
{{ if .settings.bs5 }}
<script src="https://stackpath.bootstrapcdn.com/bootstrap/5.0.0-alpha1/js/bootstrap.min.js" integrity="sha384-oesi62hOLfzrys4LxRF63OJCXdXDipiYWBnvTl9Y9/TRlw5xlKIEHpNyvvDShgf/" crossorigin="anonymous"></script>
{{ else }}
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js" integrity="sha384-OgVRvuATP1z7JjHLkuOU7Xw704+h835Lr+6QL9UvYjZE3Ipu6Tp75j7Bh/kR0JKI" crossorigin="anonymous"></script>
Expand Down Expand Up @@ -74,9 +74,9 @@ <h1>
<form action="#" method="POST" id="accountForm">
<div class="form-group">
<label for="inputEmail">Email</label>
<input type="email" class="form-control" id="{{ if .username }}inputEmail{{ else }}inputUsername{{ end }}" name="{{ if .username }}email{{ else }}username{{ end }}" placeholder="Email" value="{{ .email }}" required>
<input type="email" class="form-control" id="{{ if .settings.username }}inputEmail{{ else }}inputUsername{{ end }}" name="{{ if .settings.username }}email{{ else }}username{{ end }}" placeholder="Email" value="{{ .email }}" required>
</div>
{{ if .username }}
{{ if .settings.username }}
<div class="form-group">
<label for="inputUsername">Username</label>
<input type="username" class="form-control" id="inputUsername" name="username" placeholder="Username" required>
Expand Down Expand Up @@ -114,10 +114,8 @@ <h1>
</div>
</div>
</div>
<script src="common.js"></script>
<script>
var usernameEnabled = {{ .username }}
var validationStrings = {
<script>
window.validationStrings = {
"length": {
"singular": "Must have at least {n} character",
"plural": "Must have a least {n} characters"
Expand All @@ -138,8 +136,9 @@ <h1>
"singular": "Must have at least {n} special character",
"plural": "Must have at least {n} special characters"
}
}
};
</script>
<script src="form.js"></script>
{{ template "form-base" .settings }}
</body>
</html>
</html>

6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
},
"homepage": "https://github.com/hrfee/jellyfin-accounts#readme",
"dependencies": {
"@types/jquery": "^3.5.1",
"@types/jquery": "^3.5.3",
"autoprefixer": "^9.8.5",
"bootstrap": "^5.0.0-alpha1",
"bootstrap4": "npm:bootstrap@^4.5.0",
Expand Down
2 changes: 1 addition & 1 deletion pwval.go
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ func (vd *Validator) validate(password string) map[string]bool {
} else if unicode.IsLower(c) {
count["lowercase"] += 1
} else if unicode.IsNumber(c) {
count["numbers"] += 1
count["number"] += 1
} else {
for _, s := range vd.specialChars {
if c == s {
Expand Down
130 changes: 20 additions & 110 deletions ts/accounts.ts
Original file line number Diff line number Diff line change
@@ -1,25 +1,17 @@
const checkCheckboxes = (): void => {
const defaultsButton = document.getElementById('accountsTabSetDefaults');
const deleteButton = document.getElementById('accountsTabDelete');
const checkboxes: NodeListOf<HTMLInputElement> = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]:checked');
let checked = checkboxes.length;
if (checked == 0) {
Unfocus(defaultsButton);
Unfocus(deleteButton);
} else {
Focus(defaultsButton);
Focus(deleteButton);
if (checked == 1) {
deleteButton.textContent = 'Delete User';
} else {
deleteButton.textContent = 'Delete Users';
}
}
import { checkCheckboxes, populateUsers, populateRadios } from "./modules/accounts.js";
import { _post, _get, _delete, rmAttr, addAttr } from "./modules/common.js";
import { populateProfiles } from "./modules/settings.js";
import { Focus, Unfocus, createEl, storeDefaults } from "./modules/admin.js";

interface aWindow extends Window {
changeEmail(icon: HTMLElement, id: string): void;
}

declare var window: aWindow;

const validateEmail = (email: string): boolean => /\S+@\S+\.\S+/.test(email);

function changeEmail(icon: HTMLElement, id: string): void {
window.changeEmail = (icon: HTMLElement, id: string): void => {
const iconContent = icon.outerHTML;
icon.setAttribute('class', '');
const entry = icon.nextElementSibling as HTMLInputElement;
Expand Down Expand Up @@ -79,83 +71,7 @@ function changeEmail(icon: HTMLElement, id: string): void {
icon.parentNode.appendChild(cross);
};

var jfUsers: Array<Object>;

function populateUsers(): void {
const acList = document.getElementById('accountsList');
acList.innerHTML = `
<div class="d-flex align-items-center">
<strong>Getting Users...</strong>
<div class="spinner-border ml-auto" role="status" aria-hidden="true"></div>
</div>
`;
Unfocus(acList.parentNode.querySelector('thead'));
const accountsList = document.createElement('tbody');
accountsList.id = 'accountsList';
const generateEmail = (id: string, name: string, email: string): string => {
let entry: HTMLDivElement = document.createElement('div');
entry.id = 'email_' + id;
let emailValue: string = email;
if (emailValue == undefined) {
emailValue = "";
}
entry.innerHTML = `
<i class="fa fa-edit d-inline-block icon-button" style="margin-right: 2%;" onclick="changeEmail(this, '${id}')"></i>
<input type="email" class="form-control-plaintext form-control-sm text-muted d-inline-block addressText" id="address_${id}" style="width: auto;" value="${emailValue}" readonly>
`;
return entry.outerHTML;
};
const template = (id: string, username: string, email: string, lastActive: string, admin: boolean): string => {
let isAdmin = "No";
if (admin) {
isAdmin = "Yes";
}
let fci = "form-check-input";
if (bsVersion != 5) {
fci = "";
}
return `
<td nowrap="nowrap" class="align-middle" scope="row"><input class="${fci}" type="checkbox" value="" id="select_${id}" onclick="checkCheckboxes();"></td>
<td nowrap="nowrap" class="align-middle">${username}</td>
<td nowrap="nowrap" class="align-middle">${generateEmail(id, name, email)}</td>
<td nowrap="nowrap" class="align-middle">${lastActive}</td>
<td nowrap="nowrap" class="align-middle">${isAdmin}</td>
`;
};

_get("/users", null, function (): void {
if (this.readyState == 4 && this.status == 200) {
jfUsers = this.response['users'];
for (const user of jfUsers) {
let tr = document.createElement('tr');
tr.innerHTML = template(user['id'], user['name'], user['email'], user['last_active'], user['admin']);
accountsList.appendChild(tr);
}
Focus(acList.parentNode.querySelector('thead'));
acList.replaceWith(accountsList);
}
});
}

function populateRadios(): void {
const radioList = document.getElementById('defaultUserRadios');
radioList.textContent = '';
let first = true;
for (const i in jfUsers) {
const user = jfUsers[i];
const radio = document.createElement('div');
radio.classList.add('form-check');
let checked = '';
if (first) {
checked = 'checked';
first = false;
}
radio.innerHTML = `
<input class="form-check-input" type="radio" name="defaultRadios" id="default_${user['id']}" ${checked}>
<label class="form-check-label" for="default_${user['id']}">${user['name']}</label>`;
radioList.appendChild(radio);
}
}
console.log("bruh");

(<HTMLInputElement>document.getElementById('selectAll')).onclick = function (): void {
const checkboxes: NodeListOf<HTMLInputElement> = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]');
Expand Down Expand Up @@ -217,26 +133,26 @@ function populateRadios(): void {
}
setTimeout((): void => {
Unfocus(deleteButton);
deleteModal.hide();
window.Modals.delete.hide();
}, 4000);
} else {
Unfocus(deleteButton);
deleteModal.hide()
window.Modals.delete.hide()
}
populateUsers();
checkCheckboxes();
}
});
};
deleteModal.show();
window.Modals.delete.show();
};

(<HTMLInputElement>document.getElementById('selectAll')).checked = false;

(<HTMLButtonElement>document.getElementById('accountsTabSetDefaults')).onclick = function (): void {
const checkboxes: NodeListOf<HTMLInputElement> = document.getElementById('accountsList').querySelectorAll('input[type=checkbox]:checked');
let userIDs: Array<string> = new Array(checkboxes.length);
for (let i = 0; i < checkboxes.length; i++){
for (let i = 0; i < checkboxes.length; i++){
userIDs[i] = checkboxes[i].id.replace("select_", "");
}
if (userIDs.length == 0) {
Expand All @@ -250,9 +166,9 @@ function populateRadios(): void {
populateProfiles(true);
const profileSelect = document.getElementById('profileSelect') as HTMLSelectElement;
profileSelect.textContent = '';
for (let i = 0; i < availableProfiles.length; i++) {
for (let i = 0; i < window.availableProfiles.length; i++) {
profileSelect.innerHTML += `
<option value="${availableProfiles[i]}" ${(i == 0) ? "selected" : ""}>${availableProfiles[i]}</option>
<option value="${window.availableProfiles[i]}" ${(i == 0) ? "selected" : ""}>${window.availableProfiles[i]}</option>
`;
}
document.getElementById('defaultsTitle').textContent = `Apply settings to ${userIDs.length} ${userString}`;
Expand All @@ -266,7 +182,7 @@ function populateRadios(): void {
Unfocus(document.getElementById('defaultUserRadiosBox'));
Unfocus(document.getElementById('newProfileBox'));
document.getElementById('storeDefaults').onclick = (): void => storeDefaults(userIDs);
userDefaultsModal.show();
window.Modals.userDefaults.show();
};

(<HTMLSelectElement>document.getElementById('defaultsSource')).addEventListener('change', function (): void {
Expand Down Expand Up @@ -311,7 +227,7 @@ function populateRadios(): void {
rmAttr(button, 'btn-success');
addAttr(button, 'btn-primary');
button.textContent = ogText;
newUserModal.hide();
window.Modals.newUser.hide();
}, 1000);
populateUsers();
} else {
Expand All @@ -338,11 +254,5 @@ function populateRadios(): void {
if (document.getElementById('newUserName') != null) {
(<HTMLInputElement>document.getElementById('newUserName')).value = '';
}
newUserModal.show();
window.Modals.newUser.show();
};






0 comments on commit 301f502

Please sign in to comment.