From ae76f2636e4344ecefdc1a71ed40c31285ee3c72 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Thu, 8 Aug 2024 14:32:19 -0500 Subject: [PATCH] update user profile stuff --- .dist/index.js | 2 +- .dist/localuser.js | 131 +++++++++++++------------ .dist/member.js | 1 - .dist/settings.js | 194 +++++++++++++++++++++++++++++++++++-- webpage/index.ts | 2 +- webpage/localuser.ts | 130 +++++++++++++------------ webpage/login.html | 6 +- webpage/member.ts | 1 - webpage/register.html | 12 +-- webpage/settings.ts | 216 ++++++++++++++++++++++++++++++++++++++---- webpage/style.css | 31 ++++-- 11 files changed, 544 insertions(+), 182 deletions(-) diff --git a/.dist/index.js b/.dist/index.js index a139f22..93f69c5 100644 --- a/.dist/index.js +++ b/.dist/index.js @@ -165,7 +165,7 @@ document.addEventListener('paste', async (e) => { }); setTheme(); function userSettings() { - thisuser.usersettings.show(); + thisuser.showusersettings(); } document.getElementById("settings").onclick = userSettings; function userConnections() { diff --git a/.dist/localuser.js b/.dist/localuser.js index a406b49..64e8a1f 100644 --- a/.dist/localuser.js +++ b/.dist/localuser.js @@ -1,12 +1,12 @@ import { Guild } from "./guild.js"; import { Direct } from "./direct.js"; -import { Voice } from "./audio.js"; import { User } from "./user.js"; import { Fullscreen } from "./fullscreen.js"; -import { setTheme } from "./login.js"; import { SnowFlake } from "./snowflake.js"; import { Message } from "./message.js"; import { Member } from "./member.js"; +import { Settings } from "./settings.js"; +import { MarkDown } from "./markdown.js"; const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]); class Localuser { lastSequence = null; @@ -636,71 +636,67 @@ class Localuser { typingtext.classList.add("hidden"); } } - genusersettings() { - const hypotheticalProfile = document.createElement("div"); - let file = null; - let newprouns = null; - let newbio = null; - let hypouser = this.user.clone(); - function regen() { - hypotheticalProfile.textContent = ""; - const hypoprofile = hypouser.buildprofile(-1, -1); - hypotheticalProfile.appendChild(hypoprofile); - } - regen(); - this.usersettings = new Fullscreen(["hdiv", - ["vdiv", - ["fileupload", "upload pfp:", function (e) { - console.log(this.files[0]); - file = this.files[0]; - const blob = URL.createObjectURL(this.files[0]); - hypouser.avatar = blob; - hypouser.hypotheticalpfp = true; - regen(); - }], - ["textbox", "Pronouns:", this.user.pronouns, function (e) { - console.log(this.value); - hypouser.pronouns = this.value; - newprouns = this.value; - regen(); - }], - ["mdbox", "Bio:", this.user.bio.rawString, function (e) { - console.log(this.value); - hypouser.bio = this.value; - newbio = this.value; - regen(); - }], - ["button", "update user content:", "submit", () => { - if (file !== null) { - this.updatepfp(file); - } - if (newprouns !== null) { - this.updatepronouns(newprouns); - } - if (newbio !== null) { - this.updatebio(newbio); - } - }], - ["select", "Theme:", ["Dark", "Light", "WHITE"], e => { - localStorage.setItem("theme", ["Dark", "Light", "WHITE"][e.target.selectedIndex]); - setTheme(); - }, ["Dark", "Light", "WHITE"].indexOf(localStorage.getItem("theme"))], - ["select", "Notification sound:", Voice.sounds, e => { - Voice.setNotificationSound(Voice.sounds[e.target.selectedIndex]); - Voice.noises(Voice.sounds[e.target.selectedIndex]); - }, Voice.sounds.indexOf(Voice.getNotificationSound())] - ], - ["vdiv", - ["html", hypotheticalProfile] - ] - ], _ => { }, function () { - console.log(this); - hypouser = this.user.clone(); + showusersettings() { + const settings = new Settings("Settings"); + this.usersettings = settings; + { + const userOptions = settings.addButton("User Settings", { ltr: true }); + const hypotheticalProfile = document.createElement("div"); + let file = null; + let newpronouns = null; + let newbio = null; + let hypouser = this.user.clone(); + function regen() { + hypotheticalProfile.textContent = ""; + const hypoprofile = hypouser.buildprofile(-1, -1); + hypotheticalProfile.appendChild(hypoprofile); + } regen(); - file = null; - newprouns = null; - newbio = null; - }.bind(this)); + const settingsLeft = userOptions.addOptions(""); + const settingsRight = userOptions.addOptions(""); + settingsRight.addHTMLArea(hypotheticalProfile); + const finput = settingsLeft.addFileInput("Upload pfp:", _ => { + if (file) { + this.updatepfp(file); + } + }); + finput.watchForChange(_ => { + if (_.length) { + file = _[0]; + const blob = URL.createObjectURL(file); + hypouser.avatar = blob; + hypouser.hypotheticalpfp = true; + regen(); + } + }); + const pronounbox = settingsLeft.addTextInput("Pronouns", _ => { + if (newpronouns) { + this.updatepronouns(newpronouns); + } + }, { initText: this.user.pronouns }); + pronounbox.watchForChange(_ => { + hypouser.pronouns = _; + newpronouns = _; + regen(); + }); + const bioBox = settingsLeft.addMDInput("Bio:", _ => { + if (newbio) { + this.updatebio(newbio); + } + }, { initText: this.user.bio.rawString }); + bioBox.watchForChange(_ => { + newbio = _; + hypouser.bio = new MarkDown(_, this); + regen(); + }); + } + settings.show(); + } + /** + @deprecated + This should be made to not be used anymore + **/ + genusersettings() { const connectionContainer = document.createElement("div"); connectionContainer.id = "connection-container"; this.userConnections = new Fullscreen(["html", @@ -942,7 +938,8 @@ class Localuser { //---------- resolving members code ----------- waitingmembers = new Map(); async resolvemember(id, guildid) { - console.warn("this function is currently non-functional, either due to a bug in the client or the server, it's currently unclear, use at your own risk"); + console.warn("this function is currently non-functional due to it not being implemented in the server"); + throw new Error("Not implemented on the server side and not fully implemented, do not use"); if (!this.waitingmembers.has(guildid)) { this.waitingmembers.set(guildid, new Map()); } diff --git a/.dist/member.js b/.dist/member.js index 8681e49..3252692 100644 --- a/.dist/member.js +++ b/.dist/member.js @@ -101,7 +101,6 @@ class Member { } const prom1 = fetch(guild.info.api.toString() + "/users/" + id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.snowflake, { headers: guild.headers }); prom1.catch(_ => { console.log(_); }); - guild.localuser.resolvemember(id?.id, guild.id); const promoise = prom1.then(_ => _.json()).then(json => { const memb = new Member(json, guild); Member.already[guild.id][id] = memb; diff --git a/.dist/settings.js b/.dist/settings.js index 9d1fe94..934327a 100644 --- a/.dist/settings.js +++ b/.dist/settings.js @@ -23,6 +23,7 @@ class Buttons { bigtable.classList.add("flexltr"); this.bigtable = bigtable; const htmlarea = document.createElement("div"); + htmlarea.classList.add("flexgrow"); const buttonTable = document.createElement("div"); buttonTable.classList.add("flexttb", "settingbuttons"); for (const thing of this.buttons) { @@ -63,6 +64,8 @@ class Buttons { this.bigtable.append(html); } save() { } + submit() { + } } class PermissionToggle { rolejson; @@ -130,6 +133,122 @@ class PermissionToggle { } return div; } + submit() { + } +} +class TextInput { + label; + owner; + onSubmit; + textContent; + input; + constructor(label, onSubmit, owner, { initText = "" } = {}) { + this.label = label; + this.textContent = initText; + this.owner = owner; + this.onSubmit = onSubmit; + } + generateHTML() { + const div = document.createElement("div"); + const span = document.createElement("span"); + span.textContent = this.label; + div.append(span); + const input = document.createElement("input"); + input.value = this.textContent; + input.type = "text"; + input.oninput = this.onChange.bind(this); + this.input = new WeakRef(input); + div.append(input); + return div; + } + onChange(ev) { + this.owner.changed(); + const value = this.input.deref().value; + this.onchange(value); + this.textContent = value; + } + onchange = _ => { }; + watchForChange(func) { + this.onchange = func; + } + submit() { + this.onSubmit(this.textContent); + } +} +class MDInput { + label; + owner; + onSubmit; + textContent; + input; + constructor(label, onSubmit, owner, { initText = "" } = {}) { + this.label = label; + this.textContent = initText; + this.owner = owner; + this.onSubmit = onSubmit; + } + generateHTML() { + const div = document.createElement("div"); + const span = document.createElement("span"); + span.textContent = this.label; + div.append(span); + div.append(document.createElement("br")); + const input = document.createElement("textarea"); + input.value = this.textContent; + input.oninput = this.onChange.bind(this); + this.input = new WeakRef(input); + div.append(input); + return div; + } + onChange(ev) { + this.owner.changed(); + const value = this.input.deref().value; + this.onchange(value); + this.textContent = value; + } + onchange = _ => { }; + watchForChange(func) { + this.onchange = func; + } + submit() { + this.onSubmit(this.textContent); + } +} +class FileInput { + label; + owner; + onSubmit; + input; + constructor(label, onSubmit, owner, {} = {}) { + this.label = label; + this.owner = owner; + this.onSubmit = onSubmit; + } + generateHTML() { + const div = document.createElement("div"); + const span = document.createElement("span"); + span.textContent = this.label; + div.append(span); + const input = document.createElement("input"); + input.type = "file"; + input.oninput = this.onChange.bind(this); + this.input = new WeakRef(input); + div.append(input); + return div; + } + onChange(ev) { + this.owner.changed(); + if (this.onchange) { + this.onchange(this.input.deref().files); + } + } + onchange = null; + watchForChange(func) { + this.onchange = func; + } + submit() { + this.onSubmit(this.input.deref().files); + } } class RoleList extends Buttons { permissions; @@ -174,28 +293,73 @@ class RoleList extends Buttons { this.onchange(this.curid, this.permission); } } +class HtmlArea { + submit; + html; + constructor(html, submit) { + this.submit = submit; + this.html = html; + } + generateHTML() { + if (this.html instanceof Function) { + return this.html(); + } + else { + return this.html; + } + } +} class Options { name; haschanged = false; options; owner; - constructor(name, owner) { + ltr; + constructor(name, owner, { ltr = false } = {}) { this.name = name; this.options = []; this.owner = owner; + this.ltr = ltr; } addPermissionToggle(roleJSON, permissions) { this.options.push(new PermissionToggle(roleJSON, permissions, this)); } + addOptions(name, { ltr = false } = {}) { + const options = new Options(name, this, { ltr }); + this.options.push(options); + return options; + } + addFileInput(label, onSubmit, {} = {}) { + const FI = new FileInput(label, onSubmit, this, {}); + this.options.push(FI); + return FI; + } + addTextInput(label, onSubmit, { initText = "" } = {}) { + const textInput = new TextInput(label, onSubmit, this, { initText }); + this.options.push(textInput); + return textInput; + } + addMDInput(label, onSubmit, { initText = "" } = {}) { + const mdInput = new MDInput(label, onSubmit, this, { initText }); + this.options.push(mdInput); + return mdInput; + } + addHTMLArea(html, submit = () => { }) { + const htmlarea = new HtmlArea(html, submit); + this.options.push(htmlarea); + return htmlarea; + } generateHTML() { const div = document.createElement("div"); div.classList.add("titlediv"); - const title = document.createElement("h2"); - title.textContent = this.name; - div.append(title); - title.classList.add("settingstitle"); + if (this.name !== "") { + const title = document.createElement("h2"); + title.textContent = this.name; + div.append(title); + title.classList.add("settingstitle"); + } const table = document.createElement("div"); - table.classList.add("flexttb", "flexspace"); + table.classList.add(this.ltr ? "flexltr" : "flexttb", "flexspace"); for (const thing of this.options) { table.append(thing.generateHTML()); } @@ -203,6 +367,10 @@ class Options { return div; } changed() { + if (this.owner instanceof Options) { + this.owner.changed(); + return; + } if (!this.haschanged) { const div = document.createElement("div"); div.classList.add("flexltr", "savediv"); @@ -215,11 +383,19 @@ class Options { this.haschanged = true; this.owner.changed(div); button.onclick = _ => { - this.owner.save(); + if (this.owner instanceof Buttons) { + this.owner.save(); + } div.remove(); + this.submit(); }; } } + submit() { + for (const thing of this.options) { + thing.submit(); + } + } } class Settings extends Buttons { static Buttons = Buttons; @@ -228,8 +404,8 @@ class Settings extends Buttons { constructor(name) { super(name); } - addButton(name) { - const options = new Options(name, this); + addButton(name, { ltr = false } = {}) { + const options = new Options(name, this, { ltr }); this.add(name, options); return options; } diff --git a/webpage/index.ts b/webpage/index.ts index db0c70c..e6694e8 100644 --- a/webpage/index.ts +++ b/webpage/index.ts @@ -184,7 +184,7 @@ document.addEventListener('paste', async (e) => { setTheme(); function userSettings(){ - thisuser.usersettings.show(); + thisuser.showusersettings(); } document.getElementById("settings").onclick=userSettings; function userConnections(){ diff --git a/webpage/localuser.ts b/webpage/localuser.ts index 8d1d611..4f32629 100644 --- a/webpage/localuser.ts +++ b/webpage/localuser.ts @@ -9,6 +9,8 @@ import { SnowFlake } from "./snowflake.js"; import { Message } from "./message.js"; import { channeljson, guildjson, memberjson, readyjson, userjson } from "./jsontypes.js"; import { Member } from "./member.js"; +import { Settings } from "./settings.js"; +import { MarkDown } from "./markdown.js"; const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]); @@ -20,7 +22,7 @@ class Localuser{ initialized:boolean; info; headers:{"Content-type":string,Authorization:string}; - usersettings:Fullscreen; + usersettings:Settings; userConnections:Fullscreen; devPortal:Fullscreen; ready:readyjson; @@ -658,74 +660,69 @@ class Localuser{ typingtext.classList.add("hidden"); } } - genusersettings():void{ - const hypotheticalProfile=document.createElement("div"); - let file=null; - let newprouns=null; - let newbio=null; - let hypouser=this.user.clone(); - function regen(){ - hypotheticalProfile.textContent=""; - const hypoprofile=hypouser.buildprofile(-1,-1); + showusersettings(){ + const settings=new Settings("Settings"); + this.usersettings=settings; + { + const userOptions=settings.addButton("User Settings",{ltr:true}); + const hypotheticalProfile=document.createElement("div"); + let file=null; + let newpronouns=null; + let newbio=null; + let hypouser=this.user.clone(); + function regen(){ + hypotheticalProfile.textContent=""; + const hypoprofile=hypouser.buildprofile(-1,-1); - hypotheticalProfile.appendChild(hypoprofile) - } - regen(); - this.usersettings=new Fullscreen( - ["hdiv", - ["vdiv", - ["fileupload","upload pfp:",function(e){ - console.log(this.files[0]) - file=this.files[0]; - const blob = URL.createObjectURL(this.files[0]); + hypotheticalProfile.appendChild(hypoprofile) + } + regen(); + const settingsLeft=userOptions.addOptions(""); + const settingsRight=userOptions.addOptions(""); + settingsRight.addHTMLArea(hypotheticalProfile); + + const finput=settingsLeft.addFileInput("Upload pfp:",_=>{ + if(file){ + this.updatepfp(file) + } + }); + finput.watchForChange(_=>{ + if(_.length){ + file=_[0]; + const blob = URL.createObjectURL(file); hypouser.avatar = blob; hypouser.hypotheticalpfp=true; regen(); - }], - ["textbox","Pronouns:",this.user.pronouns,function(e){ - console.log(this.value); - hypouser.pronouns=this.value; - newprouns=this.value; - regen(); - }], - ["mdbox","Bio:",this.user.bio.rawString,function(e){ - console.log(this.value); - hypouser.bio=this.value; - newbio=this.value; - regen(); - }], - ["button","update user content:","submit",()=>{ - if(file!==null){ - this.updatepfp(file); - } - if(newprouns!==null){ - this.updatepronouns(newprouns); - } - if(newbio!==null){ - this.updatebio(newbio); - } - }], - ["select","Theme:",["Dark","Light","WHITE"],e=>{ - localStorage.setItem("theme",["Dark","Light","WHITE"][e.target.selectedIndex]); - setTheme(); - },["Dark","Light","WHITE"].indexOf(localStorage.getItem("theme"))], - ["select","Notification sound:",Voice.sounds,e=>{ - Voice.setNotificationSound(Voice.sounds[e.target.selectedIndex]); - Voice.noises(Voice.sounds[e.target.selectedIndex]); - },Voice.sounds.indexOf(Voice.getNotificationSound())] - ], - ["vdiv", - ["html",hypotheticalProfile] - ] - ],_=>{},function(this:Localuser){ - console.log(this); - hypouser=this.user.clone(); - regen(); - file=null; - newprouns=null; - newbio=null; - }.bind(this)) - + } + }); + const pronounbox=settingsLeft.addTextInput("Pronouns",_=>{ + if(newpronouns){ + this.updatepronouns(newpronouns); + } + },{initText:this.user.pronouns}); + pronounbox.watchForChange(_=>{ + hypouser.pronouns=_; + newpronouns=_; + regen(); + }); + const bioBox=settingsLeft.addMDInput("Bio:",_=>{ + if(newbio){ + this.updatebio(newbio); + } + },{initText:this.user.bio.rawString}); + bioBox.watchForChange(_=>{ + newbio=_; + hypouser.bio=new MarkDown(_,this); + regen(); + }) + } + settings.show(); + } + /** + @deprecated + This should be made to not be used anymore + **/ + genusersettings():void{ const connectionContainer=document.createElement("div"); connectionContainer.id="connection-container"; this.userConnections=new Fullscreen( @@ -986,7 +983,8 @@ class Localuser{ //---------- resolving members code ----------- waitingmembers:Mapvoid>>=new Map(); async resolvemember(id:string,guildid:string):Promise{ - console.warn("this function is currently non-functional, either due to a bug in the client or the server, it's currently unclear, use at your own risk"); + console.warn("this function is currently non-functional due to it not being implemented in the server"); + throw new Error("Not implemented on the server side and not fully implemented, do not use"); if(!this.waitingmembers.has(guildid)){ this.waitingmembers.set(guildid,new Map()); } diff --git a/webpage/login.html b/webpage/login.html index 6e14740..0fe1f12 100644 --- a/webpage/login.html +++ b/webpage/login.html @@ -15,13 +15,13 @@

-

+


-

+


-



+



diff --git a/webpage/member.ts b/webpage/member.ts index 9d19df6..280aaa7 100644 --- a/webpage/member.ts +++ b/webpage/member.ts @@ -94,7 +94,6 @@ class Member{ } const prom1= fetch(guild.info.api.toString()+"/users/"+id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.snowflake,{headers:guild.headers}) prom1.catch(_=>{console.log(_)}) - guild.localuser.resolvemember(id?.id,guild.id); const promoise=prom1.then(_=>_.json()).then(json=>{ const memb=new Member(json,guild); Member.already[guild.id][id]=memb; diff --git a/webpage/register.html b/webpage/register.html index 83c7c5d..e8d23b4 100644 --- a/webpage/register.html +++ b/webpage/register.html @@ -16,30 +16,30 @@

- +

- +

- +

- +

- +

- +
diff --git a/webpage/settings.ts b/webpage/settings.ts index 1b2b54a..846dce8 100644 --- a/webpage/settings.ts +++ b/webpage/settings.ts @@ -2,8 +2,11 @@ import { Permissions } from "./permissions.js"; import { Guild } from "./guild.js"; import { SnowFlake } from "./snowflake.js"; import { Role } from "./role.js"; - -class Buttons{ +interface OptionsElement { + generateHTML():HTMLElement; + submit:()=>void; +} +class Buttons implements OptionsElement{ readonly name:string; readonly buttons:[string,Options|string][]; bigtable:HTMLDivElement; @@ -23,7 +26,7 @@ class Buttons{ bigtable.classList.add("flexltr"); this.bigtable=bigtable; const htmlarea=document.createElement("div"); - + htmlarea.classList.add("flexgrow"); const buttonTable=document.createElement("div"); buttonTable.classList.add("flexttb","settingbuttons"); for(const thing of this.buttons){ @@ -63,9 +66,12 @@ class Buttons{ this.bigtable.append(html); } save(){} + submit(){ + + } } -class PermissionToggle{ +class PermissionToggle implements OptionsElement{ readonly rolejson:{name:string,readableName:string,description:string}; permissions:Permissions; owner:Options; @@ -126,6 +132,123 @@ class PermissionToggle{ } return div; } + submit(){ + + } +} +class TextInput implements OptionsElement{ + readonly label:string; + readonly owner:Options; + readonly onSubmit:(str:string)=>void; + textContent:string; + input:WeakRef + constructor(label:string,onSubmit:(str:string)=>void,owner:Options,{initText=""}={}){ + this.label=label; + this.textContent=initText; + this.owner=owner; + this.onSubmit=onSubmit; + } + generateHTML():HTMLDivElement{ + const div=document.createElement("div"); + const span=document.createElement("span"); + span.textContent=this.label; + div.append(span); + const input=document.createElement("input"); + input.value=this.textContent; + input.type="text"; + input.oninput=this.onChange.bind(this); + this.input=new WeakRef(input); + div.append(input); + return div; + } + private onChange(ev:Event){ + this.owner.changed(); + const value=this.input.deref().value as string; + this.onchange(value); + this.textContent=value; + } + onchange:(str:string)=>void=_=>{}; + watchForChange(func:(str:string)=>void){ + this.onchange=func; + } + submit(){ + this.onSubmit(this.textContent); + } +} +class MDInput implements OptionsElement{ + readonly label:string; + readonly owner:Options; + readonly onSubmit:(str:string)=>void; + textContent:string; + input:WeakRef + constructor(label:string,onSubmit:(str:string)=>void,owner:Options,{initText=""}={}){ + this.label=label; + this.textContent=initText; + this.owner=owner; + this.onSubmit=onSubmit; + } + generateHTML():HTMLDivElement{ + const div=document.createElement("div"); + const span=document.createElement("span"); + span.textContent=this.label; + div.append(span); + div.append(document.createElement("br")); + const input=document.createElement("textarea"); + input.value=this.textContent; + input.oninput=this.onChange.bind(this); + this.input=new WeakRef(input); + div.append(input); + return div; + } + onChange(ev:Event){ + this.owner.changed(); + const value=this.input.deref().value as string; + this.onchange(value); + this.textContent=value; + } + onchange:(str:string)=>void=_=>{}; + watchForChange(func:(str:string)=>void){ + this.onchange=func; + } + submit(){ + this.onSubmit(this.textContent); + } +} +class FileInput implements OptionsElement{ + readonly label:string; + readonly owner:Options; + readonly onSubmit:(str:FileList)=>void; + input:WeakRef + constructor(label:string,onSubmit:(str:FileList)=>void,owner:Options,{}={}){ + this.label=label; + this.owner=owner; + this.onSubmit=onSubmit; + } + generateHTML():HTMLDivElement{ + const div=document.createElement("div"); + const span=document.createElement("span"); + span.textContent=this.label; + div.append(span); + const input=document.createElement("input"); + input.type="file"; + input.oninput=this.onChange.bind(this); + this.input=new WeakRef(input); + div.append(input); + return div; + } + onChange(ev:Event){ + this.owner.changed(); + if(this.onchange){ + this.onchange(this.input.deref().files); + } + } + onchange:(str:FileList)=>void=null; + watchForChange(func:(str:FileList)=>void){ + this.onchange=func; + } + submit(){ + this.onSubmit(this.input.deref().files); + } } class RoleList extends Buttons{ readonly permissions:[SnowFlake,Permissions][]; @@ -170,30 +293,73 @@ class RoleList extends Buttons{ this.onchange(this.curid,this.permission); } } -class Options{ +class HtmlArea implements OptionsElement{ + submit: () => void; + html:(()=>HTMLElement)|HTMLElement; + constructor(html:(()=>HTMLElement)|HTMLElement,submit:()=>void){ + this.submit=submit; + this.html=html; + } + generateHTML(): HTMLElement { + if(this.html instanceof Function){ + return this.html(); + }else{ + return this.html; + } + } +} +class Options implements OptionsElement{ name:string; haschanged=false; - readonly options:(PermissionToggle|Buttons|RoleList)[]; - readonly owner:Buttons; - - constructor(name:string,owner:Buttons){ + readonly options:OptionsElement[]; + readonly owner:Buttons|Options; + readonly ltr:boolean; + constructor(name:string,owner:Buttons|Options,{ltr=false}={}){ this.name=name; this.options=[]; this.owner=owner; + this.ltr=ltr; } addPermissionToggle(roleJSON:PermissionToggle["rolejson"],permissions:Permissions){ this.options.push(new PermissionToggle(roleJSON,permissions,this)); } + addOptions(name:string,{ltr=false}={}){ + const options=new Options(name,this,{ltr}); + this.options.push(options); + return options; + } + addFileInput(label:string,onSubmit:(files:FileList)=>void,{}={}){ + const FI=new FileInput(label,onSubmit,this,{}); + this.options.push(FI); + return FI; + } + addTextInput(label:string,onSubmit:(str:string)=>void,{initText=""}={}){ + const textInput=new TextInput(label,onSubmit,this,{initText}); + this.options.push(textInput); + return textInput; + } + addMDInput(label:string,onSubmit:(str:string)=>void,{initText=""}={}){ + const mdInput=new MDInput(label,onSubmit,this,{initText}); + this.options.push(mdInput); + return mdInput; + } + addHTMLArea(html:(()=>HTMLElement)|HTMLElement,submit:()=>void=()=>{}){ + const htmlarea=new HtmlArea(html,submit); + this.options.push(htmlarea); + return htmlarea; + } generateHTML():HTMLElement{ const div=document.createElement("div"); div.classList.add("titlediv"); - const title=document.createElement("h2"); - title.textContent=this.name; - div.append(title); - title.classList.add("settingstitle") + if(this.name!==""){ + const title=document.createElement("h2"); + title.textContent=this.name; + div.append(title); + title.classList.add("settingstitle"); + } const table=document.createElement("div"); - table.classList.add("flexttb","flexspace"); + table.classList.add(this.ltr?"flexltr":"flexttb","flexspace"); for(const thing of this.options){ table.append(thing.generateHTML()); } @@ -201,7 +367,10 @@ class Options{ return div; } changed(){ - + if(this.owner instanceof Options){ + this.owner.changed(); + return; + } if(!this.haschanged){ const div=document.createElement("div"); div.classList.add("flexltr","savediv"); @@ -215,15 +384,21 @@ class Options{ this.owner.changed(div); button.onclick=_=>{ - this.owner.save(); + if(this.owner instanceof Buttons){ + this.owner.save(); + } div.remove(); + this.submit(); } } } + submit(){ + for(const thing of this.options){ + thing.submit(); + } + } } - - class Settings extends Buttons{ static readonly Buttons=Buttons; static readonly Options=Options; @@ -231,8 +406,8 @@ class Settings extends Buttons{ constructor(name:string){ super(name); } - addButton(name:string):Options{ - const options=new Options(name,this); + addButton(name:string,{ltr=false}={}):Options{ + const options=new Options(name,this,{ltr}); this.add(name,options); return options; } @@ -262,5 +437,6 @@ class Settings extends Buttons{ this.html=null; } } + export {Settings,RoleList} diff --git a/webpage/style.css b/webpage/style.css index 84ba3c5..775c4cb 100644 --- a/webpage/style.css +++ b/webpage/style.css @@ -70,6 +70,8 @@ th { max-height: 100%; display: flex; flex-direction: column; + align-content: stretch; + align-items: stretch; } .messagediv:hover { @@ -745,6 +747,7 @@ textarea:focus-visible, align-items: center; padding: .02in; box-sizing: border-box; + flex-grow: 0; } #channels-td { @@ -1223,17 +1226,15 @@ span { padding: 0in; overflow: hidden; } -.Buttons{ - background:var(--primary-bg); - position: relative; -} .flexltr{ display:flex; flex-wrap: nowrap; flex-direction: row; max-height: 100%; overflow: auto; - flex-shrink: 0; + flex-shrink: 1; + flex-grow: 1; + align-items: stretch; } .flexttb{ display: flex; @@ -1247,9 +1248,11 @@ span { } .settingbuttons{ padding-top:.075in; - width: 4in; + width: 1.5in; border-right: solid var(--message-bg-hover); gap:.04in; + flex-grow: 0; + flex-shrink: 0; } .setting{ background:var(--user-info-bg); @@ -1269,6 +1272,7 @@ span { height:100%; display: flex; flex-direction: column; + /* width: 100%; */ } .settingstitle{ font-weight:900; @@ -1582,6 +1586,7 @@ form div{ flex-shrink: 1; flex-grow: 1; } + .reactionCount{ /* position:absolute; */ font-size:.125in; @@ -1596,6 +1601,9 @@ form div{ } .reactiondiv{ +} +.flexgrow{ + flex-grow:1; } .meReacted{ background:var(--channels-bg) !important; @@ -1618,4 +1626,13 @@ form div{ } ; padding: .03in; -} \ No newline at end of file +} +.Buttons{ + background:var(--primary-bg); + position: relative; + height: 100%; + /* box-sizing: border-box; */ + display: flex; + align-content: stretch; + align-items: stretch; +}