-
<%= profile.user %>
-

+
<%- profile.user %>
+

<% if(selfProfile){ %>
(edit)
<% } %>
-
tokes: <%= profile.tokes %> (Not yet implemented)
+
tokes: <%- profile.tokes %> (Not yet implemented)
- Signature: <%= profile.signature %>
+ Signature: <%- profile.signature %>
<% if(selfProfile){ %>
(edit)
<% } %>
- Bio: <%= profile.bio %>
+ Bio: <%- profile.bio %>
<% if(selfProfile){ %>
(edit)
<% } %>
-
Joined: <%= profile.date.toUTCString(); %>
+
Joined: <%- profile.date.toUTCString(); %>
Badgeless?
diff --git a/www/css/adminPanel.css b/www/css/adminPanel.css
index cc77166..39e58ed 100644
--- a/www/css/adminPanel.css
+++ b/www/css/adminPanel.css
@@ -75,4 +75,37 @@ img.admin-list-entry-item{
.admin-list-field-container{
text-align: center;
+}
+
+#channel-rank-title{
+ margin-bottom: 0;
+}
+
+#channel-rank-sup{
+ margin-bottom: 1em;
+}
+
+
+#channel-user-list-entry-rank-title{
+ width: 5em;
+}
+
+.panel-title{
+ text-align: center;
+}
+
+#new-rank-span{
+ display: flex;
+ padding: 0 1em;
+ height: 1.5em;
+ margin-bottom: 1em;
+}
+
+#new-rank-input{
+ flex-grow: 1;
+}
+
+#new-rank-select{
+ margin: auto;
+ height: 1.3em;
}
\ No newline at end of file
diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css
index 32536c6..7515d40 100644
--- a/www/css/theme/movie-night.css
+++ b/www/css/theme/movie-night.css
@@ -145,7 +145,7 @@ tr.admin-list-entry{
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em;
}
-td.admin-list-entry-not-first-row{
+td.admin-list-entry-not-first-col{
box-shadow: var(--accent1) 1em 0px 1px -2em, var(--accent1) -1em 0px 1px -1em;
}
@@ -263,4 +263,22 @@ select.panel-head-element{
.cpanel-header-div{
border-bottom: solid 1px var(--accent0);
+}
+
+#new-rank-span{
+ background-color: white;
+ border-radius: 1em;
+}
+
+#new-rank-span:focus-within{
+ box-shadow: 2px 2px 3px var(--focus0), -2px 2px 3px var(--focus0), 2px -2px 3px var(--focus0), -2px -2px 3px var(--focus0);
+}
+
+#new-rank-input{
+ border: none;
+}
+
+#new-rank-input:focus{
+ border: none;
+ outline: none;
}
\ No newline at end of file
diff --git a/www/js/channelSettings.js b/www/js/channelSettings.js
index 164c99a..fffc9b1 100644
--- a/www/js/channelSettings.js
+++ b/www/js/channelSettings.js
@@ -14,32 +14,207 @@ 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
.*/
-class channelSettingsPrompt{
+class channelSettingsPage{
constructor(){
+ //Get channel name off of the URL
this.channel = window.location.pathname.slice(3).replace('/settings','');
- this.hidden = document.querySelector("#channel-hidden");
- this.delete = document.querySelector("#channel-delete");
+ //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.permList = new permList(this.channel);
+ }
+}
- this.hidden.addEventListener('change', this.submitUpdate.bind(this));
- this.delete.addEventListener('click', this.promptDelete.bind(this));
+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));
+ }
+
+ 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);
+
+ 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){
- //probably not the cleanest way to get the chan name :P
- const key = event.target.id.split("-").pop();
- const value = event.target.type == "checkbox" ? event.target.checked : event.target.value;
+ 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 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();
+
+ 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;
+ }else{
+ //Create rank select
+ const rankSelect = document.createElement('select');
+ rankSelect.id = `channel-rank-select-${user.user}`
+ rankSelect.classList.add("channel-rank-select")
+
+ //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;
+ rankSelect.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();
+ });
+ }
+
+ 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 prefrenceList{
+ constructor(channel){
+ this.channel = channel;
+ this.inputs = document.querySelectorAll(".channel-preference-list-item");
+ }
+
+ 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));
+ this.handleUpdate(await utils.ajax.setChannelSetting(this.channel, settingsMap), event.target, key);
}
- handleUpdate(updateObj){
- this.hidden.checked = updateObj.hidden;
+ handleUpdate(data, target, key){
+ if(data){
+ target = data[key];
+ }
+ }
+}
+
+class deleteBtn{
+ constructor(channel){
+ this.channel = channel;
+ this.delete = document.querySelector("#channel-delete");
+
+ this.delete.addEventListener('click', this.promptDelete.bind(this));
}
- promptDelete(){
+ 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);
}
@@ -51,4 +226,32 @@ class channelSettingsPrompt{
}
}
-new channelSettingsPrompt();
\ No newline at end of file
+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];
+ }
+}
+
+new channelSettingsPage();
\ No newline at end of file
diff --git a/www/js/profile.js b/www/js/profile.js
index a7ee1e3..ab0abd4 100644
--- a/www/js/profile.js
+++ b/www/js/profile.js
@@ -47,7 +47,11 @@ class profileEditPrompt{
//Setup properties
this.prompt.id = `profile-${this.field}-prompt`;
this.prompt.classList.add("profile-edit-prompt");
- this.prompt.placeholder = this.content.innerHTML;
+ if(this.field == "img"){
+ this.prompt.placeholder = this.content.src;
+ }else{
+ this.prompt.placeholder = this.content.innerHTML;
+ }
//Setup event listener
this.prompt.addEventListener("keydown", this.update.bind(this));
diff --git a/www/js/utils.js b/www/js/utils.js
index c473d13..412b4ad 100644
--- a/www/js/utils.js
+++ b/www/js/utils.js
@@ -187,6 +187,18 @@ class canopyAjaxUtils{
}
}
+ async getRankEnum(){
+ var response = await fetch(`/api/account/rankEnum`,{
+ method: "GET"
+ });
+
+ if(response.status == 200){
+ return (await response.json())
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
async deleteAccount(pass){
const response = await fetch(`/api/account/delete`,{
method: "POST",
@@ -253,6 +265,18 @@ class canopyAjaxUtils{
}
}
+ async getChannelRank(channel){
+ var response = await fetch(`/api/channel/rank?chanName=${channel}`,{
+ method: "GET"
+ });
+
+ if(response.status == 200){
+ return (await response.json())
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
async setChannelRank(chanName, user, rank){
var response = await fetch(`/api/channel/rank`,{
method: "POST",