diff --git a/src/views/partial/profile/settings.ejs b/src/views/partial/profile/settings.ejs
index 8f5a9f8..aabe7a8 100644
--- a/src/views/partial/profile/settings.ejs
+++ b/src/views/partial/profile/settings.ejs
@@ -22,6 +22,7 @@ along with this program. If not, see . %>
+
\ No newline at end of file
diff --git a/www/css/global.css b/www/css/global.css
index d3a2919..9707e01 100644
--- a/www/css/global.css
+++ b/www/css/global.css
@@ -153,7 +153,8 @@ p.navbar-item, input.navbar-item{
bottom: 0;
right: 0;
left: 0;
- z-index: 9000;
+ /*Popup backer has to exist behind popups that are behind default ones for error reasons*/
+ z-index: 8998;
}
.popup-div{
diff --git a/www/css/popup/ignoredUsers.css b/www/css/popup/ignoredUsers.css
new file mode 100644
index 0000000..18ee7ce
--- /dev/null
+++ b/www/css/popup/ignoredUsers.css
@@ -0,0 +1,38 @@
+/*Canopy - The next generation of stoner streaming software
+Copyright (C) 2024-2026 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 .*/
+
+/*Gotta keep er' behind the errors*/
+.popup-div:has(#ignored-users-popup){
+ z-index: 8999;
+}
+
+#ignored-users-popup-list-div{
+ padding: 0;
+ margin-bottom: 1em;
+}
+
+.ignored-users-popup-ignored-user {
+ display: flex;
+}
+
+.ignored-users-popup-ignored-user-label {
+ margin: 0;
+}
+
+.ignored-users-popup-ignored-user {
+ justify-content: space-between;
+ padding: 0 0.5em;
+}
diff --git a/www/css/theme/movie-night.css b/www/css/theme/movie-night.css
index 053356f..2f7b2df 100644
--- a/www/css/theme/movie-night.css
+++ b/www/css/theme/movie-night.css
@@ -23,6 +23,7 @@ along with this program. If not, see .*/
--bg1: rgb(70, 70, 70);
--bg2: rgb(220, 220, 220);
--bg1-alt0: rgb(30, 30, 30);
+ --bg1-alt1: rgb(10, 10, 10);
--bg2-alt0: rgb(180, 180, 180);
--accent0: rgb(47, 47, 47);
@@ -588,6 +589,18 @@ select.panel-head-element{
color: var(--accent1);
}
+/*Ignored Users Popup*/
+#ignored-users-popup-list-div{
+ border-radius: 0;
+ border: 1px solid var(--accent1);
+}
+
+
+.ignored-users-popup-ignored-user:nth-child(even):not(.chat-entry-toke){
+ box-shadow: 3px 0px 1px var(--bg1-alt1) inset;
+ background-color: var(--bg1-alt0);
+}
+
/* psa */
#psa-container{
border: 3px solid var(--danger0);
diff --git a/www/css/theme/the-network-classic.css b/www/css/theme/the-network-classic.css
index b0edd61..6a16288 100644
--- a/www/css/theme/the-network-classic.css
+++ b/www/css/theme/the-network-classic.css
@@ -778,6 +778,18 @@ select.panel-head-element{
color: var(--accent1);
}
+/*Ignored Users Popup*/
+#ignored-users-popup-list-div{
+ border-radius: 0;
+ border: 1px solid var(--accent1);
+}
+
+
+.ignored-users-popup-ignored-user:nth-child(even):not(.chat-entry-toke){
+ box-shadow: 3px 0px 1px var(--bg1) inset;
+ background-color: var(--bg2-alt0);
+}
+
/* psa */
#psa-container{
border: 3px solid var(--danger0);
diff --git a/www/js/profile.js b/www/js/profile.js
index ed823fe..91847ae 100644
--- a/www/js/profile.js
+++ b/www/js/profile.js
@@ -142,46 +142,6 @@ class profileUpdateImagePrompt extends profileUpdatePrompt{
}
}
-class passwordResetPrompt{
- constructor(){
- this.oldPassNode = document.querySelector('#account-settings-password-reset-old');
- this.newPassNode = document.querySelector('#account-settings-password-reset-new');
- this.confirmPassNode = document.querySelector('#account-settings-password-reset-confirm');
-
- this.setupInput(this.oldPassNode);
- this.setupInput(this.newPassNode);
- this.setupInput(this.confirmPassNode);
- }
-
- setupInput(node){
- if(node != null){
- node.addEventListener("keydown", this.update.bind(this));
- }
- }
-
- async update(event){
- var hasVal = (this.oldPassNode.value && this.newPassNode.value && this.confirmPassNode.value);
- if((!event || event.key == "Enter") && hasVal){
- if(this.newPassNode.value == this.confirmPassNode.value){
- const updateObj = {};
-
- updateObj.passChange = {
- oldPass: this.oldPassNode.value,
- newPass: this.newPassNode.value,
- confirmPass: this.confirmPassNode.value
- };
-
- const response = await utils.ajax.updateProfile(updateObj);
-
- if(response != null){
- //Return user homepage after good pass change, as we've probably been logged out by the server for security.
- window.location.pathname = '/';
- }
- }
- }
- }
-}
-
class tokeList{
constructor(){
this.tokeList = document.querySelector('#profile-tokes');
@@ -288,6 +248,93 @@ class changePasswordPopup{
}
}
+class ignoredUsersPopup{
+ constructor(){
+ this.popup = new canopyUXUtils.popup("ignoredUsers", true, this.asyncConstructor.bind(this), document, false);
+ }
+
+ asyncConstructor(){
+ this.ignoredUsersListDiv = document.querySelector('#ignored-users-popup-list-div')
+ this.ignorePrompt = document.querySelector('#ignored-users-popup-prompt');
+ this.ignoreButton = document.querySelector('#ignored-users-popup-button');
+
+ //Refresh ignored list
+ this.refreshList();
+
+ this.setupInput();
+ }
+
+ //grabs and re-renders list, good for asynchronous fire-and-forget usage
+ async refreshList(){
+ this.renderUsers(await utils.ajax.getIgnoredUsers());
+ }
+
+ renderUsers(users){
+ //Clear out list div
+ this.ignoredUsersListDiv.innerHTML = '';
+
+ //run through our ignored users
+ for(const user of users){
+ //Create a div about it
+ const userDiv = document.createElement('div');
+ userDiv.classList.add('ignored-users-popup-ignored-user');
+
+ //Create userlabel
+ const userLabel = document.createElement('p');
+ userLabel.classList.add('ignored-users-popup-ignored-user-label');
+ userLabel.textContent = user.user;
+
+ //Create unignore icon
+ const unignoreIcon = document.createElement('i');
+ unignoreIcon.classList.add('bi-emoji-smile-fill', 'ignored-users-popup-unignore-user-icon');
+ unignoreIcon.title = `Unignore ${user.user}`;
+
+ //Make the unignore button actually fuckin' do something for once in its no-good, lazy, yellowbellied, chicken-fucking life
+ unignoreIcon.addEventListener('click', (event)=>{this.unignore(user.user)});
+
+ //ASSEMBLE
+ userDiv.append(userLabel, unignoreIcon);
+
+ //Render out to popup
+ this.ignoredUsersListDiv.appendChild(userDiv);
+
+ }
+ }
+
+ setupInput(){
+ this.ignorePrompt.addEventListener('keydown', this.ignore.bind(this));
+ this.ignoreButton.addEventListener('click', this.ignore.bind(this));
+ }
+
+ async ignore(event){
+ //If we clicked or hit enter
+ if(event.key == null || event.key == "Enter"){
+ //Tell server who you wanna ignore and get the new list back
+ const ignoredList = await utils.ajax.ignoreUser(this.ignorePrompt.value);
+
+ //If we received a good list
+ if(ignoredList != null){
+ //Render out received ignored lsit
+ this.renderUsers(ignoredList);
+ }
+
+ //Clear prompt
+ this.ignorePrompt.value = "";
+ }
+ }
+
+ async unignore(user){
+ //Tell server who you wanna unignore and get the new list back
+ const ignoredList = await utils.ajax.unignoreUser(user);
+
+ //If we received a good list
+ if(ignoredList != null){
+ //Render out received ignored lsit
+ this.renderUsers(ignoredList);
+ }
+ }
+}
+
class deleteAccountPopup{
constructor(){
this.popup = new canopyUXUtils.popup("nukeUser", true, this.asyncConstructor.bind(this));
@@ -318,4 +365,5 @@ const bioPrompt = new profileUpdateTextPrompt('bio', true);
const profileTokeList = new tokeList();
const accountEmailChange = new accountSettingsButton('update-email', changeEmailPopup);
const accountPasswordChange = new accountSettingsButton('change-password', changePasswordPopup);
-const accountDeleteButton = new accountSettingsButton('delete', deleteAccountPopup);
\ No newline at end of file
+const accountIgnoredUsers = new accountSettingsButton('change-password', changePasswordPopup);
+const accountDeleteButton = new accountSettingsButton('ignored-users', ignoredUsersPopup);
\ No newline at end of file
diff --git a/www/js/utils.js b/www/js/utils.js
index bdfa514..4661e3a 100644
--- a/www/js/utils.js
+++ b/www/js/utils.js
@@ -885,6 +885,52 @@ class canopyAjaxUtils{
}
}
+ async getIgnoredUsers(){
+ var response = await fetch(`/api/account/ignore`,{
+ method: "GET"
+ });
+
+ if(response.ok){
+ return (await response.json())
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
+ async ignoreUser(user){
+ const response = await fetch(`/api/account/ignore`,{
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "x-csrf-token": utils.ajax.getCSRFToken()
+ },
+ body: JSON.stringify({user})
+ });
+
+ if(response.ok){
+ return (await response.json())
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
+ async unignoreUser(user){
+ const response = await fetch(`/api/account/unignore`,{
+ method: "POST",
+ headers: {
+ "Content-Type": "application/json",
+ "x-csrf-token": utils.ajax.getCSRFToken()
+ },
+ body: JSON.stringify({user})
+ });
+
+ if(response.ok){
+ return (await response.json())
+ }else{
+ utils.ux.displayResponseError(await response.json());
+ }
+ }
+
async deleteAccount(pass){
const response = await fetch(`/api/account/delete`,{
method: "POST",
diff --git a/www/popup/ignoredUsers.html b/www/popup/ignoredUsers.html
new file mode 100644
index 0000000..5eae53a
--- /dev/null
+++ b/www/popup/ignoredUsers.html
@@ -0,0 +1,24 @@
+
+
+
+
+
\ No newline at end of file