Gave profile page a facelift in perperation for email/password update pop-ups.
This commit is contained in:
parent
a51152a89d
commit
c32f3df3f3
16 changed files with 316 additions and 157 deletions
|
|
@ -65,7 +65,7 @@ input.control-prompt{
|
|||
flex: 1;
|
||||
}
|
||||
|
||||
div.dynamic-container{
|
||||
div.dynamic-container, div.nested-dynamic-container{
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -13,53 +13,96 @@ 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/>.*/
|
||||
#account{
|
||||
display: flex;
|
||||
margin-top: 2em;
|
||||
justify-content: space-evenly;
|
||||
}
|
||||
|
||||
p.profile-item-edit{
|
||||
display: inline;
|
||||
#profile-div{
|
||||
display: flex;
|
||||
padding: 1em;
|
||||
flex-direction: row;
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
#profile-info{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#profile-info-aux{
|
||||
flex: 2;
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
#account-settings-div{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
#account-settings-buttons{
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#profile-username{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
#profile-img{
|
||||
position: relative;
|
||||
}
|
||||
|
||||
#profile-img-content{
|
||||
margin: 1em 0;
|
||||
}
|
||||
|
||||
#profile-img-prompt{
|
||||
position: absolute;
|
||||
left: -2em;
|
||||
right: -2em;
|
||||
top: calc(50% - 1.3em);
|
||||
}
|
||||
|
||||
.profile-toke-count{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span.profile-item{
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
p.profile-item-label{
|
||||
display: inline;
|
||||
}
|
||||
|
||||
span.account-settings{
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
input.account-settings-password-reset{
|
||||
display: block;
|
||||
}
|
||||
|
||||
a#account-settings-delete-button{
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
span.profile-toke-count{
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
width: fit-content;
|
||||
margin: 0.2em;
|
||||
}
|
||||
|
||||
p.profile-toke-count{
|
||||
p.profile-item{
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
span.profile-item-oneliner{
|
||||
text-wrap: nowrap
|
||||
}
|
||||
|
||||
.profile-item-prompt{
|
||||
display: none;
|
||||
}
|
||||
|
||||
#profile-tokes{
|
||||
resize: vertical;
|
||||
max-width: fit-content;
|
||||
width: fit-content;
|
||||
height: fit-content;
|
||||
min-height: 1.5em;
|
||||
max-height: 5.8em;
|
||||
display: none;
|
||||
padding: 0.5em 0;
|
||||
border-bottom-right-radius: 0;
|
||||
text-wrap: nowrap;
|
||||
visibility: collapse;
|
||||
}
|
||||
|
||||
/*Little hacky but this keeps initial max-height from fucking up resizing*/
|
||||
|
|
@ -69,4 +112,23 @@ p.profile-toke-count{
|
|||
|
||||
.profile-toke{
|
||||
margin: 0.2em 1em;
|
||||
}
|
||||
|
||||
#profile-bio-label{
|
||||
margin-bottom: 0.2em;
|
||||
}
|
||||
|
||||
#profile-bio-content{
|
||||
width: 30VW;
|
||||
}
|
||||
|
||||
#profile-bio-prompt{
|
||||
width: 30VW;
|
||||
height: 11em;
|
||||
resize: vertical;
|
||||
}
|
||||
|
||||
/* temp */
|
||||
input:not([type="checkbox"]):not(.navbar-item):not(.profile-item-prompt){
|
||||
display: block;
|
||||
}
|
||||
|
|
@ -187,10 +187,23 @@ textarea{
|
|||
.dynamic-container{
|
||||
background-color: var(--bg1);
|
||||
color: var(--accent1);
|
||||
}
|
||||
|
||||
.nested-dynamic-container{
|
||||
border: 1px var(--accent1) solid;
|
||||
background-color: var(--bg0);
|
||||
color: var(--accent0);
|
||||
}
|
||||
|
||||
.dynamic-container, .nested-dynamic-container{
|
||||
box-shadow: 3px 3px 1px var(--bg1-alt0) inset;
|
||||
border-radius: 1em;
|
||||
}
|
||||
|
||||
.dynamic-container a{
|
||||
color: var(--accent1);
|
||||
}
|
||||
|
||||
|
||||
tr{
|
||||
box-shadow: var(--accent1) 0px 1em 1px -2em, var(--accent1) 0px -1em 1px -1em;
|
||||
|
|
|
|||
|
|
@ -14,104 +14,125 @@ 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/>.*/
|
||||
|
||||
//Base Class
|
||||
class profileEditPrompt{
|
||||
constructor(field, content, useTextArea = false){
|
||||
class profileUpdatePrompt{
|
||||
constructor(field){
|
||||
this.field = field;
|
||||
this.useTextArea = useTextArea;
|
||||
this.content = content;
|
||||
this.link = document.querySelector(`#profile-${field}-edit`);
|
||||
this.contentNode = document.querySelector(`#profile-${field}-content`);
|
||||
this.promptNode = document.querySelector(`#profile-${field}-prompt`);
|
||||
|
||||
//Bail out if something ain't right
|
||||
if(!this.link || !this.content){
|
||||
return;
|
||||
}
|
||||
|
||||
this.setupPrompt();
|
||||
}
|
||||
|
||||
setupPrompt(){
|
||||
if(this.link != null){
|
||||
this.link.addEventListener("click", this.prompt.bind(this));
|
||||
if(this.promptNode != null){
|
||||
this.setupInput();
|
||||
}
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
this.contentNode.addEventListener('click', this.popPrompt.bind(this));
|
||||
this.promptNode.addEventListener('keydown', this.closePrompt.bind(this));
|
||||
}
|
||||
|
||||
prompt(){
|
||||
//Create input element
|
||||
if(this.useTextArea){
|
||||
this.prompt = document.createElement("textArea");
|
||||
popPrompt(){
|
||||
this.promptNode.style.display = 'inline';
|
||||
this.promptNode.focus();
|
||||
}
|
||||
|
||||
closePrompt(event){
|
||||
//Check if we're finished
|
||||
const fin = event.key == "Escape" || event.key == "Enter";
|
||||
|
||||
//IF we are
|
||||
if(fin){
|
||||
//Display the prompt
|
||||
this.promptNode.style.display = 'none';
|
||||
}
|
||||
|
||||
//Return whether or not we're done
|
||||
return fin;
|
||||
}
|
||||
}
|
||||
|
||||
class profileUpdateTextPrompt extends profileUpdatePrompt{
|
||||
constructor(field, fillPrompt = false){
|
||||
//call derived constructor
|
||||
super(field);
|
||||
|
||||
//Set fill prompt
|
||||
this.fillPrompt = fillPrompt;
|
||||
}
|
||||
|
||||
popPrompt(){
|
||||
//Call derived method
|
||||
super.popPrompt();
|
||||
|
||||
//If we're filling the prompt
|
||||
if(this.fillPrompt){
|
||||
//Fill the prompt content and placeholder
|
||||
this.promptNode.value = this.contentNode.textContent;
|
||||
this.promptNode.placeholder = this.field;
|
||||
//otherwise
|
||||
}else{
|
||||
this.prompt = document.createElement("input");
|
||||
//Just fill the placeholder
|
||||
this.promptNode.placeholder = this.contentNode.textContent;
|
||||
}
|
||||
|
||||
//Setup properties
|
||||
this.prompt.id = `profile-${this.field}-prompt`;
|
||||
this.prompt.classList.add("profile-edit-prompt");
|
||||
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));
|
||||
|
||||
//replace label
|
||||
this.content.replaceWith(this.prompt);
|
||||
//Hide content
|
||||
this.contentNode.style.display = 'none';
|
||||
}
|
||||
|
||||
async update(event){
|
||||
if((!event || event.key == "Enter") && this.prompt.value){
|
||||
//setup object
|
||||
var updateObj = {};
|
||||
updateObj[this.field] = this.prompt.value;
|
||||
async closePrompt(event){
|
||||
//Call derived constructor and check if we're finished, assuming we're finished...
|
||||
if(super.closePrompt(event)){
|
||||
//Display
|
||||
this.contentNode.style.display = 'inline-block';
|
||||
|
||||
//contact server, and collect response
|
||||
var updated_content = (await utils.ajax.updateProfile(updateObj))[this.field];
|
||||
//If we're not cancelling
|
||||
if(event.key == "Enter"){
|
||||
//Create empty update object
|
||||
let updateObj = {};
|
||||
|
||||
//Update label
|
||||
if(updated_content != null){
|
||||
if(this.field == "img"){
|
||||
this.content.src = updated_content;
|
||||
}else{
|
||||
this.content.innerHTML = updated_content;
|
||||
}
|
||||
//Fill field
|
||||
updateObj[this.field] = this.promptNode.value;
|
||||
|
||||
//Send er' off
|
||||
const update = await utils.ajax.updateProfile(updateObj);
|
||||
|
||||
//Fill content from update
|
||||
this.contentNode.innerHTML = update[this.field];
|
||||
}
|
||||
this.finish();
|
||||
}else if(event.key == "Escape" || event.key == "Enter"){
|
||||
this.finish();
|
||||
}
|
||||
}
|
||||
|
||||
finish(){
|
||||
this.prompt.replaceWith(this.content);
|
||||
}
|
||||
}
|
||||
|
||||
class profileTextEditPrompt extends profileEditPrompt{
|
||||
constructor(field, useTextArea = false){
|
||||
//Get content based on field name
|
||||
var content = document.querySelector(`#profile-${field}-content`);
|
||||
//Derived Constructor
|
||||
super(field, content, useTextArea);
|
||||
}
|
||||
|
||||
prompt(){
|
||||
super.prompt();
|
||||
}
|
||||
|
||||
async update(event){
|
||||
await super.update(event)
|
||||
}
|
||||
}
|
||||
|
||||
//Child Classes
|
||||
class profileImgEditPrompt extends profileEditPrompt{
|
||||
class profileUpdateImagePrompt extends profileUpdatePrompt{
|
||||
constructor(){
|
||||
//Get content based on field name
|
||||
var content = document.querySelector(`#profile-img`);
|
||||
//Derived constructor
|
||||
super("img", content, false);
|
||||
//call derived constructor
|
||||
super('img');
|
||||
}
|
||||
|
||||
popPrompt(){
|
||||
//Call derived method
|
||||
super.popPrompt();
|
||||
|
||||
this.promptNode.placeholder = this.contentNode.src;
|
||||
}
|
||||
|
||||
async closePrompt(event){
|
||||
//Call derived constructor
|
||||
super.closePrompt(event);
|
||||
|
||||
//If we're not cancelling
|
||||
if(event.key == "Enter"){
|
||||
//Create empty update object
|
||||
let updateObj = {};
|
||||
|
||||
//Fill field
|
||||
updateObj[this.field] = this.promptNode.value;
|
||||
|
||||
//Send er' off
|
||||
const update = await utils.ajax.updateProfile(updateObj);
|
||||
|
||||
//Fill content from update
|
||||
this.contentNode.src = update[this.field];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -169,11 +190,11 @@ class tokeList{
|
|||
}
|
||||
|
||||
toggleTokeList(){
|
||||
if(this.tokeList.checkVisibility()){
|
||||
this.tokeList.style.display = "none";
|
||||
if(this.tokeList.style.visibility == "visible"){
|
||||
this.tokeList.style.visibility = "collapse";
|
||||
this.tokeListToggleIcon.classList.replace("bi-caret-down-fill","bi-caret-left-fill");
|
||||
}else{
|
||||
this.tokeList.style.display = "block";
|
||||
this.tokeList.style.visibility = "visible";
|
||||
this.tokeListToggleIcon.classList.replace("bi-caret-left-fill","bi-caret-down-fill");
|
||||
}
|
||||
}
|
||||
|
|
@ -183,7 +204,9 @@ class deleteAccountButton{
|
|||
constructor(){
|
||||
this.deleteLink = document.querySelector('#account-settings-delete-button');
|
||||
|
||||
this.setupInput();
|
||||
if(this.deleteLink != null){
|
||||
this.setupInput();
|
||||
}
|
||||
}
|
||||
|
||||
setupInput(){
|
||||
|
|
@ -218,9 +241,10 @@ class deleteAccountPopup{
|
|||
}
|
||||
|
||||
//Object Instantiation
|
||||
new profileTextEditPrompt("signature");
|
||||
new profileTextEditPrompt("bio", true);
|
||||
new profileImgEditPrompt();
|
||||
new tokeList();
|
||||
new passwordResetPrompt();
|
||||
new deleteAccountButton();
|
||||
const imgPrompt = new profileUpdateImagePrompt();
|
||||
const pronounsPrompt = new profileUpdateTextPrompt('pronouns');
|
||||
const signaturePrompt = new profileUpdateTextPrompt('signature');
|
||||
const bioPrompt = new profileUpdateTextPrompt('bio', true);
|
||||
const profileTokeList = new tokeList();
|
||||
const accountPassResetPrompt = new passwordResetPrompt();
|
||||
const accountDeleteButton = new deleteAccountButton();
|
||||
Loading…
Add table
Add a link
Reference in a new issue