968 lines
36 KiB
TypeScript
968 lines
36 KiB
TypeScript
import {Guild} from "./guild.js";
|
|
import {Channel} from "./channel.js";
|
|
import {Direct} from "./direct.js";
|
|
import {Voice} from "./audio.js";
|
|
import {User} from "./user.js";
|
|
import {Fullscreen} from "./fullscreen.js";
|
|
import {setTheme, Specialuser} from "./login.js";
|
|
import { SnowFlake } from "./snowflake.js";
|
|
import { Message } from "./message.js";
|
|
import { channeljson, readyjson, userjson } from "./jsontypes.js";
|
|
|
|
const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]);
|
|
|
|
class Localuser{
|
|
lastSequence:number|null=null;
|
|
token:string;
|
|
userinfo:Specialuser;
|
|
serverurls;
|
|
initialized:boolean;
|
|
info;
|
|
headers:{"Content-type":string,Authorization:string};
|
|
usersettings:Fullscreen;
|
|
userConnections:Fullscreen;
|
|
devPortal:Fullscreen;
|
|
ready:readyjson;
|
|
guilds:Guild[];
|
|
guildids:Map<string,Guild>;
|
|
user:User;
|
|
status:string;
|
|
channelfocus:Channel;
|
|
lookingguild:Guild;
|
|
guildhtml:Map<string, HTMLDivElement>;
|
|
ws:WebSocket;
|
|
typing:[string,number][];
|
|
wsinterval:NodeJS.Timeout;
|
|
connectionSucceed=0;
|
|
errorBackoff=0;
|
|
constructor(userinfo:Specialuser){
|
|
this.token=userinfo.token;
|
|
this.userinfo=userinfo;
|
|
this.serverurls=this.userinfo.serverurls;
|
|
this.initialized=false;
|
|
this.info=this.serverurls;
|
|
this.headers={"Content-type": "application/json; charset=UTF-8",Authorization:this.userinfo.token};
|
|
}
|
|
gottenReady(ready:readyjson):void{
|
|
this.usersettings=null;
|
|
this.initialized=true;
|
|
this.ready=ready;
|
|
this.guilds=[];
|
|
this.guildids=new Map();
|
|
this.user=new User(ready.d.user,this);
|
|
this.userinfo.username=this.user.username;
|
|
this.userinfo.pfpsrc=this.user.getpfpsrc();
|
|
this.status=this.ready.d.user_settings.status;
|
|
this.channelfocus=null;
|
|
this.lookingguild=null;
|
|
this.guildhtml=new Map();
|
|
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,members[thing.id]);
|
|
this.guilds.push(temp);
|
|
this.guildids.set(temp.id,temp);
|
|
}
|
|
{
|
|
const temp=new Direct(ready.d.private_channels,this);
|
|
this.guilds.push(temp);
|
|
this.guildids.set(temp.id,temp);
|
|
}
|
|
console.log(ready.d.user_guild_settings.entries);
|
|
|
|
|
|
for(const thing of ready.d.user_guild_settings.entries){
|
|
this.guildids.get(thing.guild_id).notisetting(thing);
|
|
}
|
|
|
|
for(const thing of ready.d.read_state.entries){
|
|
const channel=this.resolveChannelFromID(thing.id);
|
|
if(!channel){continue;}
|
|
const guild=channel.guild;
|
|
if(guild===undefined){
|
|
continue
|
|
}
|
|
const guildid=guild.snowflake;
|
|
this.guildids.get(guildid.id).channelids[thing.channel_id].readStateInfo(thing);
|
|
}
|
|
this.typing=[];
|
|
}
|
|
outoffocus():void{
|
|
document.getElementById("servers").innerHTML="";
|
|
document.getElementById("channels").innerHTML="";
|
|
if(this.channelfocus){
|
|
this.channelfocus.infinite.delete();
|
|
}
|
|
this.lookingguild=null;
|
|
this.channelfocus=null;
|
|
}
|
|
unload():void{
|
|
this.initialized=false;
|
|
clearInterval(this.wsinterval);
|
|
this.outoffocus();
|
|
this.guilds=[];
|
|
this.guildids=new Map();
|
|
this.ws.close(4001)
|
|
SnowFlake.clear();
|
|
User.clear();
|
|
}
|
|
async initwebsocket():Promise<void>{
|
|
let returny=null
|
|
const promise=new Promise((res)=>{returny=res});
|
|
this.ws = new WebSocket(this.serverurls.gateway.toString()+"?encoding=json&v=9"+(DecompressionStream?"&compress=zlib-stream":""));
|
|
this.ws.addEventListener('open', (event) => {
|
|
console.log('WebSocket connected');
|
|
this.ws.send(JSON.stringify({
|
|
"op": 2,
|
|
"d": {
|
|
"token":this.token,
|
|
"capabilities": 16381,
|
|
"properties": {
|
|
"browser": "Jank Client",
|
|
"client_build_number": 0,//might update this eventually lol
|
|
"release_channel": "Custom",
|
|
"browser_user_agent": navigator.userAgent
|
|
},
|
|
"compress": !!DecompressionStream,
|
|
"presence": {
|
|
"status": "online",
|
|
"since": new Date().getTime(),
|
|
"activities": [],
|
|
"afk": false
|
|
}
|
|
}
|
|
}))
|
|
});
|
|
let ds:DecompressionStream;
|
|
let w:WritableStreamDefaultWriter;
|
|
let r:ReadableStreamDefaultReader;
|
|
let arr:Uint8Array;
|
|
if(DecompressionStream){
|
|
ds = new DecompressionStream("deflate");
|
|
w= ds.writable.getWriter();
|
|
r=ds.readable.getReader();
|
|
arr=new Uint8Array();
|
|
}
|
|
|
|
|
|
let build="";
|
|
this.ws.addEventListener('message', async (event) => {
|
|
let temp:{op:number,t:string};
|
|
if(event.data instanceof Blob){
|
|
const buff=await event.data.arrayBuffer()
|
|
const array=new Uint8Array(buff);
|
|
|
|
const temparr=new Uint8Array(array.length+arr.length);
|
|
temparr.set(arr, 0);
|
|
temparr.set(array, arr.length);
|
|
arr=temparr;
|
|
|
|
const len=array.length;
|
|
if(!(array[len-1]===255&&array[len-2]===255&&array[len-3]===0&&array[len-4]===0)){
|
|
return;
|
|
}
|
|
w.write(arr.buffer);
|
|
arr=new Uint8Array();
|
|
//console.log(data,test);
|
|
while(true){
|
|
const read=(await r.read());
|
|
const data=new TextDecoder().decode(read.value);
|
|
if(data===""){
|
|
break;
|
|
}
|
|
build+=data;
|
|
console.log("temp");
|
|
try{
|
|
temp=JSON.parse(build);
|
|
build="";
|
|
if(temp.op===0&&temp.t==="READY"){
|
|
returny();
|
|
}
|
|
this.handleEvent(temp);
|
|
}catch{
|
|
}
|
|
}
|
|
}else{
|
|
temp=JSON.parse(event.data);
|
|
}
|
|
if(temp.op===0&&temp.t==="READY"){
|
|
returny();
|
|
}
|
|
this.handleEvent(temp);
|
|
|
|
});
|
|
|
|
this.ws.addEventListener("close", event => {
|
|
console.log("WebSocket closed with code " + event.code);
|
|
if (this.wsinterval) clearInterval(this.wsinterval);
|
|
|
|
this.unload();
|
|
document.getElementById("loading").classList.remove("doneloading");
|
|
document.getElementById("loading").classList.add("loading");
|
|
if (((event.code>1000 && event.code<1016) || wsCodesRetry.has(event.code))) {
|
|
if (this.connectionSucceed!==0 && Date.now()>this.connectionSucceed+20000) this.errorBackoff=0;
|
|
else this.errorBackoff++;
|
|
this.connectionSucceed=0;
|
|
|
|
document.getElementById("load-desc").innerHTML="Unable to connect to the Spacebar server, retrying in <b>" + Math.round(0.2 + (this.errorBackoff*2.8)) + "</b> seconds...";
|
|
|
|
setTimeout(() => {
|
|
document.getElementById("load-desc").textContent="Retrying...";
|
|
|
|
this.initwebsocket().then(() => {
|
|
this.loaduser();
|
|
this.init();
|
|
document.getElementById("loading").classList.add("doneloading");
|
|
document.getElementById("loading").classList.remove("loading");
|
|
console.log("done loading");
|
|
});
|
|
}, 200 + (this.errorBackoff*2800));
|
|
} else document.getElementById("load-desc").textContent="Unable to connect to the Spacebar server. Please try logging out and back in.";
|
|
});
|
|
|
|
await promise;
|
|
return;
|
|
}
|
|
handleEvent(temp){
|
|
console.debug(temp);
|
|
if (temp.s) this.lastSequence=temp.s;
|
|
if(temp.op==0){
|
|
switch(temp.t){
|
|
case "MESSAGE_CREATE":
|
|
if(this.initialized){
|
|
this.messageCreate(temp);
|
|
}
|
|
break;
|
|
case "MESSAGE_DELETE":
|
|
console.log(temp.d);
|
|
SnowFlake.getSnowFlakeFromID(temp.d.id,Message).getObject().deleteEvent();
|
|
break;
|
|
case "READY":
|
|
this.gottenReady(temp as readyjson);
|
|
this.genusersettings();
|
|
break;
|
|
case "MESSAGE_UPDATE":
|
|
const message=SnowFlake.getSnowFlakeFromID(temp.d.id,Message).getObject();
|
|
message.giveData(temp.d);
|
|
break;
|
|
case "TYPING_START":
|
|
if(this.initialized){
|
|
this.typingStart(temp);
|
|
}
|
|
break;
|
|
case "USER_UPDATE":
|
|
if(this.initialized){
|
|
const users=SnowFlake.getSnowFlakeFromID(temp.d.id,User).getObject() as User;
|
|
console.log(users,temp.d.id)
|
|
if(users){
|
|
users.userupdate(temp.d);
|
|
}
|
|
}
|
|
break
|
|
case "CHANNEL_UPDATE":
|
|
if(this.initialized){
|
|
this.updateChannel(temp.d);
|
|
}
|
|
break;
|
|
case "CHANNEL_CREATE":
|
|
if(this.initialized){
|
|
this.createChannel(temp.d);
|
|
}
|
|
break;
|
|
case "CHANNEL_DELETE":
|
|
if(this.initialized){
|
|
this.delChannel(temp.d);
|
|
}
|
|
break;
|
|
case "GUILD_DELETE":
|
|
{
|
|
const guildy=this.guildids.get(temp.d.id);
|
|
this.guildids.delete(temp.d.id);
|
|
this.guilds.splice(this.guilds.indexOf(guildy),1);
|
|
guildy.html.remove();
|
|
break;
|
|
}
|
|
case "GUILD_CREATE":
|
|
{
|
|
const guildy=new Guild(temp.d,this,this.user);
|
|
this.guilds.push(guildy);
|
|
this.guildids.set(guildy.id,guildy);
|
|
document.getElementById("servers").insertBefore(guildy.generateGuildIcon(),document.getElementById("bottomseparator"));
|
|
}
|
|
}
|
|
|
|
}else if(temp.op===10){
|
|
console.log("heartbeat down")
|
|
this.wsinterval=setInterval(_=>{
|
|
if (this.connectionSucceed===0) this.connectionSucceed=Date.now()
|
|
|
|
this.ws.send(JSON.stringify({op:1,d:this.lastSequence}))
|
|
},temp.d.heartbeat_interval)
|
|
}
|
|
}
|
|
resolveChannelFromID(ID:string):Channel{
|
|
let resolve=this.guilds.find(guild => guild.channelids[ID]);
|
|
if(resolve){
|
|
return resolve.channelids[ID];
|
|
}
|
|
return undefined;
|
|
}
|
|
updateChannel(json:channeljson):void{
|
|
SnowFlake.getSnowFlakeFromID(json.guild_id,Guild).getObject().updateChannel(json);
|
|
if(json.guild_id===this.lookingguild.id){
|
|
this.loadGuild(json.guild_id);
|
|
}
|
|
}
|
|
createChannel(json:channeljson):void{
|
|
json.guild_id??="@me";
|
|
SnowFlake.getSnowFlakeFromID(json.guild_id,Guild).getObject().createChannelpac(json);
|
|
if(json.guild_id===this.lookingguild.id){
|
|
this.loadGuild(json.guild_id);
|
|
}
|
|
}
|
|
delChannel(json:channeljson):void{
|
|
json.guild_id??="@me";
|
|
this.guildids.get(json.guild_id).delChannel(json);
|
|
|
|
if(json.guild_id===this.lookingguild.id){
|
|
this.loadGuild(json.guild_id);
|
|
}
|
|
}
|
|
init():void{
|
|
const location=window.location.href.split("/");
|
|
this.buildservers();
|
|
if(location[3]==="channels"){
|
|
const guild=this.loadGuild(location[4]);
|
|
guild.loadChannel(location[5]);
|
|
this.channelfocus=guild.channelids[location[5]];
|
|
}
|
|
|
|
}
|
|
loaduser():void{
|
|
document.getElementById("username").textContent=this.user.username;
|
|
(document.getElementById("userpfp") as HTMLImageElement).src=this.user.getpfpsrc();
|
|
document.getElementById("status").textContent=this.status;
|
|
}
|
|
isAdmin():boolean{
|
|
return this.lookingguild.isAdmin();
|
|
}
|
|
loadGuild(id:string):Guild{
|
|
let guild=this.guildids.get(id);
|
|
if(!guild){
|
|
guild=this.guildids.get("@me");
|
|
}
|
|
if(this.lookingguild){
|
|
this.lookingguild.html.classList.remove("serveropen");
|
|
}
|
|
if(guild.html){
|
|
guild.html.classList.add("serveropen")
|
|
}
|
|
this.lookingguild=guild;
|
|
document.getElementById("serverName").textContent=guild.properties.name;
|
|
//console.log(this.guildids,id)
|
|
document.getElementById("channels").innerHTML="";
|
|
document.getElementById("channels").appendChild(guild.getHTML());
|
|
return guild;
|
|
}
|
|
buildservers():void{
|
|
const serverlist=document.getElementById("servers");//
|
|
const outdiv=document.createElement("div");
|
|
const img=document.createElement("img");
|
|
const div=document.createElement("div");
|
|
div.classList.add("home","servericon");
|
|
|
|
img.src="/icons/home.svg";
|
|
img.classList.add("svgtheme")
|
|
img["all"]=this.guildids.get("@me");
|
|
this.guildids.get("@me").html=outdiv;
|
|
const unread=document.createElement("div");
|
|
unread.classList.add("unread");
|
|
outdiv.append(unread);
|
|
outdiv.append(div);
|
|
div.appendChild(img);
|
|
|
|
outdiv.classList.add("servernoti")
|
|
serverlist.append(outdiv);
|
|
img.onclick=function(){
|
|
this["all"].loadGuild();
|
|
this["all"].loadChannel();
|
|
}
|
|
const sentdms=document.createElement("div");
|
|
sentdms.classList.add("sentdms");
|
|
serverlist.append(sentdms);
|
|
sentdms.id="sentdms";
|
|
|
|
const br=document.createElement("hr")
|
|
br.classList.add("lightbr");
|
|
serverlist.appendChild(br)
|
|
for(const thing of this.guilds){
|
|
if(thing instanceof Direct){
|
|
(thing as Direct).unreaddms();
|
|
continue;
|
|
}
|
|
const divy=thing.generateGuildIcon();
|
|
serverlist.append(divy);
|
|
}
|
|
{
|
|
const br=document.createElement("hr");
|
|
br.classList.add("lightbr");
|
|
serverlist.appendChild(br);
|
|
br.id="bottomseparator";
|
|
|
|
const div=document.createElement("div");
|
|
div.textContent="+";
|
|
div.classList.add("home","servericon")
|
|
serverlist.appendChild(div)
|
|
div.onclick=_=>{
|
|
this.createGuild();
|
|
}
|
|
const guilddsdiv=document.createElement("div");
|
|
const guildDiscoveryContainer=document.createElement("img");
|
|
guildDiscoveryContainer.src="/icons/explore.svg";
|
|
guildDiscoveryContainer.classList.add("svgtheme");
|
|
guilddsdiv.classList.add("home","servericon");
|
|
guilddsdiv.appendChild(guildDiscoveryContainer);
|
|
serverlist.appendChild(guilddsdiv);
|
|
guildDiscoveryContainer.addEventListener("click", ()=>{
|
|
this.guildDiscovery();
|
|
});
|
|
|
|
}
|
|
this.unreads();
|
|
}
|
|
createGuild(){
|
|
let inviteurl="";
|
|
const error=document.createElement("span");
|
|
|
|
const full=new Fullscreen(["tabs",[
|
|
["Join using invite",[
|
|
"vdiv",
|
|
["textbox",
|
|
"Invite Link/Code",
|
|
"",
|
|
function(){
|
|
inviteurl=this.value;
|
|
}
|
|
],
|
|
["html",error]
|
|
,
|
|
["button",
|
|
"",
|
|
"Submit",
|
|
_=>{
|
|
let parsed="";
|
|
if(inviteurl.includes("/")){
|
|
parsed=inviteurl.split("/")[inviteurl.split("/").length-1]
|
|
}else{
|
|
parsed=inviteurl;
|
|
}
|
|
fetch(this.info.api.toString()+"/v9/invites/"+parsed,{
|
|
method:"POST",
|
|
headers:this.headers,
|
|
}).then(r=>r.json()).then(_=>{
|
|
if(_.message){
|
|
error.textContent=_.message;
|
|
}
|
|
})
|
|
}
|
|
]
|
|
|
|
]],
|
|
["Create Server",[
|
|
"text","Not currently implemented, sorry"
|
|
]]
|
|
]])
|
|
full.show();
|
|
}
|
|
async guildDiscovery() {
|
|
const content=document.createElement("div");
|
|
content.classList.add("guildy");
|
|
content.textContent="Loading...";
|
|
const full=new Fullscreen(["html", content]);
|
|
full.show();
|
|
|
|
const res=await fetch(this.info.api.toString()+"/v9/discoverable-guilds?limit=50", {
|
|
headers: this.headers
|
|
});
|
|
const json=await res.json();
|
|
|
|
content.innerHTML="";
|
|
const title=document.createElement("h2");
|
|
title.textContent="Guild discovery ("+json.total+" entries)";
|
|
content.appendChild(title);
|
|
|
|
const guilds=document.createElement("div");
|
|
guilds.id="discovery-guild-content";
|
|
|
|
json.guilds.forEach(guild=>{
|
|
const content=document.createElement("div");
|
|
content.classList.add("discovery-guild");
|
|
|
|
if(guild.banner) {
|
|
const banner=document.createElement("img");
|
|
banner.classList.add("banner");
|
|
banner.crossOrigin="anonymous";
|
|
banner.src=this.info.cdn.toString()+"icons/"+guild.id+"/"+guild.banner+".png?size=256";
|
|
banner.alt="";
|
|
content.appendChild(banner);
|
|
}
|
|
|
|
const nameContainer=document.createElement("div");
|
|
nameContainer.classList.add("flex");
|
|
const img=document.createElement("img");
|
|
img.classList.add("icon");
|
|
img.crossOrigin="anonymous";
|
|
img.src=this.info.cdn.toString()+(guild.icon ? ("icons/"+guild.id+"/"+guild.icon+".png?size=48") : "embed/avatars/3.png");
|
|
img.alt="";
|
|
nameContainer.appendChild(img);
|
|
|
|
const name=document.createElement("h3");
|
|
name.textContent=guild.name;
|
|
nameContainer.appendChild(name);
|
|
content.appendChild(nameContainer);
|
|
const desc=document.createElement("p");
|
|
desc.textContent=guild.description;
|
|
content.appendChild(desc);
|
|
|
|
content.addEventListener("click", async ()=>{
|
|
const joinRes=await fetch(this.info.api.toString()+"/v9/guilds/"+guild.id+"/members/@me", {
|
|
method: "PUT",
|
|
headers: this.headers
|
|
});
|
|
if(joinRes.ok) full.hide();
|
|
})
|
|
guilds.appendChild(content);
|
|
})
|
|
content.appendChild(guilds);
|
|
}
|
|
messageCreate(messagep):void{
|
|
messagep.d.guild_id??="@me";
|
|
this.guildids.get(messagep.d.guild_id).channelids[messagep.d.channel_id].messageCreate(messagep);
|
|
this.unreads();
|
|
}
|
|
unreads():void{
|
|
for(const thing of this.guilds){
|
|
if(thing.id==="@me"){continue;}
|
|
const html=this.guildhtml.get(thing.id);
|
|
thing.unreads(html);
|
|
}
|
|
}
|
|
typingStart(typing):void{
|
|
if(this.channelfocus.snowflake===typing.d.channel_id){
|
|
const memb=typing.d.member;
|
|
let name;
|
|
if(memb.id===this.user.snowflake){
|
|
console.log("you is typing")
|
|
return;
|
|
}
|
|
console.log("user is typing and you should see it");
|
|
if(memb.nick){
|
|
name=memb.nick;
|
|
}else{
|
|
name=memb.user.username;
|
|
}
|
|
let already=false;
|
|
for(const thing of this.typing){
|
|
if(thing[0]===name){
|
|
thing[1]=new Date().getTime();
|
|
already=true;
|
|
break;
|
|
}
|
|
}
|
|
if(!already){
|
|
this.typing.push([name,new Date().getTime()]);
|
|
}
|
|
setTimeout(this.rendertyping.bind(this),10000);
|
|
this.rendertyping();
|
|
}
|
|
}
|
|
updatepfp(file:Blob):void{
|
|
var reader = new FileReader();
|
|
reader.readAsDataURL(file);
|
|
reader.onload = ()=>{
|
|
fetch(this.info.api.toString()+"/users/@me",{
|
|
method:"PATCH",
|
|
headers:this.headers,
|
|
body:JSON.stringify({
|
|
avatar:reader.result,
|
|
})
|
|
});
|
|
};
|
|
|
|
}
|
|
updatepronouns(pronouns:string):void{
|
|
fetch(this.info.api.toString()+"/users/@me/profile",{
|
|
method:"PATCH",
|
|
headers:this.headers,
|
|
body:JSON.stringify({
|
|
pronouns:pronouns,
|
|
})
|
|
});
|
|
}
|
|
updatebio(bio:string):void{
|
|
fetch(this.info.api.toString()+"/v9/users/@me/profile",{
|
|
method:"PATCH",
|
|
headers:this.headers,
|
|
body:JSON.stringify({
|
|
bio:bio,
|
|
})
|
|
});
|
|
}
|
|
rendertyping():void{
|
|
const typingtext=document.getElementById("typing")
|
|
let build="";
|
|
const array2=[];
|
|
let showing=false;
|
|
let i=0;
|
|
for(const thing of this.typing){
|
|
i++;
|
|
if(thing[1]>new Date().getTime()-5000){
|
|
build+=thing[0];
|
|
array2.push(thing);
|
|
showing=true;
|
|
if(i!==this.typing.length){
|
|
build+=",";
|
|
}
|
|
}
|
|
}
|
|
if(i>1){
|
|
build+=" are typing";
|
|
}else{
|
|
build+=" is typing";
|
|
}
|
|
if(showing){
|
|
typingtext.classList.remove("hidden");
|
|
document.getElementById("typingtext").textContent=build;
|
|
}else{
|
|
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);
|
|
|
|
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(this:Localuser){
|
|
console.log(this);
|
|
hypouser=this.user.clone();
|
|
regen();
|
|
file=null;
|
|
newprouns=null;
|
|
newbio=null;
|
|
}.bind(this))
|
|
|
|
const connectionContainer=document.createElement("div");
|
|
connectionContainer.id="connection-container";
|
|
this.userConnections=new Fullscreen(
|
|
["html",
|
|
connectionContainer
|
|
], () => {}, async () => {
|
|
connectionContainer.innerHTML="";
|
|
|
|
const res=await fetch(this.info.api.toString()+"/v9/connections", {
|
|
headers: this.headers
|
|
});
|
|
const json=await res.json();
|
|
|
|
Object.keys(json).sort(key => json[key].enabled ? -1 : 1).forEach(key => {
|
|
const connection=json[key];
|
|
|
|
const container=document.createElement("div");
|
|
container.textContent=key.charAt(0).toUpperCase() + key.slice(1);
|
|
|
|
if (connection.enabled) {
|
|
container.addEventListener("click", async () => {
|
|
const connectionRes=await fetch(this.info.api.toString()+"/v9/connections/" + key + "/authorize", {
|
|
headers: this.headers
|
|
});
|
|
const connectionJSON=await connectionRes.json();
|
|
window.open(connectionJSON.url, "_blank", "noopener noreferrer");
|
|
})
|
|
} else {
|
|
container.classList.add("disabled")
|
|
container.title="This connection has been disabled server-side."
|
|
}
|
|
|
|
connectionContainer.appendChild(container);
|
|
})
|
|
}
|
|
);
|
|
|
|
let appName="";
|
|
const appListContainer=document.createElement("div");
|
|
appListContainer.id="app-list-container";
|
|
this.devPortal=new Fullscreen(
|
|
["vdiv",
|
|
["hdiv",
|
|
["textbox", "Name:", appName, event => {
|
|
appName=event.target.value;
|
|
}],
|
|
["button",
|
|
"",
|
|
"Create application",
|
|
async () => {
|
|
if (appName.trim().length == 0) return alert("Please enter a name for the application.");
|
|
|
|
const res=await fetch(this.info.api.toString()+"/v9/applications", {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
name: appName
|
|
})
|
|
});
|
|
const json=await res.json();
|
|
this.manageApplication(json.id);
|
|
this.devPortal.hide();
|
|
}
|
|
]
|
|
],
|
|
["html",
|
|
appListContainer
|
|
]
|
|
], () => {}, async () => {
|
|
appListContainer.innerHTML="";
|
|
|
|
const res=await fetch(this.info.api.toString()+"/v9/applications", {
|
|
headers: this.headers
|
|
});
|
|
const json=await res.json();
|
|
|
|
json.forEach(application => {
|
|
const container=document.createElement("div");
|
|
|
|
if (application.cover_image) {
|
|
const cover=document.createElement("img");
|
|
cover.crossOrigin="anonymous";
|
|
cover.src=this.info.cdn.toString()+"/app-icons/" + application.id + "/" + application.cover_image + ".png?size=256";
|
|
cover.alt="";
|
|
cover.loading="lazy";
|
|
container.appendChild(cover);
|
|
}
|
|
|
|
const name=document.createElement("h2");
|
|
name.textContent=application.name + (application.bot ? " (Bot)" : "");
|
|
container.appendChild(name);
|
|
|
|
container.addEventListener("click", async () => {
|
|
this.devPortal.hide();
|
|
this.manageApplication(application.id);
|
|
});
|
|
appListContainer.appendChild(container);
|
|
})
|
|
}
|
|
)
|
|
}
|
|
async manageApplication(appId="") {
|
|
const res=await fetch(this.info.api.toString()+"/v9/applications/" + appId, {
|
|
headers: this.headers
|
|
});
|
|
const json=await res.json();
|
|
|
|
const fields: any={};
|
|
const appDialog=new Fullscreen(
|
|
["vdiv",
|
|
["title",
|
|
"Editing " + json.name
|
|
],
|
|
["vdiv",
|
|
["textbox", "Application name:", json.name, event => {
|
|
fields.name=event.target.value;
|
|
}],
|
|
["mdbox", "Description:", json.description, event => {
|
|
fields.description=event.target.value;
|
|
}],
|
|
["vdiv",
|
|
json.icon ? ["img", this.info.cdn.toString()+"/app-icons/" + appId + "/" + json.icon + ".png?size=128", [128, 128]] : ["text", "No icon"],
|
|
["fileupload", "Application icon:", event => {
|
|
const reader=new FileReader();
|
|
reader.readAsDataURL(event.target.files[0]);
|
|
reader.onload=() => {
|
|
fields.icon=reader.result;
|
|
}
|
|
}]
|
|
]
|
|
],
|
|
["hdiv",
|
|
["textbox", "Privacy policy URL:", json.privacy_policy_url || "", event => {
|
|
fields.privacy_policy_url=event.target.value;
|
|
}],
|
|
["textbox", "Terms of Service URL:", json.terms_of_service_url || "", event => {
|
|
fields.terms_of_service_url=event.target.value;
|
|
}]
|
|
],
|
|
["hdiv",
|
|
["checkbox", "Make bot publicly inviteable?", json.bot_public, event => {
|
|
fields.bot_public=event.target.checked;
|
|
}],
|
|
["checkbox", "Require code grant to invite the bot?", json.bot_require_code_grant, event => {
|
|
fields.bot_require_code_grant=event.target.checked;
|
|
}]
|
|
],
|
|
["hdiv",
|
|
["button",
|
|
"",
|
|
"Save changes",
|
|
async () => {
|
|
const updateRes=await fetch(this.info.api.toString()+"/v9/applications/" + appId, {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify(fields)
|
|
});
|
|
if (updateRes.ok) appDialog.hide();
|
|
else {
|
|
const updateJSON=await updateRes.json();
|
|
alert("An error occurred: " + updateJSON.message);
|
|
}
|
|
}
|
|
],
|
|
["button",
|
|
"",
|
|
(json.bot ? "Manage" : "Add") + " bot",
|
|
async () => {
|
|
if (!json.bot) {
|
|
if (!confirm("Are you sure you want to add a bot to this application? There's no going back.")) return;
|
|
|
|
const updateRes=await fetch(this.info.api.toString()+"/v9/applications/" + appId + "/bot", {
|
|
method: "POST",
|
|
headers: this.headers
|
|
});
|
|
const updateJSON=await updateRes.json();
|
|
alert("Bot token:\n" + updateJSON.token);
|
|
}
|
|
|
|
appDialog.hide();
|
|
this.manageBot(appId);
|
|
}
|
|
]
|
|
]
|
|
]
|
|
)
|
|
appDialog.show();
|
|
}
|
|
async manageBot(appId="") {
|
|
const res=await fetch(this.info.api.toString()+"/v9/applications/" + appId, {
|
|
headers: this.headers
|
|
});
|
|
const json=await res.json();
|
|
if (!json.bot) return alert("For some reason, this application doesn't have a bot (yet).");
|
|
|
|
const fields: any={
|
|
username: json.bot.username,
|
|
avatar: json.bot.avatar ? (this.info.cdn.toString()+"/app-icons/" + appId + "/" + json.bot.avatar + ".png?size=256") : ""
|
|
};
|
|
const botDialog=new Fullscreen(
|
|
["vdiv",
|
|
["title",
|
|
"Editing bot: " + json.bot.username
|
|
],
|
|
["hdiv",
|
|
["textbox", "Bot username:", json.bot.username, event => {
|
|
fields.username=event.target.value
|
|
}],
|
|
["vdiv",
|
|
fields.avatar ? ["img", fields.avatar, [128, 128]] : ["text", "No avatar"],
|
|
["fileupload", "Bot avatar:", event => {
|
|
const reader=new FileReader();
|
|
reader.readAsDataURL(event.target.files[0]);
|
|
reader.onload=() => {
|
|
fields.avatar=reader.result;
|
|
}
|
|
}]
|
|
]
|
|
],
|
|
["hdiv",
|
|
["button",
|
|
"",
|
|
"Save changes",
|
|
async () => {
|
|
const updateRes=await fetch(this.info.api.toString()+"/v9/applications/" + appId + "/bot", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify(fields)
|
|
});
|
|
if (updateRes.ok) botDialog.hide();
|
|
else {
|
|
const updateJSON=await updateRes.json();
|
|
alert("An error occurred: " + updateJSON.message);
|
|
}
|
|
}
|
|
],
|
|
["button",
|
|
"",
|
|
"Reset token",
|
|
async () => {
|
|
if (!confirm("Are you sure you want to reset the bot token? Your bot will stop working until you update it.")) return;
|
|
|
|
const updateRes=await fetch(this.info.api.toString()+"/v9/applications/" + appId + "/bot/reset", {
|
|
method: "POST",
|
|
headers: this.headers
|
|
});
|
|
const updateJSON=await updateRes.json();
|
|
alert("New token:\n" + updateJSON.token);
|
|
botDialog.hide();
|
|
}
|
|
]
|
|
]
|
|
]
|
|
);
|
|
botDialog.show();
|
|
}
|
|
}
|
|
export {Localuser};
|