import {Contextmenu} from "./contextmenu.js"; import {User} from "./user.js"; import {Member} from "./member.js"; import {MarkDown} from "./markdown.js"; import {Embed} from "./embed.js"; import { Channel } from "./channel.js"; import {Localuser} from "./localuser.js"; import { Role } from "./role.js"; import {File} from "./file.js"; class Message{ static contextmenu=new Contextmenu("message menu"); owner:Channel; headers:Localuser["headers"]; embeds:Embed[]; author:User; mentions:User[]; mention_roles:Role[]; attachments:File[];//probably should be its own class tbh, should be Attachments[] id:string; message_reference; type:number; timestamp:number; content:MarkDown; 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.rawString); }); Message.contextmenu.addbutton("Reply",function(this:Message,div:HTMLDivElement){ this.channel.setReplying(this); }); Message.contextmenu.addbutton("Copy message id",function(){ navigator.clipboard.writeText(this.id); }); Message.contextmenu.addbutton("Edit",function(){ 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:Channel){ this.owner=owner; this.headers=this.owner.headers; this.giveData(messagejson); } giveData(messagejson){ for(const thing of Object.keys(messagejson)){ if(thing==="attachments"){ this.attachments=[]; for(const thing of messagejson.attachments){ this.attachments.push(new File(thing,this)); } continue; }else if(thing==="content"){ this.content=new MarkDown(messagejson[thing],this.channel); continue; } this[thing]=messagejson[thing]; } for(const thing in this.embeds){ console.log(thing,this.embeds) this.embeds[thing]=new Embed(this.embeds[thing],this); } this.author=new User(this.author,this.localuser); for(const thing in this.mentions){ this.mentions[thing]=new User(this.mentions[thing],this.localuser); } if(this.mentions.length||this.mention_roles.length){//currently mention_roles isn't implemented on the spacebar servers console.log(this.mentions,this.mention_roles) } if(this.mentionsuser(this.localuser.user)){ console.log(this); } if(this.div){ this.generateMessage(); } } canDelete(){ return this.channel.hasPermission("MANAGE_MESSAGES")||this.author.id===this.localuser.user.id; } get channel(){ return this.owner; } get guild(){ return this.owner.guild; } get localuser(){ return this.owner.localuser; } get info(){ return this.owner.info; } messageevents(obj:HTMLDivElement,del=Message.del){ const func=Message.contextmenu.bind(obj,this); this.div=obj; del.then(_=>{ obj.removeEventListener("click",func); this.div.remove(); this.div=null; }) obj.classList.add("messagediv"); } mentionsuser(userd:User|Member){ if(userd instanceof User){ return this.mentions.includes(userd); }else if(userd instanceof Member){ return this.mentions.includes(userd.user); } } getimages(){ const build=[]; for(const thing of this.attachments){ if(thing.content_type.startsWith('image/')){ build.push(thing); } } return build; } async edit(content){ return await fetch(this.info.api.toString()+"/channels/"+this.channel.id+"/messages/"+this.id,{ method: "PATCH", headers: this.headers, body:JSON.stringify({content:content}) }); } 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 prev=this.channel.idToPrev[this.id]; const next=this.channel.idToNext[this.id]; this.channel.idToNext[prev]=next; this.channel.idToPrev[next]=prev; delete this.channel.messageids[this.id]; const regen=this.channel.messageids[prev] if(regen){ regen.generateMessage(); } } generateMessage(premessage:Message=null){ if(!premessage){ premessage=this.channel.messageids[this.channel.idToNext[this.id]]; } const div=this.div; if(this===this.channel.replyingto){ div.classList.add("replying"); } div.innerHTML=""; const build = document.createElement('div'); build.classList.add("flexltr"); if(this.message_reference){ const replyline=document.createElement("div"); const line=document.createElement("hr"); const minipfp=document.createElement("img") minipfp.classList.add("replypfp"); replyline.appendChild(line); replyline.appendChild(minipfp); const username=document.createElement("span"); replyline.appendChild(username); const reply=document.createElement("div"); username.classList.add("username"); this.author.bind(username,this.guild); /* 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); const line2=document.createElement("hr"); replyline.appendChild(line2); line2.classList.add("reply"); line.classList.add("startreply"); replyline.classList.add("replyflex") this.channel.getmessage(this.message_reference.message_id).then(message=>{ const author=message.author; reply.appendChild(message.content.makeHTML({stdsize:true})); minipfp.src=author.getpfpsrc() author.bind(minipfp); username.textContent=author.username; author.bind(username); }); div.appendChild(replyline); } build.classList.add("message"); div.appendChild(build); if({0:true,19:true}[this.type]||this.attachments.length!==0){ const pfpRow = document.createElement('div'); pfpRow.classList.add("flexltr"); let pfpparent, current if(premessage!=null){ pfpparent??=premessage; let pfpparent2=pfpparent.all; pfpparent2??=pfpparent; const old=(new Date(pfpparent2.timestamp).getTime())/1000; const newt=(new Date(this.timestamp).getTime())/1000; current=(newt-old)>600; } const combine=(premessage?.author?.id!=this.author.id)||(current)||this.message_reference if(combine){ const pfp=this.author.buildpfp(); this.author.bind(pfp); pfpRow.appendChild(pfp); }else{ div["pfpparent"]=pfpparent; } pfpRow.classList.add("pfprow") build.appendChild(pfpRow); const text=document.createElement("div"); text.classList.add("flexttb") const texttxt=document.createElement("div"); texttxt.classList.add("commentrow","flexttb"); text.appendChild(texttxt); if(combine){ const username=document.createElement("span"); username.classList.add("username") this.author.bind(username,this.guild); div.classList.add("topMessage"); username.textContent=this.author.username; const userwrap=document.createElement("div"); userwrap.classList.add("flexltr"); userwrap.appendChild(username) if(this.author.bot){ const username=document.createElement("span"); username.classList.add("bot") username.textContent="BOT"; userwrap.appendChild(username) } const time=document.createElement("span"); time.textContent=" "+formatTime(new Date(this.timestamp)); time.classList.add("timestamp") userwrap.appendChild(time); texttxt.appendChild(userwrap) }else{ div.classList.remove("topMessage"); } const messaged=this.content.makeHTML(); div["txt"]=messaged; const messagedwrap=document.createElement("div"); messagedwrap.classList.add("flexttb") messagedwrap.appendChild(messaged) texttxt.appendChild(messagedwrap) build.appendChild(text) if(this.attachments.length){ console.log(this.attachments) const attach = document.createElement("div"); attach.classList.add("flexltr"); for(const thing of this.attachments){ attach.appendChild(thing.getHTML()) } messagedwrap.appendChild(attach) } if(this.embeds.length){ const embeds = document.createElement("div") embeds.classList.add("flexltr"); for(const thing of this.embeds){ embeds.appendChild(thing.generateHTML()); } messagedwrap.appendChild(embeds) } // }else if(this.type===7){ const text=document.createElement("div"); text.classList.add("flexttb") const texttxt=document.createElement("div"); text.appendChild(texttxt); build.appendChild(text); texttxt.classList.add("flexltr"); const messaged=document.createElement("p"); div["txt"]=messaged; messaged.textContent="welcome: "+this.author.username; texttxt.appendChild(messaged); const time=document.createElement("span"); time.textContent=" "+formatTime(new Date(this.timestamp)); time.classList.add("timestamp"); texttxt.append(time); div.classList.add("topMessage") } div["all"]=this; return(div) } buildhtml(premessage:Message,del:Promise=Message.del){ if(this.div){console.error(`HTML for ${this.id} already exists, aborting`);return;} //premessage??=messages.lastChild; const div=document.createElement("div"); this.div=div; this.messageevents(div,del); return this.generateMessage(premessage); } } function formatTime(date) { const now = new Date(); const sameDay = date.getDate() === now.getDate() && date.getMonth() === now.getMonth() && date.getFullYear() === now.getFullYear(); const yesterday = new Date(now); yesterday.setDate(now.getDate() - 1); const isYesterday = date.getDate() === yesterday.getDate() && date.getMonth() === yesterday.getMonth() && date.getFullYear() === yesterday.getFullYear(); const formatTime = date => date.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); if (sameDay) { return `Today at ${formatTime(date)}`; } else if (isYesterday) { return `Yesterday at ${formatTime(date)}`; } else { return `${date.toLocaleDateString()} at ${formatTime(date)}`; } } Message.setup(); export { Message };