377 lines
12 KiB
JavaScript
377 lines
12 KiB
JavaScript
/*Canopy - The next generation of stoner streaming software
|
|
Copyright (C) 2024 Rainbownapkin and the TTN Community
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
it under the terms of the GNU Affero General Public License as
|
|
published by the Free Software Foundation, either version 3 of the
|
|
License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Affero General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Affero General Public License
|
|
along with this program. If not, see <https://www.gnu.org/licenses/>.*/
|
|
|
|
class channelSettingsPage{
|
|
constructor(){
|
|
//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.rankList = new rankList(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);
|
|
}
|
|
}
|
|
|
|
class rankList{
|
|
constructor(channel){
|
|
this.channel = channel
|
|
this.table = document.querySelector(".admin-list-table");
|
|
this.userPrompt = document.querySelector("#new-rank-input");
|
|
this.rankSelect = document.querySelector("#new-rank-select");
|
|
|
|
//Load the userlist and setup input
|
|
this.loadList();
|
|
this.setupInput();
|
|
}
|
|
|
|
setupInput(){
|
|
this.userPrompt.addEventListener("keydown", this.submitNewRank.bind(this));
|
|
}
|
|
|
|
async loadList(){
|
|
const list = await utils.ajax.getChannelRank(this.channel);
|
|
|
|
this.updateList(list);
|
|
}
|
|
|
|
async submitNewRank(event){
|
|
if(event.key != "Enter" && event.key != null){
|
|
//Bail out if we didn't hit enter
|
|
return;
|
|
}
|
|
|
|
//Send new rank
|
|
this.submitUserRank(this.userPrompt.value, this.rankSelect.value);
|
|
|
|
//Clear out prompt
|
|
this.userPrompt.value = "";
|
|
}
|
|
|
|
async submitUpdate(event){
|
|
const user = event.target.id.replace("channel-rank-select-","");
|
|
const rank = event.target.value;
|
|
|
|
await this.submitUserRank(user, rank);
|
|
}
|
|
|
|
async submitUserRank(user, rank){
|
|
await this.updateList(await utils.ajax.setChannelRank(this.channel, user, rank));
|
|
}
|
|
|
|
async updateList(data){
|
|
//If no data
|
|
if(!data){
|
|
//Do not pass go, do not collect $200
|
|
return;
|
|
}
|
|
//Get name/rank of logged in user
|
|
const curName = document.querySelector("#username").textContent
|
|
const curUser = data[curName];
|
|
const rankEnum = await utils.ajax.getRankEnum();
|
|
|
|
//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 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;
|
|
|
|
//If the listed user rank is equal or higher than the signed-in user
|
|
if(rankEnum.indexOf(user.rank) >= rankEnum.indexOf(curUser.rank)){
|
|
var rankContent = user.rank;
|
|
}else{
|
|
//Create 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) => {
|
|
//Create an option for the given rank
|
|
const rankOption = document.createElement('option');
|
|
rankOption.value = rank;
|
|
rankOption.innerHTML = rank;
|
|
rankOption.selected = user.rank == rank;
|
|
rankContent.appendChild(rankOption);
|
|
});
|
|
}
|
|
|
|
//Generate row and append to table
|
|
this.table.appendChild(utils.ux.newTableRow([
|
|
imgNode,
|
|
user.id,
|
|
user.user,
|
|
rankContent
|
|
]));
|
|
});
|
|
}
|
|
|
|
clearTable(){
|
|
//get all non-title table rows
|
|
const rows = this.table.querySelectorAll("tr.admin-list-entry");
|
|
|
|
//for each row
|
|
rows.forEach((row) => {
|
|
//The Lord Yeeteth, and The Lord Yoinketh away...
|
|
row.remove();
|
|
});
|
|
}
|
|
}
|
|
|
|
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;
|
|
this.inputs = document.querySelectorAll(".channel-preference-list-item");
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
setupInput(){
|
|
this.inputs.forEach((input) => {
|
|
input.addEventListener("change", this.submitUpdate.bind(this));
|
|
});
|
|
}
|
|
|
|
async submitUpdate(event){
|
|
const key = event.target.id.replace("channel-preference-","");
|
|
const value = event.target.checked;
|
|
const settingsMap = new Map([
|
|
[key, value]
|
|
]);
|
|
|
|
this.handleUpdate(await utils.ajax.setChannelSetting(this.channel, settingsMap), event.target, key);
|
|
}
|
|
|
|
handleUpdate(data, target, key){
|
|
if(data){
|
|
target = data[key];
|
|
}
|
|
}
|
|
}
|
|
|
|
class permList{
|
|
constructor(channel){
|
|
this.channel = channel
|
|
this.inputs = document.querySelectorAll(".channel-perm-select");
|
|
this.setupInput();
|
|
}
|
|
|
|
setupInput(){
|
|
this.inputs.forEach((input) => {
|
|
input.addEventListener("change", this.submitUpdate.bind(this));
|
|
});
|
|
}
|
|
|
|
async submitUpdate(event){
|
|
const key = event.target.id.replace("admin-perm-list-rank-select-","");
|
|
const value = event.target.value;
|
|
const permMap = new Map([
|
|
[key, value]
|
|
]);
|
|
|
|
this.handleUpdate(await utils.ajax.setChannelPermissions(this.channel, permMap), event.target, key);
|
|
}
|
|
|
|
handleUpdate(data, target, key){
|
|
target.value = data[key];
|
|
}
|
|
}
|
|
|
|
class deleteBtn{
|
|
constructor(channel){
|
|
this.channel = channel;
|
|
this.delete = document.querySelector("#channel-delete");
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
setupInput(){
|
|
this.delete.addEventListener('click', () => {new deleteAccountPopup(this.channel)});
|
|
}
|
|
|
|
}
|
|
|
|
class deleteAccountPopup{
|
|
constructor(channel){
|
|
this.channel = channel;
|
|
this.popup = new canopyUXUtils.popup("nukeChannel", true, this.asyncConstructor.bind(this));
|
|
}
|
|
|
|
asyncConstructor(){
|
|
this.prompt = document.querySelector("#delete-channel-popup-prompt");
|
|
this.label = document.querySelector("#delete-channel-popup-content");
|
|
|
|
//Fill channel label
|
|
this.label.innerHTML = this.label.innerHTML.replaceAll("[CHANNEL]", this.channel);
|
|
|
|
this.setupInput();
|
|
}
|
|
|
|
setupInput(){
|
|
this.prompt.addEventListener("keydown", this.nukeAccount.bind(this));
|
|
}
|
|
|
|
async nukeAccount(event){
|
|
if(event.key == "Enter"){
|
|
console.log(this.channel);
|
|
if(this.channel === event.target.value){
|
|
await utils.ajax.deleteChannel(this.channel, event.target.value);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
const channelSettings = new channelSettingsPage(); |