1205 lines
32 KiB
TypeScript
1205 lines
32 KiB
TypeScript
"use strict";
|
|
import{ Message }from"./message.js";
|
|
import{Voice}from"./audio.js";
|
|
import{Contextmenu}from"./contextmenu.js";
|
|
import{Dialog}from"./dialog.js";
|
|
import{Guild}from"./guild.js";
|
|
import{ Localuser }from"./localuser.js";
|
|
import{ Permissions }from"./permissions.js";
|
|
import{ Settings }from"./settings.js";
|
|
import{ Role,RoleList }from"./role.js";
|
|
import{InfiniteScroller}from"./infiniteScroller.js";
|
|
import{ SnowFlake }from"./snowflake.js";
|
|
import{ channeljson, messageCreateJson, messagejson, readyjson }from"./jsontypes.js";
|
|
import{ MarkDown }from"./markdown.js";
|
|
import { Member } from "./member.js";
|
|
|
|
declare global {
|
|
interface NotificationOptions {
|
|
image?: string|null|undefined
|
|
}
|
|
}
|
|
class Channel extends SnowFlake{
|
|
editing:Message|null;
|
|
type:number;
|
|
owner:Guild;
|
|
headers:Localuser["headers"];
|
|
name:string;
|
|
parent_id?:string;
|
|
parent:Channel|undefined;
|
|
children:Channel[];
|
|
guild_id:string;
|
|
permission_overwrites:Map<string,Permissions>;
|
|
permission_overwritesar:[Role,Permissions][];
|
|
topic:string;
|
|
nsfw:boolean;
|
|
position:number=0;
|
|
lastreadmessageid:string|undefined;
|
|
lastmessageid:string|undefined;
|
|
mentions:number;
|
|
lastpin:string;
|
|
move_id?:string;
|
|
typing:number;
|
|
message_notifications:number;
|
|
allthewayup:boolean;
|
|
static contextmenu=new Contextmenu<Channel,undefined>("channel menu");
|
|
replyingto:Message|null;
|
|
infinite:InfiniteScroller;
|
|
idToPrev:Map<string,string>=new Map();
|
|
idToNext:Map<string,string>=new Map();
|
|
messages:Map<string,Message>=new Map();
|
|
static setupcontextmenu(){
|
|
this.contextmenu.addbutton("Copy channel id",function(this:Channel){
|
|
navigator.clipboard.writeText(this.id);
|
|
});
|
|
|
|
this.contextmenu.addbutton("Mark as read",function(this:Channel){
|
|
this.readbottom();
|
|
});
|
|
|
|
this.contextmenu.addbutton("Settings[temp]",function(this:Channel){
|
|
this.generateSettings();
|
|
});
|
|
|
|
this.contextmenu.addbutton("Delete channel",function(this:Channel){
|
|
this.deleteChannel();
|
|
},null,function(){
|
|
return this.isAdmin();
|
|
});
|
|
|
|
this.contextmenu.addbutton("Edit channel",function(this:Channel){
|
|
this.editChannel();
|
|
},null,function(){
|
|
return this.isAdmin();
|
|
});
|
|
|
|
this.contextmenu.addbutton("Make invite",function(this:Channel){
|
|
this.createInvite();
|
|
},null,function(){
|
|
return this.hasPermission("CREATE_INSTANT_INVITE")&&this.type!==4;
|
|
});
|
|
/*
|
|
this.contextmenu.addbutton("Test button",function(){
|
|
this.localuser.ws.send(JSON.stringify({
|
|
"op": 14,
|
|
"d": {
|
|
"guild_id": this.guild.id,
|
|
"channels": {
|
|
[this.id]: [
|
|
[
|
|
0,
|
|
99
|
|
]
|
|
]
|
|
}
|
|
}
|
|
}))
|
|
},null);
|
|
/**/
|
|
}
|
|
createInvite(){
|
|
const div=document.createElement("div");
|
|
div.classList.add("invitediv");
|
|
const text=document.createElement("span");
|
|
div.append(text);
|
|
let uses=0;
|
|
let expires=1800;
|
|
const copycontainer=document.createElement("div");
|
|
copycontainer.classList.add("copycontainer");
|
|
const copy=document.createElement("span");
|
|
copy.classList.add("copybutton","svgtheme","svg-copy");
|
|
copycontainer.append(copy);
|
|
copycontainer.onclick=_=>{
|
|
if(text.textContent){
|
|
navigator.clipboard.writeText(text.textContent);
|
|
}
|
|
};
|
|
div.append(copycontainer);
|
|
const update=()=>{
|
|
fetch(`${this.info.api}/channels/${this.id}/invites`,{
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
flags: 0,
|
|
target_type: null,
|
|
target_user_id: null,
|
|
max_age: expires+"",
|
|
max_uses: uses,
|
|
temporary: uses!==0
|
|
})
|
|
}).then(_=>_.json()).then(json=>{
|
|
const params=new URLSearchParams("");
|
|
params.set("instance",this.info.wellknown);
|
|
const encoded=params.toString();
|
|
text.textContent=`${location.origin}/invite/${json.code}?${encoded}`;
|
|
});
|
|
};
|
|
update();
|
|
new Dialog(["vdiv",
|
|
["title","Invite people"],
|
|
["text",`to #${this.name} in ${this.guild.properties.name}`],
|
|
["select","Expire after:",["30 Minutes","1 Hour","6 Hours","12 Hours","1 Day","7 Days","30 Days","Never"],function(e){
|
|
expires=[1800,3600,21600,43200,86400,604800,2592000,0][e.srcElement.selectedIndex];
|
|
update();
|
|
},0],
|
|
["select","Max uses:",["No limit","1 use","5 uses","10 uses","25 uses","50 uses","100 uses"],function(e){
|
|
uses=[0,1,5,10,25,50,100][e.srcElement.selectedIndex];
|
|
update();
|
|
},0],
|
|
["html",div]
|
|
]).show();
|
|
}
|
|
generateSettings(){
|
|
this.sortPerms();
|
|
const settings=new Settings("Settings for "+this.name);
|
|
|
|
const s1=settings.addButton("roles");
|
|
|
|
s1.options.push(new RoleList(this.permission_overwritesar,this.guild,this.updateRolePermissions.bind(this),true));
|
|
settings.show();
|
|
}
|
|
sortPerms(){
|
|
this.permission_overwritesar.sort((a,b)=>{
|
|
return this.guild.roles.findIndex(_=>_===a[0])-this.guild.roles.findIndex(_=>_===b[0]);
|
|
});
|
|
}
|
|
setUpInfiniteScroller(){
|
|
this.infinite=new InfiniteScroller((async (id:string,offset:number):Promise<string|undefined>=>{
|
|
if(offset===1){
|
|
if(this.idToPrev.has(id)){
|
|
return this.idToPrev.get(id);
|
|
}else{
|
|
await this.grabBefore(id);
|
|
return this.idToPrev.get(id);
|
|
}
|
|
}else{
|
|
if(this.idToNext.has(id)){
|
|
if(!this.messages.has(this.idToNext.get(id))){console.error("how did we get here?")}
|
|
return this.idToNext.get(id);
|
|
}else if(this.lastmessage?.id!==id){
|
|
await this.grabAfter(id);
|
|
return this.idToNext.get(id);
|
|
}else{
|
|
}
|
|
}
|
|
}),
|
|
(async (id:string):Promise<HTMLElement>=>{
|
|
//await new Promise(_=>{setTimeout(_,Math.random()*10)})
|
|
const messgage=this.messages.get(id);
|
|
try{
|
|
if(messgage){
|
|
return messgage.buildhtml();
|
|
}else{
|
|
console.error(id+" not found");
|
|
}
|
|
}catch(e){
|
|
console.error(e);
|
|
}
|
|
return document.createElement("div");
|
|
}),
|
|
(async (id:string)=>{
|
|
const message=this.messages.get(id);
|
|
try{
|
|
if(message){
|
|
message.deleteDiv();
|
|
return true;
|
|
}
|
|
}catch(e){
|
|
console.error(e);
|
|
}finally{}
|
|
return false;
|
|
}),
|
|
this.readbottom.bind(this)
|
|
);
|
|
}
|
|
constructor(json:channeljson|-1,owner:Guild,id:string=json===-1?"":json.id){
|
|
super(id);
|
|
if(json===-1){
|
|
return;
|
|
}
|
|
this.editing;
|
|
this.type=json.type;
|
|
this.owner=owner;
|
|
this.headers=this.owner.headers;
|
|
this.name=json.name;
|
|
if(json.parent_id){
|
|
this.parent_id=json.parent_id;
|
|
}
|
|
this.parent=undefined;
|
|
this.children=[];
|
|
this.guild_id=json.guild_id;
|
|
this.permission_overwrites=new Map();
|
|
this.permission_overwritesar=[];
|
|
for(const thing of json.permission_overwrites){
|
|
if(thing.id==="1182819038095799904"||thing.id==="1182820803700625444"){
|
|
continue;
|
|
}
|
|
if(!this.permission_overwrites.has(thing.id)){//either a bug in the server requires this, or the API is cursed
|
|
this.permission_overwrites.set(thing.id,new Permissions(thing.allow,thing.deny));
|
|
const permission=this.permission_overwrites.get(thing.id);
|
|
if(permission){
|
|
const role=this.guild.roleids.get(thing.id);
|
|
if(role){
|
|
this.permission_overwritesar.push([role,permission]);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
this.topic=json.topic;
|
|
this.nsfw=json.nsfw;
|
|
this.position=json.position;
|
|
this.lastreadmessageid=undefined;
|
|
if(json.last_message_id){
|
|
this.lastmessageid=json.last_message_id;
|
|
}else{
|
|
this.lastmessageid=undefined;
|
|
}
|
|
this.setUpInfiniteScroller();
|
|
this.perminfo??={};
|
|
}
|
|
get perminfo(){
|
|
return this.guild.perminfo.channels[this.id];
|
|
}
|
|
set perminfo(e){
|
|
this.guild.perminfo.channels[this.id]=e;
|
|
}
|
|
isAdmin(){
|
|
return this.guild.isAdmin();
|
|
}
|
|
get guild(){
|
|
return this.owner;
|
|
}
|
|
get localuser(){
|
|
return this.guild.localuser;
|
|
}
|
|
get info(){
|
|
return this.owner.info;
|
|
}
|
|
readStateInfo(json:readyjson["d"]["read_state"]["entries"][0]){
|
|
this.lastreadmessageid=json.last_message_id;
|
|
this.mentions=json.mention_count;
|
|
this.mentions??=0;
|
|
this.lastpin=json.last_pin_timestamp;
|
|
}
|
|
get hasunreads():boolean{
|
|
if(!this.hasPermission("VIEW_CHANNEL")){
|
|
return false;
|
|
}
|
|
return (!!this.lastmessageid)&&
|
|
(
|
|
(!this.lastreadmessageid)||
|
|
SnowFlake.stringToUnixTime(this.lastmessageid)>SnowFlake.stringToUnixTime(this.lastreadmessageid)
|
|
)
|
|
&&this.type!==4;
|
|
}
|
|
hasPermission(name:string,member=this.guild.member):boolean{
|
|
if(member.isAdmin()){
|
|
return true;
|
|
}
|
|
for(const thing of member.roles){
|
|
let premission=this.permission_overwrites.get(thing.id);
|
|
if(premission){
|
|
const perm=premission.getPermission(name);
|
|
if(perm){
|
|
return perm===1;
|
|
}
|
|
}
|
|
if(thing.permissions.getPermission(name)){
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
get canMessage():boolean{
|
|
if((this.permission_overwritesar.length===0)&&this.hasPermission("MANAGE_CHANNELS")){
|
|
const role=this.guild.roles.find(_=>_.name==="@everyone");
|
|
if(role){
|
|
this.addRoleToPerms(role);
|
|
}
|
|
}
|
|
return this.hasPermission("SEND_MESSAGES");
|
|
}
|
|
sortchildren(){
|
|
this.children.sort((a,b)=>{
|
|
return a.position-b.position;
|
|
});
|
|
}
|
|
resolveparent(guild:Guild){
|
|
const parentid=this.parent_id;
|
|
if(!parentid)return false;
|
|
this.parent=this.localuser.channelids.get(parentid);
|
|
this.parent??=undefined;
|
|
if(this.parent!==undefined){
|
|
this.parent.children.push(this);
|
|
}
|
|
return this.parent!==undefined;
|
|
}
|
|
calculateReorder(){
|
|
let position=-1;
|
|
const build:{id:string,position:number|undefined,parent_id:string|undefined}[]=[];
|
|
for(const thing of this.children){
|
|
const thisthing:{id:string,position:number|undefined,parent_id:string|undefined}={id: thing.id,position: undefined,parent_id: undefined};
|
|
if(thing.position<position){
|
|
thing.position=thisthing.position=position+1;
|
|
}
|
|
position=thing.position;
|
|
if(thing.move_id&&thing.move_id!==thing.parent_id){
|
|
thing.parent_id=thing.move_id;
|
|
thisthing.parent_id=thing.parent?.id;
|
|
thing.move_id=undefined;
|
|
//console.log(this.guild.channelids[thisthing.parent_id.id]);
|
|
}
|
|
if(thisthing.position||thisthing.parent_id){
|
|
build.push(thisthing);
|
|
}
|
|
}
|
|
return build;
|
|
}
|
|
static dragged:[Channel,HTMLDivElement]|[]=[];
|
|
html:WeakRef<HTMLElement>|undefined;
|
|
get visable(){
|
|
return this.hasPermission("VIEW_CHANNEL")
|
|
}
|
|
createguildHTML(admin=false):HTMLDivElement{
|
|
const div=document.createElement("div");
|
|
this.html=new WeakRef(div);
|
|
if(!this.visable){
|
|
let quit=true;
|
|
for(const thing of this.children){
|
|
if(thing.visable){
|
|
quit=false;
|
|
}
|
|
}
|
|
if(quit){
|
|
return div;
|
|
}
|
|
}
|
|
div["all"]=this;
|
|
div.draggable=admin;
|
|
div.addEventListener("dragstart",e=>{
|
|
Channel.dragged=[this,div];e.stopImmediatePropagation();
|
|
});
|
|
div.addEventListener("dragend",()=>{
|
|
Channel.dragged=[];
|
|
});
|
|
if(this.type===4){
|
|
this.sortchildren();
|
|
const caps=document.createElement("div");
|
|
|
|
const decdiv=document.createElement("div");
|
|
const decoration=document.createElement("span");
|
|
decoration.classList.add("svgtheme","collapse-icon","svg-category");
|
|
decdiv.appendChild(decoration);
|
|
|
|
const myhtml=document.createElement("p2");
|
|
myhtml.textContent=this.name;
|
|
decdiv.appendChild(myhtml);
|
|
caps.appendChild(decdiv);
|
|
const childrendiv=document.createElement("div");
|
|
if(admin){
|
|
const addchannel=document.createElement("span");
|
|
addchannel.textContent="+";
|
|
addchannel.classList.add("addchannel");
|
|
caps.appendChild(addchannel);
|
|
addchannel.onclick=_=>{
|
|
this.guild.createchannels(this.createChannel.bind(this));
|
|
};
|
|
this.coatDropDiv(decdiv,childrendiv);
|
|
}
|
|
div.appendChild(caps);
|
|
caps.classList.add("capsflex");
|
|
decdiv.classList.add("channeleffects");
|
|
decdiv.classList.add("channel");
|
|
|
|
Channel.contextmenu.bindContextmenu(decdiv,this,undefined);
|
|
decdiv["all"]=this;
|
|
|
|
|
|
for(const channel of this.children){
|
|
childrendiv.appendChild(channel.createguildHTML(admin));
|
|
}
|
|
childrendiv.classList.add("channels");
|
|
setTimeout(_=>{
|
|
if(!this.perminfo.collapsed){
|
|
childrendiv.style.height = childrendiv.scrollHeight + "px";
|
|
}
|
|
},100);
|
|
div.appendChild(childrendiv);
|
|
if(this.perminfo.collapsed){
|
|
decoration.classList.add("hiddencat");
|
|
childrendiv.style.height = "0px";
|
|
}
|
|
decdiv.onclick=()=>{
|
|
if(childrendiv.style.height!=="0px"){
|
|
decoration.classList.add("hiddencat");
|
|
this.perminfo.collapsed=true;
|
|
this.localuser.userinfo.updateLocal();
|
|
childrendiv.style.height = "0px";
|
|
}else{
|
|
decoration.classList.remove("hiddencat");
|
|
this.perminfo.collapsed=false;
|
|
this.localuser.userinfo.updateLocal();
|
|
childrendiv.style.height = childrendiv.scrollHeight + "px";
|
|
}
|
|
};
|
|
|
|
}else{
|
|
div.classList.add("channel");
|
|
if(this.hasunreads){
|
|
div.classList.add("cunread");
|
|
}
|
|
Channel.contextmenu.bindContextmenu(div,this,undefined);
|
|
if(admin){
|
|
this.coatDropDiv(div);
|
|
}
|
|
div["all"]=this;
|
|
const myhtml=document.createElement("span");
|
|
myhtml.textContent=this.name;
|
|
if(this.type===0){
|
|
const decoration=document.createElement("span");
|
|
div.appendChild(decoration);
|
|
decoration.classList.add("space","svgtheme","svg-channel");
|
|
}else if(this.type===2){//
|
|
const decoration=document.createElement("span");
|
|
div.appendChild(decoration);
|
|
decoration.classList.add("space","svgtheme","svg-voice");
|
|
}else if(this.type===5){//
|
|
const decoration=document.createElement("span");
|
|
div.appendChild(decoration);
|
|
decoration.classList.add("space","svgtheme","svg-announce");
|
|
}else{
|
|
console.log(this.type);
|
|
}
|
|
div.appendChild(myhtml);
|
|
div.onclick=_=>{
|
|
this.getHTML();
|
|
};
|
|
}
|
|
return div;
|
|
}
|
|
get myhtml(){
|
|
if(this.html){
|
|
return this.html.deref();
|
|
}else{
|
|
return undefined
|
|
}
|
|
}
|
|
readbottom(){
|
|
if(!this.hasunreads){
|
|
return;
|
|
}
|
|
fetch(this.info.api+"/channels/"+this.id+"/messages/"+this.lastmessageid+"/ack",{
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({})
|
|
});
|
|
this.lastreadmessageid=this.lastmessageid;
|
|
this.guild.unreads();
|
|
if(this.myhtml){
|
|
this.myhtml.classList.remove("cunread");
|
|
}
|
|
}
|
|
coatDropDiv(div:HTMLDivElement,container:HTMLElement|boolean=false){
|
|
div.addEventListener("dragenter", event=>{
|
|
console.log("enter");
|
|
event.preventDefault();
|
|
});
|
|
|
|
div.addEventListener("dragover", event=>{
|
|
event.preventDefault();
|
|
});
|
|
|
|
div.addEventListener("drop", event=>{
|
|
const that=Channel.dragged[0];
|
|
if(!that)return;
|
|
event.preventDefault();
|
|
if(container){
|
|
that.move_id=this.id;
|
|
if(that.parent){
|
|
that.parent.children.splice(that.parent.children.indexOf(that),1);
|
|
}
|
|
that.parent=this;
|
|
(container as HTMLElement).prepend(Channel.dragged[1] as HTMLDivElement);
|
|
this.children.unshift(that);
|
|
}else{
|
|
console.log(this,Channel.dragged);
|
|
that.move_id=this.parent_id;
|
|
if(that.parent){
|
|
that.parent.children.splice(that.parent.children.indexOf(that),1);
|
|
}else{
|
|
this.guild.headchannels.splice(this.guild.headchannels.indexOf(that),1);
|
|
}
|
|
that.parent=this.parent;
|
|
if(that.parent){
|
|
const build:Channel[]=[];
|
|
for(let i=0;i<that.parent.children.length;i++){
|
|
build.push(that.parent.children[i]);
|
|
if(that.parent.children[i]===this){
|
|
build.push(that);
|
|
}
|
|
}
|
|
that.parent.children=build;
|
|
}else{
|
|
const build:Channel[]=[];
|
|
for(let i=0;i<this.guild.headchannels.length;i++){
|
|
build.push(this.guild.headchannels[i]);
|
|
if(this.guild.headchannels[i]===this){
|
|
build.push(that);
|
|
}
|
|
}
|
|
this.guild.headchannels=build;
|
|
}
|
|
if(Channel.dragged[1]){
|
|
div.after(Channel.dragged[1]);
|
|
}
|
|
}
|
|
this.guild.calculateReorder();
|
|
});
|
|
|
|
return div;
|
|
}
|
|
createChannel(name:string,type:number){
|
|
fetch(this.info.api+"/guilds/"+this.guild.id+"/channels",{
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
name,
|
|
type,
|
|
parent_id: this.id,
|
|
permission_overwrites: [],
|
|
})
|
|
});
|
|
}
|
|
editChannel(){
|
|
let name=this.name;
|
|
let topic=this.topic;
|
|
let nsfw=this.nsfw;
|
|
const thisid=this.id;
|
|
const thistype=this.type;
|
|
const full=new Dialog(
|
|
["hdiv",
|
|
["vdiv",
|
|
["textbox","Channel name:",this.name,function(this:HTMLInputElement){
|
|
name=this.value;
|
|
}],
|
|
["mdbox","Channel topic:",this.topic,function(this:HTMLTextAreaElement){
|
|
topic=this.value;
|
|
}],
|
|
["checkbox","NSFW Channel",this.nsfw,function(this:HTMLInputElement){
|
|
nsfw=this.checked;
|
|
}],
|
|
["button","","submit",()=>{
|
|
fetch(this.info.api+"/channels/"+thisid,{
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
name,
|
|
type: thistype,
|
|
topic,
|
|
bitrate: 64000,
|
|
user_limit: 0,
|
|
nsfw,
|
|
flags: 0,
|
|
rate_limit_per_user: 0
|
|
})
|
|
});
|
|
console.log(full);
|
|
full.hide();
|
|
}]
|
|
]
|
|
|
|
]);
|
|
full.show();
|
|
console.log(full);
|
|
}
|
|
deleteChannel(){
|
|
fetch(this.info.api+"/channels/"+this.id,{
|
|
method: "DELETE",
|
|
headers: this.headers
|
|
});
|
|
}
|
|
setReplying(message:Message){
|
|
if(this.replyingto?.div){
|
|
this.replyingto.div.classList.remove("replying");
|
|
}
|
|
this.replyingto=message;
|
|
if(!this.replyingto?.div)return;
|
|
console.log(message);
|
|
this.replyingto.div.classList.add("replying");
|
|
this.makereplybox();
|
|
}
|
|
makereplybox(){
|
|
const replybox=document.getElementById("replybox") as HTMLElement;
|
|
if(this.replyingto){
|
|
replybox.innerHTML="";
|
|
const span=document.createElement("span");
|
|
span.textContent="Replying to "+this.replyingto.author.username;
|
|
const X=document.createElement("button");
|
|
X.onclick=_=>{
|
|
if(this.replyingto?.div){
|
|
this.replyingto.div.classList.remove("replying");
|
|
}
|
|
replybox.classList.add("hideReplyBox");
|
|
this.replyingto=null;
|
|
replybox.innerHTML="";
|
|
};
|
|
replybox.classList.remove("hideReplyBox");
|
|
X.textContent="⦻";
|
|
X.classList.add("cancelReply");
|
|
replybox.append(span);
|
|
replybox.append(X);
|
|
}else{
|
|
replybox.classList.add("hideReplyBox");
|
|
}
|
|
}
|
|
async getmessage(id:string):Promise<Message>{
|
|
const message=this.messages.get(id);
|
|
if(message){
|
|
return message;
|
|
}else{
|
|
const gety=await fetch(this.info.api+"/channels/"+this.id+"/messages?limit=1&around="+id,{headers: this.headers});
|
|
const json=await gety.json();
|
|
return new Message(json[0],this);
|
|
}
|
|
}
|
|
static genid:number=0;
|
|
async getHTML(){
|
|
const id=++Channel.genid;
|
|
if(this.localuser.channelfocus){
|
|
this.localuser.channelfocus.infinite.delete();
|
|
}
|
|
if(this.guild!==this.localuser.lookingguild){
|
|
this.guild.loadGuild();
|
|
}
|
|
if(this.localuser.channelfocus&&this.localuser.channelfocus.myhtml){
|
|
this.localuser.channelfocus.myhtml.classList.remove("viewChannel");
|
|
}
|
|
if(this.myhtml){
|
|
this.myhtml.classList.add("viewChannel");
|
|
}
|
|
this.guild.prevchannel=this;
|
|
this.guild.perminfo.prevchannel=this.id;
|
|
this.localuser.userinfo.updateLocal();
|
|
this.localuser.channelfocus=this;
|
|
const prom=this.infinite.delete();
|
|
history.pushState(null, "","/channels/"+this.guild_id+"/"+this.id);
|
|
|
|
this.localuser.pageTitle("#"+this.name);
|
|
const channelTopic=document.getElementById("channelTopic") as HTMLSpanElement;
|
|
if(this.topic){
|
|
channelTopic.innerHTML=new MarkDown(this.topic, this).makeHTML().innerHTML;
|
|
channelTopic.removeAttribute("hidden");
|
|
}else channelTopic.setAttribute("hidden","");
|
|
|
|
const loading=document.getElementById("loadingdiv") as HTMLDivElement;
|
|
Channel.regenLoadingMessages();
|
|
loading.classList.add("loading");
|
|
this.rendertyping();
|
|
await this.putmessages();
|
|
await prom;
|
|
if(id!==Channel.genid){
|
|
return;
|
|
}
|
|
this.makereplybox();
|
|
|
|
await this.buildmessages();
|
|
//loading.classList.remove("loading");
|
|
(document.getElementById("typebox") as HTMLDivElement).contentEditable=""+this.canMessage;
|
|
}
|
|
typingmap:Map<Member,number>=new Map();
|
|
async typingStart(typing):Promise<void>{
|
|
const memb=await Member.new(typing.d.member,this.guild);
|
|
if(!memb)return;
|
|
if(memb.id===this.localuser.user.id){
|
|
console.log("you is typing");
|
|
return;
|
|
}
|
|
console.log("user is typing and you should see it");
|
|
this.typingmap.set(memb,Date.now());
|
|
setTimeout(this.rendertyping.bind(this),10000);
|
|
this.rendertyping();
|
|
}
|
|
rendertyping():void{
|
|
const typingtext=document.getElementById("typing") as HTMLDivElement;
|
|
let build="";
|
|
let showing=false;
|
|
let i=0;
|
|
const curtime=Date.now()-5000;
|
|
for(const thing of this.typingmap.keys()){
|
|
if(this.typingmap.get(thing) as number>curtime){
|
|
if(i!==0){
|
|
build+=", ";
|
|
}
|
|
i++;
|
|
if(thing.nick){
|
|
build+=thing.nick;
|
|
}else{
|
|
build+=thing.user.username;
|
|
}
|
|
showing=true;
|
|
}else{
|
|
this.typingmap.delete(thing);
|
|
}
|
|
}
|
|
if(i>1){
|
|
build+=" are typing";
|
|
}else{
|
|
build+=" is typing";
|
|
}
|
|
if(this.localuser.channelfocus===this){
|
|
if(showing){
|
|
typingtext.classList.remove("hidden");
|
|
const typingtext2=document.getElementById("typingtext") as HTMLDivElement;
|
|
typingtext2.textContent=build;
|
|
}else{
|
|
typingtext.classList.add("hidden");
|
|
}
|
|
}
|
|
}
|
|
static regenLoadingMessages(){
|
|
const loading=document.getElementById("loadingdiv") as HTMLDivElement;
|
|
loading.innerHTML="";
|
|
for(let i=0;i<15;i++){
|
|
const div=document.createElement("div");
|
|
div.classList.add("loadingmessage");
|
|
if(Math.random()<0.5){
|
|
const pfp=document.createElement("div");
|
|
pfp.classList.add("loadingpfp");
|
|
const username=document.createElement("div");
|
|
username.style.width=Math.floor(Math.random()*96*1.5+40)+"px";
|
|
username.classList.add("loadingcontent");
|
|
div.append(pfp,username);
|
|
}
|
|
const content=document.createElement("div");
|
|
content.style.width=Math.floor(Math.random()*96*3+40)+"px";
|
|
content.style.height=Math.floor(Math.random()*3+1)*20+"px";
|
|
content.classList.add("loadingcontent");
|
|
div.append(content);
|
|
loading.append(div);
|
|
}
|
|
}
|
|
lastmessage:Message|undefined;
|
|
async putmessages(){
|
|
if(this.allthewayup){
|
|
return;
|
|
}
|
|
if(this.lastreadmessageid&&this.messages.has(this.lastreadmessageid)){
|
|
return;
|
|
}
|
|
const j=await fetch(this.info.api+"/channels/"+this.id+"/messages?limit=100",{
|
|
headers: this.headers,
|
|
});
|
|
|
|
const response=await j.json();
|
|
if(response.length!==100){
|
|
this.allthewayup=true;
|
|
}
|
|
let prev:Message|undefined;
|
|
for(const thing of response){
|
|
const message=new Message(thing,this);
|
|
if(prev){
|
|
this.idToNext.set(message.id,prev.id);
|
|
this.idToPrev.set(prev.id,message.id);
|
|
}else{
|
|
this.lastmessage=message;
|
|
this.lastmessageid=message.id;
|
|
}
|
|
prev=message;
|
|
}
|
|
}
|
|
delChannel(json:channeljson){
|
|
const build:Channel[]=[];
|
|
for(const thing of this.children){
|
|
if(thing.id!==json.id){
|
|
build.push(thing);
|
|
}
|
|
}
|
|
this.children=build;
|
|
}
|
|
async grabAfter(id:string){
|
|
if(id===this.lastmessage?.id){
|
|
return;
|
|
}
|
|
await fetch(this.info.api+"/channels/"+this.id+"/messages?limit=100&after="+id,{
|
|
headers: this.headers
|
|
}).then(j=>{
|
|
return j.json();
|
|
}).then(response=>{
|
|
let previd:string=id;
|
|
for(const i in response){
|
|
let messager:Message;
|
|
let willbreak=false;
|
|
if(this.messages.has(response[i].id)){
|
|
messager=this.messages.get(response[i].id) as Message;
|
|
willbreak=true;
|
|
}else{
|
|
messager=new Message(response[i],this);
|
|
}
|
|
this.idToPrev.set(messager.id,previd);
|
|
this.idToNext.set(previd,messager.id);
|
|
previd=messager.id;
|
|
if(willbreak){
|
|
break;
|
|
}
|
|
}
|
|
//out.buildmessages();
|
|
});
|
|
}
|
|
topid:string;
|
|
async grabBefore(id:string){
|
|
if(this.topid&&id===this.topid){
|
|
return;
|
|
}
|
|
|
|
await fetch(this.info.api+"/channels/"+this.id+"/messages?before="+id+"&limit=100",{
|
|
headers: this.headers
|
|
}).then(j=>{
|
|
return j.json();
|
|
}).then((response:messagejson[])=>{
|
|
if(response.length<100){
|
|
this.allthewayup=true;
|
|
if(response.length===0){
|
|
this.topid=id;
|
|
}
|
|
}
|
|
let previd=id;
|
|
for(const i in response){
|
|
let messager:Message;
|
|
let willbreak=false;
|
|
if(this.messages.has(response[i].id)){
|
|
console.log("flaky");
|
|
messager=this.messages.get(response[i].id) as Message;
|
|
willbreak=true;
|
|
}else{
|
|
messager=new Message(response[i],this);
|
|
}
|
|
|
|
this.idToNext.set(messager.id,previd);
|
|
this.idToPrev.set(previd,messager.id);
|
|
previd=messager.id;
|
|
|
|
if(Number(i)===response.length-1&&response.length<100){
|
|
this.topid=previd;
|
|
}
|
|
if(willbreak){
|
|
break;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* Please dont use this, its not implemented.
|
|
* @deprecated
|
|
* @todo
|
|
**/
|
|
async grabArround(id:string){//currently unused and no plans to use it yet
|
|
throw new Error("please don't call this, no one has implemented it :P");
|
|
}
|
|
async buildmessages(){
|
|
this.infinitefocus=false;
|
|
this.tryfocusinfinate();
|
|
}
|
|
infinitefocus=false;
|
|
async tryfocusinfinate(){
|
|
if(this.infinitefocus)return;
|
|
this.infinitefocus=true;
|
|
const messages=document.getElementById("channelw") as HTMLDivElement;
|
|
for(const thing of messages.getElementsByClassName("messagecontainer")){
|
|
thing.remove();
|
|
}
|
|
const loading=document.getElementById("loadingdiv") as HTMLDivElement;
|
|
const removetitle=document.getElementById("removetitle");
|
|
//messages.innerHTML="";
|
|
let id:string|undefined;
|
|
if(this.lastreadmessageid&&this.messages.has(this.lastreadmessageid)){
|
|
id=this.lastreadmessageid;
|
|
}else if(this.lastreadmessageid&&(id=this.findClosest(this.lastreadmessageid))){
|
|
|
|
}else if(this.lastmessageid&&this.messages.has(this.lastmessageid)){
|
|
id=this.goBackIds(this.lastmessageid,50);
|
|
}
|
|
if(!id){
|
|
if(!removetitle){
|
|
const title=document.createElement("h2");
|
|
title.id="removetitle";
|
|
title.textContent="No messages appear to be here, be the first to say something!";
|
|
title.classList.add("titlespace");
|
|
messages.append(title);
|
|
}
|
|
this.infinitefocus=false;
|
|
loading.classList.remove("loading");
|
|
return;
|
|
}else if(removetitle){
|
|
removetitle.remove();
|
|
}
|
|
if(this.localuser.channelfocus!==this){
|
|
return;
|
|
}
|
|
for(const elm of messages.getElementsByClassName("scroller")){
|
|
elm.remove();
|
|
console.warn("rouge element detected and removed")
|
|
}
|
|
messages.append(await this.infinite.getDiv(id));
|
|
this.infinite.updatestuff();
|
|
this.infinite.watchForChange().then(async _=>{
|
|
//await new Promise(resolve => setTimeout(resolve, 0));
|
|
this.infinite.focus(id,false);//if someone could figure out how to make this work correctly without this, that's be great :P
|
|
loading.classList.remove("loading");
|
|
});
|
|
//this.infinite.focus(id.id,false);
|
|
}
|
|
private goBackIds(id:string,back:number,returnifnotexistant=true):string|undefined{
|
|
while(back!==0){
|
|
const nextid=this.idToPrev.get(id);
|
|
if(nextid){
|
|
id=nextid;
|
|
back--;
|
|
}else{
|
|
if(returnifnotexistant){
|
|
break;
|
|
}else{
|
|
return undefined;
|
|
}
|
|
}
|
|
}
|
|
return id;
|
|
}
|
|
private findClosest(id:string|undefined){
|
|
if(!this.lastmessageid||!id)return;
|
|
let flake:string|undefined=this.lastmessageid;
|
|
const time=SnowFlake.stringToUnixTime(id);
|
|
let flaketime=SnowFlake.stringToUnixTime(flake);
|
|
while(flake&&time<flaketime){
|
|
flake=this.idToPrev.get(flake);
|
|
|
|
if(!flake){
|
|
return;
|
|
}
|
|
flaketime=SnowFlake.stringToUnixTime(flake);
|
|
}
|
|
return flake;
|
|
}
|
|
updateChannel(json:channeljson){
|
|
this.type=json.type;
|
|
this.name=json.name;
|
|
const parent=this.localuser.channelids.get(json.parent_id);
|
|
if(parent){
|
|
this.parent=parent;
|
|
this.parent_id=parent.id;
|
|
}else{
|
|
this.parent=undefined;
|
|
this.parent_id=undefined;
|
|
}
|
|
|
|
this.children=[];
|
|
this.guild_id=json.guild_id;
|
|
this.permission_overwrites=new Map();
|
|
for(const thing of json.permission_overwrites){
|
|
if(thing.id==="1182819038095799904"||thing.id==="1182820803700625444"){
|
|
continue;
|
|
}
|
|
this.permission_overwrites.set(thing.id,new Permissions(thing.allow,thing.deny));
|
|
const permisions=this.permission_overwrites.get(thing.id);
|
|
if(permisions){
|
|
const role=this.guild.roleids.get(thing.id);
|
|
if(role){
|
|
this.permission_overwritesar.push([role,permisions]);
|
|
}
|
|
}
|
|
}
|
|
this.topic=json.topic;
|
|
this.nsfw=json.nsfw;
|
|
}
|
|
typingstart(){
|
|
if(this.typing>Date.now()){
|
|
return;
|
|
}
|
|
this.typing=Date.now()+6000;
|
|
fetch(this.info.api+"/channels/"+this.id+"/typing",{
|
|
method: "POST",
|
|
headers: this.headers
|
|
});
|
|
}
|
|
get notification(){
|
|
let notinumber:number|null=this.message_notifications;
|
|
if(Number(notinumber)===3){
|
|
notinumber=null;
|
|
}
|
|
notinumber??=this.guild.message_notifications;
|
|
switch(Number(notinumber)){
|
|
case 0:
|
|
return"all";
|
|
case 1:
|
|
return"mentions";
|
|
case 2:
|
|
return"none";
|
|
case 3:
|
|
return"default";
|
|
}
|
|
}
|
|
async sendMessage(content:string,{attachments=[],embeds=[],replyingto=null}:
|
|
{attachments:Blob[],embeds,replyingto:Message|null}){
|
|
let replyjson:any;
|
|
if(replyingto){
|
|
replyjson=
|
|
{
|
|
guild_id: replyingto.guild.id,
|
|
channel_id: replyingto.channel.id,
|
|
message_id: replyingto.id,
|
|
};
|
|
}
|
|
if(attachments.length===0){
|
|
const body={
|
|
content,
|
|
nonce: Math.floor(Math.random()*1000000000),
|
|
message_reference: undefined
|
|
};
|
|
if(replyjson){
|
|
body.message_reference=replyjson;
|
|
}
|
|
return await fetch(this.info.api+"/channels/"+this.id+"/messages",{
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify(body)
|
|
});
|
|
}else{
|
|
const formData = new FormData();
|
|
const body={
|
|
content,
|
|
nonce: Math.floor(Math.random()*1000000000),
|
|
message_reference: undefined
|
|
};
|
|
if(replyjson){
|
|
body.message_reference=replyjson;
|
|
}
|
|
formData.append("payload_json", JSON.stringify(body));
|
|
for(const i in attachments){
|
|
formData.append("files["+i+"]",attachments[i]);
|
|
}
|
|
return await fetch(this.info.api+"/channels/"+this.id+"/messages", {
|
|
method: "POST",
|
|
body: formData,
|
|
headers: {Authorization: this.headers.Authorization}
|
|
});
|
|
}
|
|
}
|
|
messageCreate(messagep:messageCreateJson):void{
|
|
if(!this.hasPermission("VIEW_CHANNEL")){
|
|
return;
|
|
}
|
|
const messagez=new Message(messagep.d,this);
|
|
this.lastmessage=messagez;
|
|
if(this.lastmessageid){
|
|
this.idToNext.set(this.lastmessageid,messagez.id);
|
|
this.idToPrev.set(messagez.id,this.lastmessageid);
|
|
}
|
|
|
|
this.lastmessageid=messagez.id;
|
|
|
|
if(messagez.author===this.localuser.user){
|
|
this.lastreadmessageid=messagez.id;
|
|
if(this.myhtml){
|
|
this.myhtml.classList.remove("cunread");
|
|
}
|
|
}else{
|
|
if(this.myhtml){
|
|
this.myhtml.classList.add("cunread");
|
|
}
|
|
}
|
|
this.guild.unreads();
|
|
if(this===this.localuser.channelfocus){
|
|
if(!this.infinitefocus){
|
|
this.tryfocusinfinate();
|
|
}
|
|
this.infinite.addedBottom()
|
|
}
|
|
if(messagez.author===this.localuser.user){
|
|
return;
|
|
}
|
|
if(this.localuser.lookingguild?.prevchannel===this&&document.hasFocus()){
|
|
return;
|
|
}
|
|
if(this.notification==="all"){
|
|
this.notify(messagez);
|
|
}else if(this.notification==="mentions"&&messagez.mentionsuser(this.localuser.user)){
|
|
this.notify(messagez);
|
|
}
|
|
}
|
|
notititle(message:Message):string{
|
|
return message.author.username+" > "+this.guild.properties.name+" > "+this.name;
|
|
}
|
|
notify(message:Message,deep=0){
|
|
Voice.noises(Voice.getNotificationSound());
|
|
if(!("Notification" in window)){
|
|
|
|
}else if(Notification.permission === "granted"){
|
|
let noticontent:string|undefined|null=message.content.textContent;
|
|
if(message.embeds[0]){
|
|
noticontent||=message.embeds[0].json.title;
|
|
noticontent||=message.content.textContent;
|
|
}
|
|
noticontent||="Blank Message";
|
|
let imgurl:null|string=null;
|
|
const images=message.getimages();
|
|
if(images.length){
|
|
const image = images[0];
|
|
if(image.proxy_url){
|
|
imgurl||=image.proxy_url;
|
|
}
|
|
imgurl||=image.url;
|
|
}
|
|
const notification = new Notification(this.notititle(message),{
|
|
body: noticontent,
|
|
icon: message.author.getpfpsrc(),
|
|
image: imgurl,
|
|
});
|
|
notification.addEventListener("click",_=>{
|
|
window.focus();
|
|
this.getHTML();
|
|
});
|
|
}else if(Notification.permission !== "denied"){
|
|
Notification.requestPermission().then(()=>{
|
|
if(deep===3){
|
|
return;
|
|
}
|
|
this.notify(message,deep+1);
|
|
});
|
|
}
|
|
}
|
|
async addRoleToPerms(role:Role){
|
|
await fetch(this.info.api+"/channels/"+this.id+"/permissions/"+role.id,{
|
|
method: "PUT",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
allow: "0",
|
|
deny: "0",
|
|
id: role.id,
|
|
type: 0
|
|
})
|
|
});
|
|
const perm=new Permissions("0","0");
|
|
this.permission_overwrites.set(role.id,perm);
|
|
this.permission_overwritesar.push([role,perm]);
|
|
}
|
|
async updateRolePermissions(id:string,perms:Permissions){
|
|
const permission=this.permission_overwrites.get(id);
|
|
if(permission){
|
|
permission.allow=perms.allow;
|
|
permission.deny=perms.deny;
|
|
await fetch(this.info.api+"/channels/"+this.id+"/permissions/"+id,{
|
|
method: "PUT",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
allow: permission.allow.toString(),
|
|
deny: permission.deny.toString(),
|
|
id,
|
|
type: 0
|
|
})
|
|
});
|
|
}
|
|
}
|
|
}
|
|
Channel.setupcontextmenu();
|
|
export{Channel};
|
|
|