diff --git a/.dist/channel.js b/.dist/channel.js index 1d65758..413c829 100644 --- a/.dist/channel.js +++ b/.dist/channel.js @@ -31,6 +31,7 @@ class Channel { message_notifications; allthewayup; static contextmenu = new Contextmenu("channel menu"); + replyingto; static setupcontextmenu() { Channel.contextmenu.addbutton("Copy channel id", function () { console.log(this); @@ -68,7 +69,6 @@ class Channel { for (const thing of JSON.permission_overwrites) { this.permission_overwrites[thing.id] = new Permissions(thing.allow, thing.deny); } - console.log(this.permission_overwrites); this.topic = JSON.topic; this.nsfw = JSON.nsfw; this.position = JSON.position; @@ -94,20 +94,30 @@ class Channel { this.lastpin = json.last_pin_timestamp; } get hasunreads() { + if (!this.hasPermission("VIEW_CHANNEL")) { + return false; + } return this.lastmessageid !== this.lastreadmessageid && this.type !== 4; } - get canMessage() { - console.log("this should run"); - for (const thing of Object.entries(this.permission_overwrites)) { - const perm = thing[1].getPermision("SEND_MESSAGES"); - if (perm === 1) { + hasPermission(name, member = this.guild.member) { + if (member.isAdmin()) { + return true; + } + for (const thing of member.roles) { + if (this.permission_overwrites[thing.id]) { + let perm = this.permission_overwrites[thing.id].getPermision(name); + if (perm) { + return perm === 1; + } + } + if (thing.permissions.getPermision(name)) { return true; } - if (perm === -1) { - return false; - } } - return true; + return false; + } + get canMessage() { + return this.hasPermission("SEND_MESSAGES"); } sortchildren() { this.children.sort((a, b) => { return a.position - b.position; }); @@ -144,6 +154,17 @@ class Channel { static dragged = []; createguildHTML(admin = false) { const div = document.createElement("div"); + if (!this.hasPermission("VIEW_CHANNEL")) { + let quit = true; + for (const thing of this.children) { + if (thing.hasPermission("VIEW_CHANNEL")) { + quit = false; + } + } + if (quit) { + return div; + } + } div["all"] = this; div.draggable = admin; div.addEventListener("dragstart", (e) => { Channel.dragged = [this, div]; e.stopImmediatePropagation(); }); @@ -383,34 +404,38 @@ class Channel { headers: this.headers }); } - getHTML() { + async getHTML() { if (this.guild !== this.localuser.lookingguild) { this.guild.loadGuild(); } this.guild.prevchannel = this; this.localuser.channelfocus = this; - this.putmessages(); + const prom = Message.wipeChanel(); + await this.putmessages(); + await prom; + this.buildmessages(); history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.id); document.getElementById("channelname").textContent = "#" + this.name; console.log(this); document.getElementById("typebox").disabled = !this.canMessage; } - putmessages() { - const out = this; - fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?limit=100", { + async putmessages() { + if (this.messages.length >= 100) { + return; + } + ; + const j = await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?limit=100", { method: 'GET', headers: this.headers, - }).then((j) => { return j.json(); }).then(responce => { - document.getElementById("messages").innerHTML = ''; - for (const thing of responce) { - const messager = new Message(thing, this); - if (out.messageids[messager.id] == undefined) { - out.messageids[messager.id] = messager; - out.messages.push(messager); - } - } - out.buildmessages(); }); + const responce = await j.json(); + for (const thing of responce) { + const messager = new Message(thing, this); + if (this.messageids[messager.id] === undefined) { + this.messageids[messager.id] = messager; + this.messages.push(messager); + } + } } delChannel(JSON) { const build = []; @@ -565,6 +590,9 @@ class Channel { } } messageCreate(messagep) { + if (!this.hasPermission("VIEW_CHANNEL")) { + return; + } const messagez = new Message(messagep.d, this); this.lastmessageid = messagez.id; if (messagez.author === this.localuser.user) { diff --git a/.dist/contextmenu.js b/.dist/contextmenu.js index dc7d998..4310b4e 100644 --- a/.dist/contextmenu.js +++ b/.dist/contextmenu.js @@ -52,11 +52,15 @@ class Contextmenu { return this.div; } bind(obj, addinfo = undefined) { - obj.addEventListener("contextmenu", (event) => { + const func = (event) => { event.preventDefault(); event.stopImmediatePropagation(); this.makemenu(event.clientX, event.clientY, addinfo, obj); - }); + }; + obj.addEventListener("contextmenu", func); + return func; + } + static keepOnScreen(obj) { } } Contextmenu.setup(); diff --git a/.dist/direct.js b/.dist/direct.js index 9fcf9f6..bfa5b6e 100644 --- a/.dist/direct.js +++ b/.dist/direct.js @@ -4,7 +4,7 @@ import { Message } from "./message.js"; import { User } from "./user.js"; class Direct extends Guild { constructor(JSON, owner) { - super(-1, owner); + super(-1, owner, null); this.message_notifications = 0; console.log(JSON); this.owner = owner; @@ -97,10 +97,16 @@ class Group extends Channel { }; return div; } - getHTML() { + async getHTML() { + if (this.guild !== this.localuser.lookingguild) { + this.guild.loadGuild(); + } + const prom = Message.wipeChanel(); this.guild.prevchannel = this; this.localuser.channelfocus = this; - this.putmessages(); + await this.putmessages(); + await prom; + this.buildmessages(); history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.id); document.getElementById("channelname").textContent = "@" + this.name; } @@ -186,5 +192,11 @@ class Group extends Channel { else { } } + isAdmin() { + return false; + } + hasPermission(name, member) { + return true; + } } export { Direct, Group }; diff --git a/.dist/guild.js b/.dist/guild.js index 161bd18..dc7b712 100644 --- a/.dist/guild.js +++ b/.dist/guild.js @@ -2,6 +2,7 @@ import { Channel } from "./channel.js"; import { Contextmenu } from "./contextmenu.js"; import { Role } from "./role.js"; import { Fullscreen } from "./fullscreen.js"; +import { Member } from "./member.js"; class Guild { owner; headers; @@ -52,15 +53,12 @@ class Guild { },null,_=>{return thisuser.isAdmin()}) */ } - constructor(JSON, owner) { + constructor(JSON, owner, member) { if (JSON === -1) { return; } this.owner = owner; this.headers = this.owner.headers; - if (!this.owner) { - console.error("localuser was not included, please fix"); - } this.channels = []; this.channelids = {}; this.id = JSON.id; @@ -74,6 +72,7 @@ class Guild { this.roles.push(roleh); this.roleids[roleh.id] = roleh; } + Member.resolve(member, this).then(_ => this.member = _); for (const thing of JSON.channels) { const temp = new Channel(thing, this); this.channels.push(temp); @@ -353,19 +352,10 @@ class Guild { body: JSON.stringify(build) }); } - fillMember(member) { - const realroles = []; - for (const thing of member.roles) { - realroles.push(this.getRole(thing)); - } - member.roles = realroles; - return member; - } - giveMember(member) { - this.fillMember(member); - this.member = member; - } getRole(ID) { + if (!this.roleids[ID]) { + console.error(`role id ${ID} does not exist`, this.roleids); + } return this.roleids[ID]; } hasRole(r) { diff --git a/.dist/index.js b/.dist/index.js index e20cb84..1b98373 100644 --- a/.dist/index.js +++ b/.dist/index.js @@ -128,11 +128,12 @@ async function enter(event) { channel.editing = null; } else { + replyingto = thisuser.channelfocus.replyingto; let replying = replyingto?.all; if (replyingto) { replyingto.classList.remove("replying"); } - replyingto = false; + thisuser.channelfocus.replyingto = null; channel.sendMessage(typebox.value, { attachments: images, replyingto: replying, diff --git a/.dist/localuser.js b/.dist/localuser.js index f708502..1973ac3 100644 --- a/.dist/localuser.js +++ b/.dist/localuser.js @@ -2,7 +2,6 @@ import { Guild } from "./guild.js"; import { Direct } from "./direct.js"; import { Voice } from "./audio.js"; import { User } from "./user.js"; -import { Member } from "./member.js"; import { markdown } from "./markdown.js"; import { Fullscreen } from "./fullscreen.js"; import { setTheme } from "./login.js"; @@ -48,8 +47,12 @@ class Localuser { this.channelfocus = null; this.lookingguild = null; this.guildhtml = {}; + const members = {}; + for (const thing of ready.d.merged_members) { + members[thing[0].guild_id] = thing[0]; + } for (const thing of ready.d.guilds) { - const temp = new Guild(thing, this); + const temp = new Guild(thing, this, members[thing.id]); this.guilds.push(temp); this.guildids[temp.id] = temp; } @@ -62,11 +65,6 @@ class Localuser { for (const thing of ready.d.user_guild_settings.entries) { this.guildids[thing.guild_id].notisetting(thing); } - for (const thing of ready.d.merged_members) { - const guild = this.guildids[thing[0].guild_id]; - const temp = new Member(thing[0], guild); - guild.giveMember(temp); - } for (const thing of ready.d.read_state.entries) { const guild = this.resolveChannelFromID(thing.id).guild; if (guild === undefined) { @@ -130,6 +128,10 @@ class Localuser { this.messageCreate(temp); } break; + case "MESSAGE_DELETE": + console.log(temp.d); + this.guildids[temp.d.guild_id].channelids[temp.d.channel_id].messageids[temp.d.id].deleteEvent(); + break; case "READY": this.gottenReady(temp); this.genusersettings(); @@ -193,7 +195,7 @@ class Localuser { } case "GUILD_CREATE": { - const guildy = new Guild(temp.d, this); + const guildy = new Guild(temp.d, this, this.user); this.guilds.push(guildy); this.guildids[guildy.id] = guildy; document.getElementById("servers").insertBefore(guildy.generateGuildIcon(), document.getElementById("bottomseperator")); @@ -236,7 +238,6 @@ class Localuser { return; } resolveChannelFromID(ID) { - console.log(this.guilds.find(guild => guild.channelids[ID]).channelids); let resolve = this.guilds.find(guild => guild.channelids[ID]).channelids[ID]; resolve ??= undefined; return resolve; diff --git a/.dist/member.js b/.dist/member.js index bd5c19a..74a9c69 100644 --- a/.dist/member.js +++ b/.dist/member.js @@ -1,26 +1,44 @@ import { User } from "./user.js"; +import { Guild } from "./guild.js"; class Member { static already = {}; owner; user; roles; - constructor(memberjson, owner) { - if (!owner) { - console.error("Guild not included in the creation of a member object"); - } + error; + constructor(memberjson, owner, error = false) { + this.error = error; this.owner = owner; let membery = memberjson; - if (memberjson.guild_member) { - membery = memberjson.guild_member; - this.user = memberjson.user; + this.roles = []; + if (!error) { + if (memberjson.guild_member) { + membery = memberjson.guild_member; + this.user = memberjson.user; + } } for (const thing of Object.keys(membery)) { if (thing === "guild") { continue; } + if (thing === "owner") { + continue; + } + if (thing === "roles") { + for (const strrole of membery["roles"]) { + const role = this.guild.getRole(strrole); + this.roles.push(role); + } + continue; + } this[thing] = membery[thing]; } - this.user = new User(this.user, owner.localuser); + if (error) { + this.user = memberjson; + } + else { + this.user = new User(this.user, owner.localuser); + } } get guild() { return this.owner; @@ -31,7 +49,17 @@ class Member { get info() { return this.owner.info; } - static async resolve(user, guild) { + static async resolve(unkown, guild) { + if (!(guild instanceof Guild)) { + console.error(guild); + } + let user; + if (unkown instanceof User) { + user = unkown; + } + else { + return new Member(unkown, guild); + } if (guild.id === "@me") { return null; } @@ -48,12 +76,18 @@ class Member { const promoise = fetch(guild.info.api.toString() + "/v9/users/" + user.id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.id, { headers: guild.headers }).then(_ => _.json()).then(json => { const memb = new Member(json, guild); Member.already[guild.id][user.id] = memb; - guild.fillMember(memb); console.log("resolved"); return memb; }); Member.already[guild.id][user.id] = promoise; - return await promoise; + try { + return await promoise; + } + catch (_) { + const memb = new Member(user, guild, true); + Member.already[guild.id][user.id] = memb; + return memb; + } } hasRole(ID) { console.log(this.roles, ID); @@ -74,6 +108,11 @@ class Member { return ""; } isAdmin() { + for (const role of this.roles) { + if (role.permissions.getPermision("ADMINISTRATOR")) { + return true; + } + } return this.guild.properties.owner_id === this.user.id; } } diff --git a/.dist/message.js b/.dist/message.js index a62498a..f7560d7 100644 --- a/.dist/message.js +++ b/.dist/message.js @@ -12,12 +12,25 @@ class Message { author; mentions; mention_roles; - attachments; + attachments; //probably should be its own class tbh, should be Attachments[] id; message_reference; type; timestamp; content; + static del; + static resolve; + div; + static setup() { + this.del = new Promise(_ => { this.resolve = _; }); + Message.setupcmenu(); + } + static async wipeChanel() { + this.resolve(); + document.getElementById("messages").innerHTML = ""; + await Promise.allSettled([this.resolve]); + this.del = new Promise(_ => { this.resolve = _; }); + } static setupcmenu() { Message.contextmenu.addbutton("Copy raw text", function () { navigator.clipboard.writeText(this.content); @@ -46,6 +59,9 @@ class Message { this.channel.editing = this; document.getElementById("typebox").value = this.content; }, null, _ => { return _.author.id === _.localuser.user.id; }); + Message.contextmenu.addbutton("Delete message", function () { + this.delete(); + }, null, _ => { return _.canDelete(); }); } constructor(messagejson, owner) { this.owner = owner; @@ -68,6 +84,9 @@ class Message { console.log(this); } } + canDelete() { + return this.channel.hasPermission("MANAGE_MESSAGES") || this.author.id === this.localuser.user.id; + } get channel() { return this.owner; } @@ -81,7 +100,12 @@ class Message { return this.owner.info; } messageevents(obj) { - Message.contextmenu.bind(obj, this); + const func = Message.contextmenu.bind(obj, this); + this.div = obj; + Message.del.then(_ => { + obj.removeEventListener("click", func); + this.div = null; + }); obj.classList.add("messagediv"); } mentionsuser(userd) { @@ -108,10 +132,32 @@ class Message { body: JSON.stringify({ content: content }) }); } - buildhtml(premessage) { - //premessage??=messages.lastChild; + delete() { + fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`, { + headers: this.headers, + method: "DELETE", + }); + } + deleteEvent() { + if (this.div) { + this.div.innerHTML = ""; + this.div = null; + } + const index = this.channel.messages.indexOf(this); + this.channel.messages.splice(this.channel.messages.indexOf(this), 1); + delete this.channel.messageids[this.id]; + const regen = this.channel.messages[index - 1]; + if (regen) { + regen.generateMessage(); + } + } + generateMessage(premessage = null) { + if (!premessage) { + premessage = this.channel.messages[this.channel.messages.indexOf(this) + 1]; + } + const div = this.div; + div.innerHTML = ""; const build = document.createElement('table'); - const div = document.createElement("div"); if (this.message_reference) { const replyline = document.createElement("div"); const line = document.createElement("hr"); @@ -124,7 +170,23 @@ class Message { const reply = document.createElement("div"); username.classList.add("username"); Member.resolve(this.author, this.guild).then(_ => { + if (!_) { + return; + } + ; + console.log(_.error); + if (_.error) { + username.textContent += "Error"; + alert("Should've gotten here"); + const error = document.createElement("span"); + error.textContent = "!"; + error.classList.add("membererror"); + username.after(error); + return; + } username.style.color = _.getColor(); + }).catch(_ => { + console.log(_); }); reply.classList.add("replytext"); replyline.appendChild(reply); @@ -150,7 +212,6 @@ class Message { const pfpRow = document.createElement('th'); let pfpparent, current; if (premessage != null) { - pfpparent = premessage.pfpparent; pfpparent ??= premessage; let pfpparent2 = pfpparent.all; pfpparent2 ??= pfpparent; @@ -158,7 +219,7 @@ class Message { const newt = (new Date(this.timestamp).getTime()) / 1000; current = (newt - old) > 600; } - const combine = (premessage?.userid != this.author.id && premessage?.author?.id != this.author.id) || (current) || this.message_reference; + const combine = (premessage?.author?.id != this.author.id) || (current) || this.message_reference; if (combine) { const pfp = this.author.buildpfp(); this.author.profileclick(pfp); @@ -181,6 +242,14 @@ class Message { if (!_) { return; } + ; + if (_.error) { + const error = document.createElement("span"); + error.textContent = "!"; + error.classList.add("membererror"); + username.after(error); + return; + } username.style.color = _.getColor(); }); username.textContent = this.author.username; @@ -254,10 +323,19 @@ class Message { messagedwrap.append(time); texttxt.appendChild(messagedwrap); } - div["userid"] = this.author.id; div["all"] = this; return (div); } + buildhtml(premessage) { + if (this.div) { + console.error(`HTML for ${this} already exists, aborting`); + return; + } + //premessage??=messages.lastChild; + const div = document.createElement("div"); + this.div = div; + return this.generateMessage(premessage); + } createunknown(fname, fsize, src) { const div = document.createElement("table"); div.classList.add("unknownfile"); @@ -314,5 +392,5 @@ function formatTime(date) { return `${date.toLocaleDateString()} at ${formatTime(date)}`; } } -Message.setupcmenu(); +Message.setup(); export { Message }; diff --git a/webpage/channel.ts b/webpage/channel.ts index 848c0cd..86e742b 100644 --- a/webpage/channel.ts +++ b/webpage/channel.ts @@ -7,6 +7,7 @@ import {markdown} from "./markdown.js"; import {Guild} from "./guild.js"; import { Localuser } from "./localuser.js"; import { Permissions } from "./permissions.js"; + declare global { interface NotificationOptions { image?: string @@ -38,6 +39,7 @@ class Channel{ message_notifications:number; allthewayup:boolean; static contextmenu=new Contextmenu("channel menu"); + replyingto:HTMLDivElement; static setupcontextmenu(){ Channel.contextmenu.addbutton("Copy channel id",function(){ console.log(this) @@ -79,7 +81,6 @@ class Channel{ for(const thing of JSON.permission_overwrites){ this.permission_overwrites[thing.id]=new Permissions(thing.allow,thing.deny); } - console.log(this.permission_overwrites) this.topic=JSON.topic; this.nsfw=JSON.nsfw; this.position=JSON.position; @@ -104,21 +105,29 @@ class Channel{ this.mentions??=0; this.lastpin=json.last_pin_timestamp; } - get hasunreads(){ + get hasunreads():boolean{ + if(!this.hasPermission("VIEW_CHANNEL")){return false;} return this.lastmessageid!==this.lastreadmessageid&&this.type!==4; } - get canMessage(){ - console.log("this should run"); - for(const thing of Object.entries(this.permission_overwrites)){ - const perm=thing[1].getPermision("SEND_MESSAGES"); - if(perm===1){ - return true + hasPermission(name:string,member=this.guild.member):boolean{ + if(member.isAdmin()){ + return true; + } + for(const thing of member.roles){ + if(this.permission_overwrites[thing.id]){ + let perm=this.permission_overwrites[thing.id].getPermision(name); + if(perm){ + return perm===1; + } } - if(perm===-1){ - return false; + if(thing.permissions.getPermision(name)){ + return true; } } - return true; + return false; + } + get canMessage():boolean{ + return this.hasPermission("SEND_MESSAGES"); } sortchildren(){ this.children.sort((a,b)=>{return a.position-b.position}); @@ -153,8 +162,19 @@ class Channel{ return build; } static dragged=[]; - createguildHTML(admin=false){ + createguildHTML(admin=false):HTMLDivElement{ const div=document.createElement("div"); + if(!this.hasPermission("VIEW_CHANNEL")){ + let quit=true + for(const thing of this.children){ + if(thing.hasPermission("VIEW_CHANNEL")){ + quit=false; + } + } + if(quit){ + return div; + } + } div["all"]=this; div.draggable=admin; div.addEventListener("dragstart",(e)=>{Channel.dragged=[this,div];e.stopImmediatePropagation()}) @@ -392,34 +412,35 @@ class Channel{ headers:this.headers }) } - getHTML(){ + async getHTML(){ if(this.guild!==this.localuser.lookingguild){ this.guild.loadGuild(); } this.guild.prevchannel=this; this.localuser.channelfocus=this; - this.putmessages(); + const prom=Message.wipeChanel(); + await this.putmessages(); + await prom; + this.buildmessages(); history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.id); document.getElementById("channelname").textContent="#"+this.name; console.log(this); (document.getElementById("typebox") as HTMLInputElement).disabled=!this.canMessage; } - putmessages(){ - const out=this; - fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=100",{ + async putmessages(){ + if(this.messages.length>=100){return}; + const j=await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=100",{ method: 'GET', headers: this.headers, - }).then((j)=>{return j.json()}).then(responce=>{ - document.getElementById("messages").innerHTML = ''; - for(const thing of responce){ - const messager=new Message(thing,this) - if(out.messageids[messager.id]==undefined){ - out.messageids[messager.id]=messager; - out.messages.push(messager); - } - } - out.buildmessages(); }) + const responce=await j.json(); + for(const thing of responce){ + const messager=new Message(thing,this) + if(this.messageids[messager.id]===undefined){ + this.messageids[messager.id]=messager; + this.messages.push(messager); + } + } } delChannel(JSON){ const build=[]; @@ -569,6 +590,7 @@ class Channel{ } } messageCreate(messagep:any):void{ + if(!this.hasPermission("VIEW_CHANNEL")){return} const messagez=new Message(messagep.d,this); this.lastmessageid=messagez.id; if(messagez.author===this.localuser.user){ diff --git a/webpage/contextmenu.ts b/webpage/contextmenu.ts index 279549b..e4ece80 100644 --- a/webpage/contextmenu.ts +++ b/webpage/contextmenu.ts @@ -50,11 +50,16 @@ class Contextmenu{ return this.div; } bind(obj:HTMLElement,addinfo:any=undefined){ - obj.addEventListener("contextmenu", (event) => { + const func=(event) => { event.preventDefault(); event.stopImmediatePropagation(); this.makemenu(event.clientX,event.clientY,addinfo,obj) - }); + } + obj.addEventListener("contextmenu", func); + return func; + } + static keepOnScreen(obj:HTMLElement){ + } } Contextmenu.setup(); diff --git a/webpage/direct.ts b/webpage/direct.ts index f4a5743..98f17b9 100644 --- a/webpage/direct.ts +++ b/webpage/direct.ts @@ -3,10 +3,11 @@ import { Channel } from "./channel.js"; import { Message } from "./message.js"; import { Localuser } from "./localuser.js"; import {User} from "./user.js"; +import { Member } from "./member.js"; class Direct extends Guild{ constructor(JSON,owner:Localuser){ - super(-1,owner); + super(-1,owner,null); this.message_notifications=0; console.log(JSON); this.owner=owner; @@ -98,10 +99,16 @@ class Group extends Channel{ } return div; } - getHTML(){ + async getHTML(){ + if(this.guild!==this.localuser.lookingguild){ + this.guild.loadGuild(); + } + const prom=Message.wipeChanel(); this.guild.prevchannel=this; this.localuser.channelfocus=this; - this.putmessages(); + await this.putmessages(); + await prom; + this.buildmessages(); history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.id); document.getElementById("channelname").textContent="@"+this.name; } @@ -183,5 +190,11 @@ class Group extends Channel{ } } + isAdmin(): boolean { + return false; + } + hasPermission(name: string, member?: Member): boolean { + return true; + } } export {Direct, Group}; diff --git a/webpage/guild.ts b/webpage/guild.ts index ad6650f..ff92329 100644 --- a/webpage/guild.ts +++ b/webpage/guild.ts @@ -15,7 +15,7 @@ class Guild{ roles:Role[]; roleids:{[key:string]:Role}; prevchannel:Channel; - message_notifications + message_notifications:number; headchannels:Channel[]; position:number; parent_id:string; @@ -60,16 +60,13 @@ class Guild{ },null,_=>{return thisuser.isAdmin()}) */ } - constructor(JSON,owner:Localuser){ + constructor(JSON,owner:Localuser,member){ if(JSON===-1){ return; } this.owner=owner; this.headers=this.owner.headers; - if(!this.owner){ - console.error("localuser was not included, please fix") - } this.channels=[]; this.channelids={}; this.id=JSON.id; @@ -83,6 +80,7 @@ class Guild{ this.roles.push(roleh) this.roleids[roleh.id]=roleh; } + Member.resolve(member,this).then(_=>this.member=_); for(const thing of JSON.channels){ const temp=new Channel(thing,this); this.channels.push(temp); @@ -360,19 +358,8 @@ class Guild{ body:JSON.stringify(build) }) } - fillMember(member:Member){ - const realroles=[]; - for(const thing of member.roles){ - realroles.push(this.getRole(thing)); - } - member.roles=realroles; - return member; - } - giveMember(member:Member){ - this.fillMember(member); - this.member=member; - } - getRole(ID){ + getRole(ID:string):Role{ + if(!this.roleids[ID]){console.error(`role id ${ID} does not exist`,this.roleids)} return this.roleids[ID]; } hasRole(r:Role|string){ @@ -382,7 +369,7 @@ class Guild{ } return this.member.hasRole(r as string); } - loadChannel(ID=undefined){ + loadChannel(ID:string=undefined){ if(ID&&this.channelids[ID]){ this.channelids[ID].getHTML(); return; diff --git a/webpage/index.ts b/webpage/index.ts index b04a756..996392d 100644 --- a/webpage/index.ts +++ b/webpage/index.ts @@ -142,11 +142,12 @@ async function enter(event){ channel.editing.edit((typebox).value); channel.editing=null; }else{ + replyingto= thisuser.channelfocus.replyingto; let replying=replyingto?.all; if(replyingto){ replyingto.classList.remove("replying"); } - replyingto=false; + thisuser.channelfocus.replyingto=null; channel.sendMessage(typebox.value,{ attachments:images, replyingto:replying, diff --git a/webpage/localuser.ts b/webpage/localuser.ts index 9c7cc8a..83983df 100644 --- a/webpage/localuser.ts +++ b/webpage/localuser.ts @@ -49,8 +49,13 @@ class Localuser{ this.channelfocus=null; this.lookingguild=null; this.guildhtml={}; + const members={}; + for(const thing of ready.d.merged_members){ + members[thing[0].guild_id]=thing[0]; + } + for(const thing of ready.d.guilds){ - const temp=new Guild(thing,this); + const temp=new Guild(thing,this,members[thing.id]); this.guilds.push(temp); this.guildids[temp.id]=temp; } @@ -59,15 +64,13 @@ class Localuser{ this.guilds.push(temp); this.guildids[temp.id]=temp; } - console.log(ready.d.user_guild_settings.entries) + console.log(ready.d.user_guild_settings.entries); + + for(const thing of ready.d.user_guild_settings.entries){ this.guildids[thing.guild_id].notisetting(thing); } - for(const thing of ready.d.merged_members){ - const guild=this.guildids[thing[0].guild_id] - const temp=new Member(thing[0],guild); - guild.giveMember(temp); - } + for(const thing of ready.d.read_state.entries){ const guild=this.resolveChannelFromID(thing.id).guild; if(guild===undefined){ @@ -134,6 +137,10 @@ class Localuser{ this.messageCreate(temp); } break; + case "MESSAGE_DELETE": + console.log(temp.d); + this.guildids[temp.d.guild_id].channelids[temp.d.channel_id].messageids[temp.d.id].deleteEvent(); + break; case "READY": this.gottenReady(temp); this.genusersettings(); @@ -196,7 +203,7 @@ class Localuser{ } case "GUILD_CREATE": { - const guildy=new Guild(temp.d,this); + const guildy=new Guild(temp.d,this,this.user); this.guilds.push(guildy); this.guildids[guildy.id]=guildy; document.getElementById("servers").insertBefore(guildy.generateGuildIcon(),document.getElementById("bottomseperator")); @@ -239,7 +246,6 @@ class Localuser{ return; } resolveChannelFromID(ID:string):Channel{ - console.log(this.guilds.find(guild => guild.channelids[ID]).channelids) let resolve=this.guilds.find(guild => guild.channelids[ID]).channelids[ID]; resolve??=undefined; return resolve; diff --git a/webpage/member.ts b/webpage/member.ts index c1c4cb7..6b5a3c4 100644 --- a/webpage/member.ts +++ b/webpage/member.ts @@ -6,19 +6,35 @@ class Member{ owner:Guild; user:User; roles:Role[]; - constructor(memberjson,owner:Guild){ - if(!owner){console.error("Guild not included in the creation of a member object")} + error:boolean; + constructor(memberjson,owner:Guild,error=false){ + this.error=error; this.owner=owner; let membery=memberjson; - if(memberjson.guild_member){ - membery=memberjson.guild_member; - this.user=memberjson.user; + this.roles=[]; + if(!error){ + if(memberjson.guild_member){ + membery=memberjson.guild_member; + this.user=memberjson.user; + } } for(const thing of Object.keys(membery)){ if(thing==="guild"){continue} + if(thing==="owner"){continue} + if(thing==="roles"){ + for(const strrole of membery["roles"]){ + const role=this.guild.getRole(strrole); + this.roles.push(role); + } + continue; + } this[thing]=membery[thing]; } - this.user=new User(this.user,owner.localuser); + if(error){ + this.user=memberjson as User; + }else{ + this.user=new User(this.user,owner.localuser); + } } get guild(){ return this.owner; @@ -29,7 +45,16 @@ class Member{ get info(){ return this.owner.info; } - static async resolve(user:User,guild:Guild){ + static async resolve(unkown:User|object,guild:Guild):Promise{ + if(!(guild instanceof Guild)){ + console.error(guild) + } + let user:User; + if(unkown instanceof User){ + user=unkown as User; + }else{ + return new Member(unkown,guild); + } if(guild.id==="@me"){return null} if(!Member.already[guild.id]){ Member.already[guild.id]={}; @@ -43,12 +68,17 @@ class Member{ const promoise= fetch(guild.info.api.toString()+"/v9/users/"+user.id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.id,{headers:guild.headers}).then(_=>_.json()).then(json=>{ const memb=new Member(json,guild); Member.already[guild.id][user.id]=memb; - guild.fillMember(memb); console.log("resolved") return memb - }); + }) Member.already[guild.id][user.id]=promoise; - return await promoise; + try{ + return await promoise + }catch(_){ + const memb=new Member(user,guild,true); + Member.already[guild.id][user.id]=memb; + return memb; + } } hasRole(ID:string){ console.log(this.roles,ID); @@ -69,6 +99,11 @@ class Member{ return ""; } isAdmin(){ + for(const role of this.roles){ + if(role.permissions.getPermision("ADMINISTRATOR")){ + return true; + } + } return this.guild.properties.owner_id===this.user.id; } diff --git a/webpage/message.ts b/webpage/message.ts index aaa5e88..c4aab7b 100644 --- a/webpage/message.ts +++ b/webpage/message.ts @@ -16,12 +16,25 @@ class Message{ author:User; mentions:User[]; mention_roles:Role[]; - attachments; + attachments;//probably should be its own class tbh, should be Attachments[] id:string; message_reference; type:number; timestamp:number; - content; + content:string; + static del:Promise; + static resolve:Function; + div:HTMLDivElement; + static setup(){ + this.del=new Promise(_=>{this.resolve=_}); + Message.setupcmenu(); + } + static async wipeChanel(){ + this.resolve(); + document.getElementById("messages").innerHTML=""; + await Promise.allSettled([this.resolve]); + this.del=new Promise(_=>{this.resolve=_}) + } static setupcmenu(){ Message.contextmenu.addbutton("Copy raw text",function(){ navigator.clipboard.writeText(this.content); @@ -51,8 +64,11 @@ class Message{ this.channel.editing=this; (document.getElementById("typebox") as HTMLInputElement).value=this.content; },null,_=>{return _.author.id===_.localuser.user.id}); + Message.contextmenu.addbutton("Delete message",function(){ + this.delete(); + },null,_=>{return _.canDelete()}) } - constructor(messagejson,owner){ + constructor(messagejson,owner:Channel){ this.owner=owner; this.headers=this.owner.headers; for(const thing of Object.keys(messagejson)){ @@ -73,6 +89,9 @@ class Message{ console.log(this); } } + canDelete(){ + return this.channel.hasPermission("MANAGE_MESSAGES")||this.author.id===this.localuser.user.id; + } get channel(){ return this.owner; } @@ -85,11 +104,16 @@ class Message{ get info(){ return this.owner.info; } - messageevents(obj){ - Message.contextmenu.bind(obj,this) - obj.classList.add("messagediv") + messageevents(obj:HTMLDivElement){ + const func=Message.contextmenu.bind(obj,this); + this.div=obj; + Message.del.then(_=>{ + obj.removeEventListener("click",func); + this.div=null; + }) + obj.classList.add("messagediv"); } - mentionsuser(userd){ + mentionsuser(userd:User|Member){ if(userd instanceof User){ return this.mentions.includes(userd); }else if(userd instanceof Member){ @@ -112,11 +136,32 @@ class Message{ body:JSON.stringify({content:content}) }); } - buildhtml(premessage){ - //premessage??=messages.lastChild; + delete(){ + fetch(`${this.info.api.toString()}/channels/${this.channel.id}/messages/${this.id}`,{ + headers:this.headers, + method:"DELETE", + }) + } + deleteEvent(){ + if(this.div){ + this.div.innerHTML=""; + this.div=null; + } + const index=this.channel.messages.indexOf(this); + this.channel.messages.splice(this.channel.messages.indexOf(this),1); + delete this.channel.messageids[this.id]; + const regen=this.channel.messages[index-1] + if(regen){ + regen.generateMessage(); + } + } + generateMessage(premessage:Message=null){ + if(!premessage){ + premessage=this.channel.messages[this.channel.messages.indexOf(this)+1]; + } + const div=this.div; + div.innerHTML=""; const build = document.createElement('table'); - const div=document.createElement("div"); - if(this.message_reference){ const replyline=document.createElement("div"); const line=document.createElement("hr"); @@ -130,7 +175,21 @@ class Message{ username.classList.add("username"); Member.resolve(this.author,this.guild).then(_=>{ + if(!_) {return}; + console.log(_.error); + if(_.error){ + username.textContent+="Error"; + alert("Should've gotten here") + const error=document.createElement("span"); + error.textContent="!"; + error.classList.add("membererror"); + username.after(error); + + return; + } username.style.color=_.getColor(); + }).catch(_=>{ + console.log(_) }); reply.classList.add("replytext"); @@ -161,7 +220,6 @@ class Message{ let pfpparent, current if(premessage!=null){ - pfpparent=premessage.pfpparent; pfpparent??=premessage; let pfpparent2=pfpparent.all; pfpparent2??=pfpparent; @@ -169,7 +227,7 @@ class Message{ const newt=(new Date(this.timestamp).getTime())/1000; current=(newt-old)>600; } - const combine=(premessage?.userid!=this.author.id&&premessage?.author?.id!=this.author.id)||(current)||this.message_reference + const combine=(premessage?.author?.id!=this.author.id)||(current)||this.message_reference if(combine){ const pfp=this.author.buildpfp(); this.author.profileclick(pfp); @@ -188,8 +246,16 @@ class Message{ const username=document.createElement("span"); username.classList.add("username") this.author.profileclick(username); - Member.resolve(this.author,this.guild).then(_=>{ - if(!_){return} + Member.resolve(this.author,this.guild).then(_=>{ + if(!_) {return}; + if(_.error){ + const error=document.createElement("span"); + error.textContent="!"; + error.classList.add("membererror"); + username.after(error); + + return; + } username.style.color=_.getColor(); }) username.textContent=this.author.username; @@ -266,10 +332,16 @@ class Message{ texttxt.appendChild(messagedwrap) } - div["userid"]=this.author.id; div["all"]=this; return(div) } + buildhtml(premessage:Message){ + if(this.div){console.error(`HTML for ${this} already exists, aborting`);return;} + //premessage??=messages.lastChild; + const div=document.createElement("div"); + this.div=div; + return this.generateMessage(premessage); + } createunknown(fname,fsize,src){ const div=document.createElement("table"); div.classList.add("unknownfile"); @@ -328,5 +400,5 @@ function formatTime(date) { return `${date.toLocaleDateString()} at ${formatTime(date)}`; } } -Message.setupcmenu(); +Message.setup(); export { Message }; diff --git a/webpage/style.css b/webpage/style.css index 45c7622..2d64f7b 100644 --- a/webpage/style.css +++ b/webpage/style.css @@ -138,7 +138,7 @@ h2 { .pfp { border-radius: 50%; - width: .5in; + width: 0.5in; height: .5in; user-select: none; cursor: pointer; @@ -599,9 +599,12 @@ textarea { #settings { cursor: pointer; user-select: none; - border-radius: .1in; + border-radius: .3in; transition: background 1s; text-align: center; + font-size: .25in; + width: .3in; + height: .3in; } #settings:hover { @@ -975,3 +978,13 @@ span { .spaceright{ margin-right:.1in; } +.membererror{ + display:inline-block; + background:#656500; + height:.15in; + width:.15in; + border-radius:.1in; + text-align:center; + border:solid black .03in; + margin-left:.025in; +} \ No newline at end of file