Finished up implementing channel-based user bans.
This commit is contained in:
parent
ef79e9941c
commit
96953979a2
19 changed files with 518 additions and 202 deletions
|
|
@ -21,8 +21,9 @@ class canopyAdminUtils{
|
|||
|
||||
//Statics
|
||||
static banUserPopup = class{
|
||||
constructor(target){
|
||||
constructor(target, cb){
|
||||
this.target = target;
|
||||
this.cb = cb;
|
||||
this.popup = new canopyUXUtils.popup("userBan", true, this.asyncConstruction.bind(this));
|
||||
}
|
||||
|
||||
|
|
@ -61,12 +62,12 @@ class canopyAdminUtils{
|
|||
this.popup.closePopup();
|
||||
|
||||
//Submit the user ban based on input
|
||||
const bans = await adminUtil.banUser(this.target, this.permBan.checked, this.expiration.value);
|
||||
const data = await adminUtil.banUser(this.target, this.permBan.checked, this.expiration.value);
|
||||
|
||||
//For some reason comparing this against undefined or null wasnt working in and of itself...
|
||||
if(typeof userBanList != "undefined" && bans != null){
|
||||
if(data != null){
|
||||
//Why add an extra get request when we already have the data? :P
|
||||
await userBanList.renderBanList(bans);
|
||||
await this.cb(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -197,8 +198,7 @@ class adminUserList{
|
|||
|
||||
banPopup(event){
|
||||
const user = event.target.id.replace("admin-user-list-ban-icon-","");
|
||||
|
||||
new canopyAdminUtils.banUserPopup(user);
|
||||
new canopyAdminUtils.banUserPopup(user, userBanList.renderBanList.bind(userBanList));
|
||||
}
|
||||
|
||||
updateSelect(update, select){
|
||||
|
|
@ -283,14 +283,10 @@ class adminUserBanList{
|
|||
|
||||
renderBanList(banList){
|
||||
this.clearBanList();
|
||||
console.log(banList);
|
||||
banList.forEach((ban) => {
|
||||
//Calculate expiration date and expiration days
|
||||
const expirationDate = new Date(ban.expirationDate);
|
||||
const expirationDays = ((expirationDate - new Date()) / (1000 * 60 * 60 * 24)).toFixed(1);
|
||||
var expirationDateString = `${expirationDate.toDateString()} (${expirationDays} day(s) left)`;
|
||||
var banActionString = ban.permanent ? "Account Deletion" : "Un-Ban";
|
||||
console.log(ban);
|
||||
var expirationDateString = `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
|
||||
var banActionString = ban.permanent ? "Nuke<br>Accounts" : "Un-Ban";
|
||||
if(ban.user == null){
|
||||
//Fudge the user object if it's already been deleted
|
||||
ban.user = {
|
||||
|
|
@ -302,8 +298,8 @@ class adminUserBanList{
|
|||
|
||||
//Fake the display string
|
||||
var signUpDateString = "-"
|
||||
expirationDateString = "Accounts Nuked"
|
||||
banActionString = "Accounts Nuked"
|
||||
expirationDateString = "Accounts<br>Nuked"
|
||||
banActionString = "Accounts<br>Nuked"
|
||||
|
||||
}else{
|
||||
var signUpDateString = new Date(ban.user.date).toDateString()
|
||||
|
|
@ -331,59 +327,28 @@ class adminUserBanList{
|
|||
nukeAccount.title = `Nuke accounts`;
|
||||
nukeAccount.addEventListener("click",console.log);
|
||||
|
||||
//Append cells to row
|
||||
entryRow.appendChild(newCell(imgNode, true));
|
||||
entryRow.appendChild(newCell(ban.user.id));
|
||||
entryRow.appendChild(newCell(ban.user.user));
|
||||
entryRow.appendChild(newCell(signUpDateString));
|
||||
entryRow.appendChild(newCell(new Date(ban.banDate).toDateString()));
|
||||
entryRow.appendChild(newCell(expirationDateString));
|
||||
entryRow.appendChild(newCell(banActionString));
|
||||
entryRow.appendChild(newCell(ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount]));
|
||||
//append img cell to row
|
||||
entryRow.appendChild(utils.ux.newTableCell(imgNode, true));
|
||||
|
||||
//Append standard cells to row
|
||||
[
|
||||
ban.user.id,
|
||||
ban.user.user,
|
||||
signUpDateString,
|
||||
new Date(ban.banDate).toDateString(),
|
||||
expirationDateString,
|
||||
banActionString,
|
||||
(ban.user.deleted ? unbanIcon : [unbanIcon, nukeAccount])
|
||||
].forEach((content)=>{
|
||||
//I don't like repeating myself, and this didn't really need it's own function
|
||||
//though we could make one where each is an object that contains ever property needed and pass it to a mktable function
|
||||
//I just don't see us using it enough to justify it :P
|
||||
entryRow.appendChild(utils.ux.newTableCell(content));
|
||||
});
|
||||
|
||||
//Append row to table
|
||||
this.table.appendChild(entryRow);
|
||||
});
|
||||
|
||||
//We should really move this over to uxutils along with newrow & newtable functions
|
||||
function newCell(content, firstCol = false){
|
||||
//Create a new 'td' element
|
||||
const cell = document.createElement('td');
|
||||
cell.classList.add("admin-list-entry","admin-list-entry-item");
|
||||
|
||||
//If it's not the first column, mention it!
|
||||
if(!firstCol){
|
||||
cell.classList.add("admin-list-entry-not-first-col");
|
||||
}
|
||||
|
||||
//check for arrays
|
||||
if(content.forEach == null){
|
||||
//add single items
|
||||
addContent(content);
|
||||
}else{
|
||||
//Crawl through content array
|
||||
content.forEach((item)=>{
|
||||
//add each item
|
||||
addContent(item);
|
||||
});
|
||||
}
|
||||
|
||||
//return the resulting cell
|
||||
return cell;
|
||||
|
||||
|
||||
function addContent(ct){
|
||||
//If we're adding as node
|
||||
if(ct.cloneNode != null){
|
||||
//append it like it's a node
|
||||
cell.appendChild(ct);
|
||||
}else{
|
||||
//otherwise use it as innerHTML
|
||||
cell.innerHTML = ct;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -19,10 +19,11 @@ class channelSettingsPage{
|
|||
//Get channel name off of the URL
|
||||
this.channel = window.location.pathname.slice(3).replace('/settings','');
|
||||
//Instantiate UX handling objects, making sure to pass the channel name.
|
||||
this.deleteBtn = new deleteBtn(this.channel);
|
||||
this.rankList = new rankList(this.channel);
|
||||
this.prefrenceList = new prefrenceList(this.channel);
|
||||
this.banList = new banList(this.channel);
|
||||
this.permList = new permList(this.channel);
|
||||
this.prefrenceList = new prefrenceList(this.channel);
|
||||
this.deleteBtn = new deleteBtn(this.channel);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -42,15 +43,6 @@ class rankList{
|
|||
this.userPrompt.addEventListener("keydown", this.submitNewRank.bind(this));
|
||||
}
|
||||
|
||||
resetInput(){
|
||||
//These change every time the table is refreshed, so we should reset the property as well.
|
||||
this.inputs = document.querySelectorAll(".channel-rank-select");
|
||||
|
||||
this.inputs.forEach((input) => {
|
||||
input.addEventListener("change", this.submitUpdate.bind(this));
|
||||
});
|
||||
}
|
||||
|
||||
async loadList(){
|
||||
const list = await utils.ajax.getChannelRank(this.channel);
|
||||
|
||||
|
|
@ -87,7 +79,7 @@ class rankList{
|
|||
//Do not pass go, do not collect $200
|
||||
return;
|
||||
}
|
||||
//Get rank of logged in user
|
||||
//Get name/rank of logged in user
|
||||
const curName = document.querySelector("#username").textContent
|
||||
const curUser = data[curName];
|
||||
const rankEnum = await utils.ajax.getRankEnum();
|
||||
|
|
@ -95,47 +87,25 @@ class rankList{
|
|||
//clear the table
|
||||
this.clearTable();
|
||||
|
||||
//For each user in the list
|
||||
Object.entries(data).forEach((userAr) => {
|
||||
//pull user object from entry array
|
||||
const user = userAr[1];
|
||||
|
||||
//Create entry row
|
||||
const entryRow = document.createElement('tr');
|
||||
entryRow.classList.add("admin-list-entry");
|
||||
|
||||
//Create IMG cell
|
||||
const imgCell = document.createElement('td')
|
||||
imgCell.classList.add("admin-list-entry","admin-list-entry-item");
|
||||
|
||||
//Create IMG node inside of IMG cell
|
||||
const imgNode = document.createElement('img');
|
||||
imgNode.classList.add("admin-list-entry","admin-list-entry-item");
|
||||
imgNode.src = user.img;
|
||||
|
||||
//append Img Node to Img Cell
|
||||
imgCell.appendChild(imgNode);
|
||||
|
||||
//Create ID cell
|
||||
const idCell = document.createElement('td');
|
||||
idCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
||||
idCell.innerHTML = user.id;
|
||||
|
||||
//Create Name cell
|
||||
const nameCell = document.createElement('td');
|
||||
nameCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
||||
nameCell.innerHTML = user.user;
|
||||
|
||||
//Create Rank cell
|
||||
const rankCell = document.createElement('td');
|
||||
rankCell.classList.add("admin-list-entry","admin-list-entry-item","admin-list-entry-not-first-col");
|
||||
//If the listed user rank is equal or higher than the signed-in user
|
||||
if(rankEnum.indexOf(user.rank) >= rankEnum.indexOf(curUser.rank)){
|
||||
rankCell.innerHTML = user.rank;
|
||||
var rankContent = user.rank;
|
||||
}else{
|
||||
//Create rank select
|
||||
const rankSelect = document.createElement('select');
|
||||
rankSelect.id = `channel-rank-select-${user.user}`
|
||||
rankSelect.classList.add("channel-rank-select")
|
||||
var rankContent = document.createElement('select');
|
||||
rankContent.id = `channel-rank-select-${user.user}`
|
||||
rankContent.classList.add("channel-rank-select")
|
||||
rankContent.addEventListener("change", this.submitUpdate.bind(this));
|
||||
|
||||
//for each rank in the enum
|
||||
rankEnum.slice().reverse().forEach((rank) => {
|
||||
|
|
@ -144,24 +114,17 @@ class rankList{
|
|||
rankOption.value = rank;
|
||||
rankOption.innerHTML = rank;
|
||||
rankOption.selected = user.rank == rank;
|
||||
rankSelect.appendChild(rankOption);
|
||||
rankContent.appendChild(rankOption);
|
||||
});
|
||||
|
||||
//Set value to current user rank and append it to Rank Cell
|
||||
rankCell.appendChild(rankSelect);
|
||||
}
|
||||
|
||||
//Append cells to row
|
||||
entryRow.appendChild(imgCell);
|
||||
entryRow.appendChild(idCell);
|
||||
entryRow.appendChild(nameCell);
|
||||
entryRow.appendChild(rankCell);
|
||||
|
||||
//Append row to table
|
||||
this.table.appendChild(entryRow);
|
||||
|
||||
//reset input events
|
||||
this.resetInput();
|
||||
//Generate row and append to table
|
||||
this.table.appendChild(utils.ux.newTableRow([
|
||||
imgNode,
|
||||
user.id,
|
||||
user.user,
|
||||
rankContent
|
||||
]));
|
||||
});
|
||||
}
|
||||
|
||||
|
|
@ -177,6 +140,137 @@ class rankList{
|
|||
}
|
||||
}
|
||||
|
||||
class banList{
|
||||
constructor(channel){
|
||||
this.channel = channel;
|
||||
this.table = document.querySelector("#admin-ban-list-table");
|
||||
this.banPrompt = document.querySelector("#new-ban-input");
|
||||
this.banButton = document.querySelector("#new-ban-button");
|
||||
|
||||
this.loadList();
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
this.banButton.addEventListener('click', this.ban.bind(this));
|
||||
}
|
||||
|
||||
async loadList(){
|
||||
const data = await utils.ajax.getChanBans(this.channel);
|
||||
this.updateList(data);
|
||||
}
|
||||
|
||||
clearList(){
|
||||
const oldRows = this.table.querySelectorAll('tr.admin-list-entry');
|
||||
oldRows.forEach((row) => {
|
||||
row.remove();
|
||||
});
|
||||
}
|
||||
|
||||
async ban(event){
|
||||
new banUserPopup(this.channel, this.banPrompt.value, this.updateList.bind(this));
|
||||
}
|
||||
|
||||
async unban(event){
|
||||
//Rip user outta the target id
|
||||
const user = event.target.id.replace("admin-user-list-unban-icon-","");
|
||||
//Tell the server to unban them and get the list returned
|
||||
const list = await utils.ajax.chanUnban(this.channel, user);
|
||||
|
||||
//Use the list to update the UI
|
||||
this.updateList(list);
|
||||
}
|
||||
|
||||
updateList(bans){
|
||||
//for each ban listed
|
||||
this.clearList();
|
||||
bans.forEach((ban)=>{
|
||||
//Create IMG node
|
||||
const imgNode = document.createElement('img');
|
||||
imgNode.classList.add("admin-list-entry","admin-list-entry-item");
|
||||
imgNode.src = ban.user.img;
|
||||
|
||||
//Create banAlts check
|
||||
const banAlts = document.createElement('i');
|
||||
banAlts.classList.add("admin-user-list-ban-alts",ban.banAlts ? "bi-check" : "bi-x");
|
||||
|
||||
//Create unban icon node
|
||||
const unbanIcon = document.createElement('i');
|
||||
unbanIcon.classList.add("bi-emoji-smile-fill","admin-user-list-icon","admin-user-list-unban-icon");
|
||||
unbanIcon.id = `admin-user-list-unban-icon-${ban.user.user}`;
|
||||
unbanIcon.title = `Unban ${ban.user.user}`;
|
||||
unbanIcon.addEventListener("click", this.unban.bind(this));
|
||||
|
||||
//THIS IS NOT YET PROPERLY IMPLEMENTED AS getDaysUntilExpiration has not been made for chan bans
|
||||
const expirationString = ban.expirationDays < 0 ? "Never" : `${new Date(ban.expirationDate).toDateString()}<br>(${ban.daysUntilExpiration} day(s) left)`;
|
||||
|
||||
//Generate row and append to table
|
||||
this.table.appendChild(utils.ux.newTableRow([
|
||||
imgNode,
|
||||
ban.user.id,
|
||||
ban.user.user,
|
||||
banAlts,
|
||||
new Date(ban.banDate).toDateString(),
|
||||
expirationString,
|
||||
unbanIcon
|
||||
]));
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
class banUserPopup{
|
||||
constructor(channel, target, cb){
|
||||
this.channel = channel;
|
||||
this.target = target;
|
||||
this.cb = cb;
|
||||
this.popup = new canopyUXUtils.popup("channelUserBan", true, this.asyncConstruction.bind(this));
|
||||
}
|
||||
|
||||
asyncConstruction(){
|
||||
this.title = document.querySelector(".popup-title");
|
||||
//Setup title text real quick-like :P
|
||||
this.title.innerHTML = `Ban ${this.target}`;
|
||||
|
||||
this.permBan = document.querySelector("#ban-popup-perm");
|
||||
this.expiration = document.querySelector("#ban-popup-expiration");
|
||||
this.expirationPrefix = document.querySelector("#ban-popup-expiration-prefix");
|
||||
this.banAlts = document.querySelector("#ban-popup-alts");
|
||||
this.banButton = document.querySelector("#ban-popup-ban-button");
|
||||
this.cancelButton = document.querySelector("#ban-popup-cancel-button");
|
||||
|
||||
this.setupInput();
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
this.permBan.addEventListener("change", this.permaBanLabel.bind(this));
|
||||
this.cancelButton.addEventListener("click", this.popup.closePopup.bind(this.popup));
|
||||
this.banButton.addEventListener("click",this.ban.bind(this));
|
||||
}
|
||||
|
||||
permaBanLabel(event){
|
||||
if(event.target.checked){
|
||||
this.expiration.disabled = true;
|
||||
}else{
|
||||
this.expiration.disabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
async ban(event){
|
||||
//Get expiration days
|
||||
const expirationDays = this.permBan.checked ? -1 : this.expiration.value;
|
||||
//Send ban request off to server and retrieve new ban list
|
||||
const data = await utils.ajax.chanBan(this.channel, this.target, expirationDays, this.banAlts.checked);
|
||||
|
||||
//Close the popup
|
||||
this.popup.closePopup();
|
||||
|
||||
//If we have data and a callback, run the callback with our data
|
||||
if(data != null && this.cb != null){
|
||||
await this.cb(data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class prefrenceList{
|
||||
constructor(channel){
|
||||
this.channel = channel;
|
||||
|
|
@ -206,26 +300,6 @@ class prefrenceList{
|
|||
}
|
||||
}
|
||||
|
||||
class deleteBtn{
|
||||
constructor(channel){
|
||||
this.channel = channel;
|
||||
this.delete = document.querySelector("#channel-delete");
|
||||
|
||||
this.delete.addEventListener('click', this.promptDelete.bind(this));
|
||||
}
|
||||
|
||||
promptDelete(){
|
||||
var confirm = window.prompt(`Warning: You are about to nuke ${this.channel} off of the face of the fucking planet, no taksie-backsies. \n \n Type in ${this.channel} to confirm.`);
|
||||
this.deleteChannel(confirm);
|
||||
}
|
||||
|
||||
async deleteChannel(confirm){
|
||||
if(this.channel === confirm){
|
||||
utils.ajax.deleteChannel(this.channel, confirm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class permList{
|
||||
constructor(channel){
|
||||
this.channel = channel
|
||||
|
|
@ -254,4 +328,24 @@ class permList{
|
|||
}
|
||||
}
|
||||
|
||||
new channelSettingsPage();
|
||||
class deleteBtn{
|
||||
constructor(channel){
|
||||
this.channel = channel;
|
||||
this.delete = document.querySelector("#channel-delete");
|
||||
|
||||
this.delete.addEventListener('click', this.promptDelete.bind(this));
|
||||
}
|
||||
|
||||
promptDelete(){
|
||||
var confirm = window.prompt(`Warning: You are about to nuke ${this.channel} off of the face of the fucking planet, no taksie-backsies. \n \n Type in ${this.channel} to confirm.`);
|
||||
this.deleteChannel(confirm);
|
||||
}
|
||||
|
||||
async deleteChannel(confirm){
|
||||
if(this.channel === confirm){
|
||||
utils.ajax.deleteChannel(this.channel, confirm);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const channelSettings = new channelSettingsPage();
|
||||
|
|
@ -34,6 +34,64 @@ class canopyUXUtils{
|
|||
});
|
||||
}
|
||||
|
||||
|
||||
//We should really move this over to uxutils along with newrow & newtable functions
|
||||
newTableCell(content, firstCol = false){
|
||||
//Create a new 'td' element
|
||||
const cell = document.createElement('td');
|
||||
cell.classList.add("admin-list-entry","admin-list-entry-item");
|
||||
|
||||
//If it's not the first column, mention it!
|
||||
if(!firstCol){
|
||||
cell.classList.add("admin-list-entry-not-first-col");
|
||||
}
|
||||
|
||||
//check for arrays
|
||||
if(content.forEach == null){
|
||||
//add single items
|
||||
addContent(content);
|
||||
}else{
|
||||
//Crawl through content array
|
||||
content.forEach((item)=>{
|
||||
//add each item
|
||||
addContent(item);
|
||||
});
|
||||
}
|
||||
|
||||
//return the resulting cell
|
||||
return cell;
|
||||
|
||||
|
||||
function addContent(ct){
|
||||
//If we're adding as node
|
||||
if(ct.cloneNode != null){
|
||||
//append it like it's a node
|
||||
cell.appendChild(ct);
|
||||
}else{
|
||||
//otherwise use it as innerHTML
|
||||
cell.innerHTML = ct;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
newTableRow(cellContent){
|
||||
//Create an empty table row to hold the cells
|
||||
const entryRow = document.createElement('tr');
|
||||
entryRow.classList.add("admin-list-entry");
|
||||
|
||||
entryRow.appendChild(this.newTableCell(cellContent[0], true));
|
||||
|
||||
cellContent.forEach((content, i)=>{
|
||||
if(i > 0){
|
||||
entryRow.append(this.newTableCell(content));
|
||||
}
|
||||
});
|
||||
|
||||
return entryRow;
|
||||
}
|
||||
|
||||
|
||||
static popup = class{
|
||||
constructor(content, ajaxPopup = false, cb){
|
||||
//Define non-popup node values
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue