diff --git a/.dist/audio.js b/.dist/audio.js index 24751a8..7ffb6fb 100644 --- a/.dist/audio.js +++ b/.dist/audio.js @@ -72,6 +72,7 @@ class Voice { return 0; }; } + return new Function(); } play() { if (this.playing) { diff --git a/.dist/channel.js b/.dist/channel.js index 376dadd..3f03995 100644 --- a/.dist/channel.js +++ b/.dist/channel.js @@ -98,7 +98,9 @@ class Channel { copy.classList.add("copybutton", "svgtheme"); copycontainer.append(copy); copycontainer.onclick = _ => { - navigator.clipboard.writeText(text.textContent); + if (text.textContent) { + navigator.clipboard.writeText(text.textContent); + } }; div.append(copycontainer); const update = () => { @@ -180,8 +182,14 @@ class Channel { console.error("Uh..."); } try { - const html = snowflake.getObject().buildhtml(this.messageids.get(this.idToPrev.get(snowflake))); - return html; + const prev = this.idToPrev.get(snowflake); + if (prev) { + const messgage = this.messageids.get(prev); + if (messgage) { + const html = snowflake.getObject().buildhtml(messgage); + return html; + } + } } catch (e) { console.error(e); @@ -209,7 +217,9 @@ class Channel { this.headers = this.owner.headers; this.name = json.name; this.snowflake = new SnowFlake(json.id, this); - this.parent_id = new SnowFlake(json.parent_id, undefined); + if (json.parent_id) { + this.parent_id = SnowFlake.getSnowFlakeFromID(json.parent_id, Channel); + } this.parent = null; this.children = []; this.guild_id = json.guild_id; @@ -222,7 +232,10 @@ class Channel { } ; this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); - this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), this.permission_overwrites.get(thing.id)]); + const permission = this.permission_overwrites.get(thing.id); + if (permission) { + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), permission]); + } } this.topic = json.topic; this.nsfw = json.nsfw; @@ -253,15 +266,16 @@ class Channel { if (!this.hasPermission("VIEW_CHANNEL")) { return false; } - return this.lastmessageid !== this.lastreadmessageid && this.type !== 4 && !!this.lastmessageid.id; + return this.lastmessageid !== this.lastreadmessageid && this.type !== 4 && !!this.lastmessageid; } hasPermission(name, member = this.guild.member) { if (member.isAdmin()) { return true; } for (const thing of member.roles) { - if (this.permission_overwrites.get(thing.id)) { - let perm = this.permission_overwrites.get(thing.id).getPermission(name); + const premission = this.permission_overwrites.get(thing.id); + if (premission) { + let perm = premission.getPermission(name); if (perm) { return perm === 1; } @@ -274,7 +288,10 @@ class Channel { } get canMessage() { if ((0 === this.permission_overwritesar.length) && this.hasPermission("MANAGE_CHANNELS")) { - this.addRoleToPerms(this.guild.roles.find(_ => _.name === "@everyone")); + const role = this.guild.roles.find(_ => _.name === "@everyone"); + if (role) { + this.addRoleToPerms(role); + } } return this.hasPermission("SEND_MESSAGES"); } @@ -282,12 +299,15 @@ class Channel { this.children.sort((a, b) => { return a.position - b.position; }); } resolveparent(guild) { - this.parent = guild.channelids[this.parent_id?.id]; + const parentid = this.parent_id?.id; + if (!parentid) + return false; + this.parent = guild.channelids[parentid]; this.parent ??= null; if (this.parent !== null) { this.parent.children.push(this); } - return this.parent === null; + return this.parent !== null; } calculateReorder() { let position = -1; @@ -301,8 +321,8 @@ class Channel { 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]); + thing.move_id = null; + console.log(this.guild.channelids[thisthing.parent_id.id]); } if (thisthing.position || thisthing.parent_id) { build.push(thisthing); @@ -466,6 +486,8 @@ class Channel { }); div.addEventListener("drop", (event) => { const that = Channel.dragged[0]; + if (!that) + return; event.preventDefault(); if (container) { that.move_id = this.snowflake; @@ -506,7 +528,9 @@ class Channel { } this.guild.headchannels = build; } - div.after(Channel.dragged[1]); + if (Channel.dragged[1]) { + div.after(Channel.dragged[1]); + } } this.guild.calculateReorder(); }); @@ -580,7 +604,9 @@ class Channel { span.textContent = "Replying to " + this.replyingto.author.username; const X = document.createElement("button"); X.onclick = _ => { - this.replyingto.div.classList.remove("replying"); + if (this.replyingto) { + this.replyingto.div.classList.remove("replying"); + } replybox.classList.add("hideReplyBox"); this.replyingto = null; replybox.innerHTML = ""; @@ -618,11 +644,13 @@ class Channel { if (this.localuser.channelfocus && this.localuser.channelfocus.myhtml) { this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); } - this.myhtml.classList.add("viewChannel"); + if (this.myhtml) { + this.myhtml.classList.add("viewChannel"); + } this.guild.prevchannel = this; this.localuser.channelfocus = this; const prom = this.infinite.delete(); - history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.snowflake); + history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.snowflake); document.getElementById("channelname").textContent = "#" + this.name; const loading = document.getElementById("loadingdiv"); Channel.regenLoadingMessages(); @@ -782,7 +810,9 @@ class Channel { } buildmessage(message, next) { const built = message.buildhtml(next); - document.getElementById("messages").prepend(built); + if (built) { + document.getElementById("messages").prepend(built); + } } async buildmessages() { /* @@ -809,7 +839,7 @@ class Channel { if (this.lastreadmessageid && this.lastreadmessageid.getObject()) { id = this.lastreadmessageid; } - else if (id = this.findClosest(this.lastreadmessageid)) { + else if (this.lastreadmessageid && (id = this.findClosest(this.lastreadmessageid))) { } else if (this.lastmessage && this.lastmessage.snowflake) { id = this.goBackIds(this.lastmessage.snowflake, 50); @@ -878,7 +908,7 @@ class Channel { updateChannel(json) { this.type = json.type; this.name = json.name; - this.parent_id = new SnowFlake(json.parent_id, undefined); + this.parent_id = SnowFlake.getSnowFlakeFromID(json.parent_id, Channel); this.parent = null; this.children = []; this.guild_id = json.guild_id; @@ -890,7 +920,10 @@ class Channel { } ; this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); - this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), this.permission_overwrites.get(thing.id)]); + const permisions = this.permission_overwrites.get(thing.id); + if (permisions) { + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), permisions]); + } } this.topic = json.topic; this.nsfw = json.nsfw; @@ -1001,7 +1034,7 @@ class Channel { if (messagez.author === this.localuser.user) { return; } - if (this.localuser.lookingguild.prevchannel === this && document.hasFocus()) { + if (this.localuser.lookingguild?.prevchannel === this && document.hasFocus()) { return; } if (this.notification === "all") { @@ -1069,18 +1102,20 @@ class Channel { } async updateRolePermissions(id, perms) { const permission = this.permission_overwrites.get(id); - permission.allow = perms.allow; - permission.deny = perms.deny; - await fetch(this.info.api + "/channels/" + this.snowflake + "/permissions/" + id, { - method: "PUT", - headers: this.headers, - body: JSON.stringify({ - allow: permission.allow.toString(), - deny: permission.deny.toString(), - id: id, - type: 0 - }) - }); + if (permission) { + permission.allow = perms.allow; + permission.deny = perms.deny; + await fetch(this.info.api + "/channels/" + this.snowflake + "/permissions/" + id, { + method: "PUT", + headers: this.headers, + body: JSON.stringify({ + allow: permission.allow.toString(), + deny: permission.deny.toString(), + id: id, + type: 0 + }) + }); + } } } Channel.setupcontextmenu(); diff --git a/.dist/direct.js b/.dist/direct.js index 56bd414..6acd216 100644 --- a/.dist/direct.js +++ b/.dist/direct.js @@ -102,7 +102,7 @@ class Group extends Channel { this.messageids = new Map(); this.permission_overwrites = new Map(); this.lastmessageid = SnowFlake.getSnowFlakeFromID(json.last_message_id, Message); - this.lastmessageid ??= new SnowFlake("0", undefined); + this.lastmessageid ??= null; this.mentions = 0; this.setUpInfiniteScroller(); this.position = Math.max(this.lastmessageid.getUnixTime(), this.snowflake.getUnixTime()); diff --git a/.dist/guild.js b/.dist/guild.js index 9a83b45..509b613 100644 --- a/.dist/guild.js +++ b/.dist/guild.js @@ -74,7 +74,7 @@ class Guild { settings.show(); } constructor(json, owner, member) { - if (json === -1) { + if (json === -1 || member === null) { return; } if (json.stickers.length) { @@ -109,7 +109,9 @@ class Guild { } this.headchannels = []; for (const thing of this.channels) { - if (thing.resolveparent(this)) { + const parent = thing.resolveparent(this); + console.log(parent, ":3"); + if (!parent) { this.headchannels.push(thing); } } @@ -347,11 +349,13 @@ class Guild { } } getHTML() { + console.log("found :3", this.headchannels); //this.printServers(); this.sortchannels(); this.printServers(); const build = document.createElement("div"); for (const thing of this.headchannels) { + console.log("found :3"); build.appendChild(thing.createguildHTML(this.isAdmin())); } return build; diff --git a/.dist/localuser.js b/.dist/localuser.js index 9d774a7..3634b9a 100644 --- a/.dist/localuser.js +++ b/.dist/localuser.js @@ -19,7 +19,6 @@ class Localuser { initialized; info; headers; - usersettings; userConnections; devPortal; ready; @@ -47,7 +46,6 @@ class Localuser { this.headers = { "Content-type": "application/json; charset=UTF-8", Authorization: this.userinfo.token }; } gottenReady(ready) { - this.usersettings = null; this.initialized = true; this.ready = ready; this.guilds = []; @@ -93,8 +91,10 @@ class Localuser { } } outoffocus() { - document.getElementById("servers").innerHTML = ""; - document.getElementById("channels").innerHTML = ""; + const servers = document.getElementById("servers"); + servers.innerHTML = ""; + const channels = document.getElementById("channels"); + channels.innerHTML = ""; if (this.channelfocus) { this.channelfocus.infinite.delete(); } @@ -114,32 +114,10 @@ class Localuser { } swapped = false; async initwebsocket() { - 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": null, //new Date().getTime() - "activities": [], - "afk": false - } - } - })); - }); + let returny; + const ws = new WebSocket(this.serverurls.gateway.toString() + "?encoding=json&v=9" + (DecompressionStream ? "&compress=zlib-stream" : "")); + ; + this.ws = ws; let ds; let w; let r; @@ -150,63 +128,91 @@ class Localuser { w = ds.writable.getWriter(); r = ds.readable.getReader(); arr = new Uint8Array(); - const textdecode = new TextDecoder(); - (async () => { - while (true) { - const read = await r.read(); - const data = textdecode.decode(read.value); - build += data; - try { - const temp = JSON.parse(build); - build = ""; - if (temp.op === 0 && temp.t === "READY") { - returny(); - } - await this.handleEvent(temp); - } - catch { } - } - })(); } - let order = new Promise((res) => (res())); - this.ws.addEventListener('message', async (event) => { - const temp2 = order; - let res; - order = new Promise((r) => (res = r)); - await temp2; - let temp; - try { - 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; + const promise = new Promise((res) => { + returny = res; + ws.addEventListener('open', (_event) => { + console.log('WebSocket connected'); + 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": null, //new Date().getTime() + "activities": [], + "afk": false + } } - w.write(arr.buffer); - arr = new Uint8Array(); - return; //had to move the while loop due to me being dumb - } - else { - temp = JSON.parse(event.data); - } - if (temp.op === 0 && temp.t === "READY") { - returny(); - } - await this.handleEvent(temp); - } - catch (e) { - console.error(e); - } - finally { - res(); + })); + }); + const textdecode = new TextDecoder(); + if (DecompressionStream) { + (async () => { + while (true) { + const read = await r.read(); + const data = textdecode.decode(read.value); + build += data; + try { + const temp = JSON.parse(build); + build = ""; + if (temp.op === 0 && temp.t === "READY") { + returny(); + } + await this.handleEvent(temp); + } + catch { } + } + })(); } }); - this.ws.addEventListener("close", async (event) => { + let order = new Promise((res) => (res())); + ws.addEventListener('message', async (event) => { + const temp2 = order; + order = new Promise(async (res) => { + await temp2; + let temp; + try { + 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(); + return; //had to move the while loop due to me being dumb + } + else { + temp = JSON.parse(event.data); + } + if (temp.op === 0 && temp.t === "READY") { + returny(); + } + await this.handleEvent(temp); + } + catch (e) { + console.error(e); + } + finally { + res(); + } + }); + }); + ws.addEventListener("close", async (event) => { this.ws = undefined; console.log("WebSocket closed with code " + event.code); this.unload(); @@ -264,8 +270,9 @@ class Localuser { this.initwebsocket().then(() => { this.loaduser(); this.init(); - document.getElementById("loading").classList.add("doneloading"); - document.getElementById("loading").classList.remove("loading"); + const loading = document.getElementById("loading"); + loading.classList.add("doneloading"); + loading.classList.remove("loading"); console.log("done loading"); }); }, 200 + (this.errorBackoff * 2800)); @@ -330,9 +337,11 @@ class Localuser { 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(); + if (guildy) { + this.guildids.delete(temp.d.id); + this.guilds.splice(this.guilds.indexOf(guildy), 1); + guildy.html.remove(); + } break; } case "GUILD_CREATE": @@ -371,6 +380,8 @@ class Localuser { } } else if (temp.op === 10) { + if (!this.ws) + return; console.log("heartbeat down"); this.heartbeat_interval = temp.d.heartbeat_interval; this.ws.send(JSON.stringify({ op: 1, d: this.lastSequence })); @@ -395,21 +406,25 @@ class Localuser { } updateChannel(json) { SnowFlake.getSnowFlakeFromID(json.guild_id, Guild).getObject().updateChannel(json); - if (json.guild_id === this.lookingguild.id) { + if (json.guild_id === this.lookingguild?.id) { this.loadGuild(json.guild_id); } } createChannel(json) { json.guild_id ??= "@me"; SnowFlake.getSnowFlakeFromID(json.guild_id, Guild).getObject().createChannelpac(json); - if (json.guild_id === this.lookingguild.id) { + if (json.guild_id === this.lookingguild?.id) { this.loadGuild(json.guild_id); } } delChannel(json) { - json.guild_id ??= "@me"; - this.guildids.get(json.guild_id).delChannel(json); - if (json.guild_id === this.lookingguild.id) { + let guild_id = json.guild_id; + guild_id ??= "@me"; + const guild = this.guildids.get(guild_id); + if (guild) { + guild.delChannel(json); + } + if (json.guild_id === this.lookingguild?.id) { this.loadGuild(json.guild_id); } } @@ -418,6 +433,9 @@ class Localuser { this.buildservers(); if (location[3] === "channels") { const guild = this.loadGuild(location[4]); + if (!guild) { + return; + } guild.loadChannel(location[5]); this.channelfocus = guild.channelids[location[5]]; } @@ -428,7 +446,12 @@ class Localuser { document.getElementById("status").textContent = this.status; } isAdmin() { - return this.lookingguild.isAdmin(); + if (this.lookingguild) { + return this.lookingguild.isAdmin(); + } + else { + return false; + } } loadGuild(id) { let guild = this.guildids.get(id); @@ -438,14 +461,19 @@ class Localuser { if (this.lookingguild) { this.lookingguild.html.classList.remove("serveropen"); } + if (!guild) + return; 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()); + const channels = document.getElementById("channels"); + channels.innerHTML = ""; + const html = guild.getHTML(); + channels.appendChild(html); + console.log("found :3", html); return guild; } buildservers() { @@ -553,8 +581,10 @@ class Localuser { ["vdiv", ["title", "Create a guild"], ["fileupload", "Icon:", function (event) { - const reader = new FileReader(); const target = event.target; + if (!target.files) + return; + const reader = new FileReader(); reader.readAsDataURL(target.files[0]); reader.onload = () => { fields.icon = reader.result; @@ -640,7 +670,10 @@ class Localuser { } messageCreate(messagep) { messagep.d.guild_id ??= "@me"; - this.guildids.get(messagep.d.guild_id).channelids[messagep.d.channel_id].messageCreate(messagep); + const guild = this.guildids.get(messagep.d.guild_id); + if (!guild) + return; + guild.channelids[messagep.d.channel_id].messageCreate(messagep); this.unreads(); } unreads() { @@ -653,8 +686,10 @@ class Localuser { } } async typingStart(typing) { - if (this.channelfocus.id === typing.d.channel_id) { + if (this.channelfocus?.id === typing.d.channel_id) { const guild = this.guildids.get(typing.d.guild_id); + if (!guild) + return; const memb = await Member.new(typing.d.member, guild); if (memb.id === this.user.id) { console.log("you is typing"); @@ -742,7 +777,8 @@ class Localuser { } if (showing) { typingtext.classList.remove("hidden"); - document.getElementById("typingtext").textContent = build; + const typingtext2 = document.getElementById("typingtext"); + typingtext2.textContent = build; } else { typingtext.classList.add("hidden"); @@ -750,7 +786,6 @@ class Localuser { } showusersettings() { const settings = new Settings("Settings"); - this.usersettings = settings; { const userOptions = settings.addButton("User Settings", { ltr: true }); const hypotheticalProfile = document.createElement("div"); @@ -1184,15 +1219,15 @@ class Localuser { if (guildid === "@me") { return undefined; } - if (!this.waitingmembers.has(guildid)) { - this.waitingmembers.set(guildid, new Map()); + let guildmap = this.waitingmembers.get(guildid); + if (!guildmap) { + guildmap = new Map(); + this.waitingmembers.set(guildid, guildmap); } - let res; - const promise = new Promise((r) => { - res = r; + const promise = new Promise((res) => { + guildmap.set(id, res); + this.getmembers(); }); - this.waitingmembers.get(guildid).set(id, res); - this.getmembers(); return await promise; } fetchingmembers = new Map(); @@ -1200,11 +1235,15 @@ class Localuser { noncebuild = new Map(); async gotChunk(chunk) { for (const thing of chunk.presences) { - this.presences.set(thing.user.id, thing); + if (thing.user) { + this.presences.set(thing.user.id, thing); + } } console.log(chunk); chunk.members ??= []; const arr = this.noncebuild.get(chunk.nonce); + if (!arr) + return; arr[0] = arr[0].concat(chunk.members); if (chunk.not_found) { arr[1] = chunk.not_found; @@ -1214,14 +1253,14 @@ class Localuser { console.log("got through"); this.noncebuild.delete(chunk.nonce); const func = this.noncemap.get(chunk.nonce); + if (!func) + return; func([arr[0], arr[1]]); this.noncemap.delete(chunk.nonce); } } async getmembers() { - let res; - const promise = new Promise(r => res = r); - setTimeout(res, 10); + const promise = new Promise(res => { setTimeout(res, 10); }); await promise; //allow for more to be sent at once :P if (this.ws) { this.waitingmembers.forEach(async (value, guildid) => { @@ -1242,36 +1281,42 @@ class Localuser { return; } ; - let res; - const promise = new Promise((r) => { - res = r; + const promise = new Promise((res) => { + const nonce = "" + Math.floor(Math.random() * 100000000000); + this.noncemap.set(nonce, res); + this.noncebuild.set(nonce, [[], [], []]); + if (!this.ws) + return; + this.ws.send(JSON.stringify({ + op: 8, + d: { + user_ids: build, + guild_id: guildid, + limit: 100, + nonce, + presences: true + } + })); + this.fetchingmembers.set(guildid, true); }); - const nonce = "" + Math.floor(Math.random() * 100000000000); - this.noncemap.set(nonce, res); - this.noncebuild.set(nonce, [[], [], []]); - this.ws.send(JSON.stringify({ - op: 8, - d: { - user_ids: build, - guild_id: guildid, - limit: 100, - nonce, - presences: true - } - })); - this.fetchingmembers.set(guildid, true); const prom = await promise; ; const data = prom[0]; for (const thing of data) { if (value.has(thing.id)) { - value.get(thing.id)(thing); + const func = value.get(thing.id); + if (!func) + continue; + func(thing); value.delete(thing.id); } } for (const thing of prom[1]) { if (value.has(thing)) { - value.get(thing)(undefined); + const func = value.get(thing); + if (!func) + continue; + func(undefined); value.delete(thing); } } @@ -1320,7 +1365,7 @@ let fixsvgtheme; else if (g === max) { h = 2 + (b - r) / (max - min); } - else if (b === max) { + else { h = 4 + (r - g) / (max - min); } } diff --git a/tsconfig.json b/tsconfig.json index 9dba0e0..e59ead1 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,7 +7,9 @@ "esModuleInterop": true, "outDir": "./.dist", "removeComments": false, - "noImplicitThis":true + "noImplicitThis":true, + "useUnknownInCatchVariables":true, + "strictNullChecks":true }, "include": [ "./webpage/*.ts" diff --git a/webpage/audio.ts b/webpage/audio.ts index fb88160..e4993b1 100644 --- a/webpage/audio.ts +++ b/webpage/audio.ts @@ -78,6 +78,7 @@ class Voice{ return 0; } } + return new Function(); } play():void{ if(this.playing){ diff --git a/webpage/channel.ts b/webpage/channel.ts index 139b558..259f9b3 100644 --- a/webpage/channel.ts +++ b/webpage/channel.ts @@ -14,7 +14,7 @@ import { channeljson, messagejson, readyjson } from "./jsontypes.js"; declare global { interface NotificationOptions { - image?: string + image?: string|null|undefined } } class Channel{ @@ -24,8 +24,8 @@ class Channel{ headers:Localuser["headers"]; name:string; snowflake:SnowFlake; - parent_id:SnowFlake; - parent:Channel; + parent_id:SnowFlake|null; + parent:Channel|null; children:Channel[]; guild_id:string; messageids:Map,Message>; @@ -34,19 +34,19 @@ class Channel{ topic:string; nsfw:boolean; position:number; - lastreadmessageid:SnowFlake; - lastmessageid:SnowFlake; + lastreadmessageid:SnowFlake|null; + lastmessageid:SnowFlake|null; mentions:number; lastpin:string; - move_id:SnowFlake; + move_id:SnowFlake|null; typing:number; message_notifications:number; allthewayup:boolean; static contextmenu=new Contextmenu("channel menu"); - replyingto:Message; + replyingto:Message|null; infinite:InfiniteScroller; - idToPrev:Map,SnowFlake>=new Map(); - idToNext:Map,SnowFlake>=new Map(); + idToPrev:Map,SnowFlake|null>=new Map(); + idToNext:Map,SnowFlake|null>=new Map(); get id(){ return this.snowflake.id; } @@ -112,7 +112,9 @@ class Channel{ copy.classList.add("copybutton","svgtheme"); copycontainer.append(copy); copycontainer.onclick=_=>{ - navigator.clipboard.writeText(text.textContent); + if(text.textContent){ + navigator.clipboard.writeText(text.textContent); + } } div.append(copycontainer); const update=()=>{ @@ -165,7 +167,7 @@ class Channel{ }) } setUpInfiniteScroller(){ - this.infinite=new InfiniteScroller(async function(this:Channel,id:string,offset:number):Promise{ + this.infinite=new InfiniteScroller(async function(this:Channel,id:string,offset:number):Promise{ const snowflake=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake; if(offset===1){ if(this.idToPrev.has(snowflake)){ @@ -193,8 +195,15 @@ class Channel{ console.error("Uh...") } try{ - const html=snowflake.getObject().buildhtml(this.messageids.get(this.idToPrev.get(snowflake))); - return html; + const prev=this.idToPrev.get(snowflake); + if(prev){ + const messgage=this.messageids.get(prev); + if(messgage){ + const html=snowflake.getObject().buildhtml(messgage); + return html; + } + } + }catch(e){ console.error(e); } @@ -221,7 +230,9 @@ class Channel{ this.headers=this.owner.headers; this.name=json.name; this.snowflake=new SnowFlake(json.id,this); - this.parent_id=new SnowFlake(json.parent_id,undefined); + if(json.parent_id){ + this.parent_id=SnowFlake.getSnowFlakeFromID(json.parent_id,Channel); + } this.parent=null; this.children=[]; this.guild_id=json.guild_id; @@ -231,7 +242,10 @@ class Channel{ 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)); - this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),this.permission_overwrites.get(thing.id)]); + const permission=this.permission_overwrites.get(thing.id); + if(permission){ + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),permission]); + } } this.topic=json.topic; @@ -261,15 +275,16 @@ class Channel{ } get hasunreads():boolean{ if(!this.hasPermission("VIEW_CHANNEL")){return false;} - return this.lastmessageid!==this.lastreadmessageid&&this.type!==4&&!!this.lastmessageid.id; + return this.lastmessageid!==this.lastreadmessageid&&this.type!==4&&!!this.lastmessageid; } hasPermission(name:string,member=this.guild.member):boolean{ if(member.isAdmin()){ return true; } for(const thing of member.roles){ - if(this.permission_overwrites.get(thing.id)){ - let perm=this.permission_overwrites.get(thing.id).getPermission(name); + const premission=this.permission_overwrites.get(thing.id); + if(premission){ + let perm=premission.getPermission(name); if(perm){ return perm===1; } @@ -282,7 +297,10 @@ class Channel{ } get canMessage():boolean{ if((0===this.permission_overwritesar.length)&&this.hasPermission("MANAGE_CHANNELS")){ - this.addRoleToPerms(this.guild.roles.find(_=>_.name==="@everyone")); + const role=this.guild.roles.find(_=>_.name==="@everyone"); + if(role){ + this.addRoleToPerms(role); + } } return this.hasPermission("SEND_MESSAGES"); } @@ -290,18 +308,20 @@ class Channel{ this.children.sort((a,b)=>{return a.position-b.position}); } resolveparent(guild:Guild){ - this.parent=guild.channelids[this.parent_id?.id]; + const parentid=this.parent_id?.id; + if(!parentid) return false; + this.parent=guild.channelids[parentid]; this.parent??=null; if(this.parent!==null){ this.parent.children.push(this); } - return this.parent===null; + return this.parent!==null; } calculateReorder(){ let position=-1; - let build=[]; + let build:{id:SnowFlake,position:number|undefined,parent_id:SnowFlake|undefined}[]=[]; for(const thing of this.children){ - const thisthing={id:thing.snowflake,position:undefined,parent_id:undefined}; + const thisthing:{id:SnowFlake,position:number|undefined,parent_id:SnowFlake|undefined}={id:thing.snowflake,position:undefined,parent_id:undefined}; if(thing.position { const that=Channel.dragged[0]; + if(!that) return; event.preventDefault(); if(container){ that.move_id=this.snowflake; @@ -479,7 +500,7 @@ class Channel{ that.parent.children.splice(that.parent.children.indexOf(that),1); } that.parent=this; - (container as HTMLElement).prepend(Channel.dragged[1]); + (container as HTMLElement).prepend(Channel.dragged[1] as HTMLDivElement); this.children.unshift(that); }else{ console.log(this,Channel.dragged); @@ -491,7 +512,7 @@ class Channel{ } that.parent=this.parent; if(that.parent){ - const build=[]; + const build:Channel[]=[]; for(let i=0;i{ - this.replyingto.div.classList.remove("replying"); + if(this.replyingto){ + this.replyingto.div.classList.remove("replying"); + } replybox.classList.add("hideReplyBox"); this.replyingto=null; replybox.innerHTML=""; @@ -624,14 +649,15 @@ class Channel{ if(this.localuser.channelfocus&&this.localuser.channelfocus.myhtml){ this.localuser.channelfocus.myhtml.classList.remove("viewChannel"); } - this.myhtml.classList.add("viewChannel") - + if(this.myhtml){ + this.myhtml.classList.add("viewChannel") + } this.guild.prevchannel=this; this.localuser.channelfocus=this; const prom=this.infinite.delete(); - history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.snowflake); - document.getElementById("channelname").textContent="#"+this.name; - const loading=document.getElementById("loadingdiv"); + history.pushState(null, "","/channels/"+this.guild_id+"/"+this.snowflake); + (document.getElementById("channelname") as HTMLSpanElement).textContent="#"+this.name; + const loading=document.getElementById("loadingdiv") as HTMLDivElement; Channel.regenLoadingMessages(); loading.classList.add("loading"); await this.putmessages(); @@ -644,10 +670,10 @@ class Channel{ await this.buildmessages(); //loading.classList.remove("loading"); console.log(this); - document.getElementById("typebox").contentEditable=""+this.canMessage; + (document.getElementById("typebox") as HTMLDivElement).contentEditable=""+this.canMessage; } static regenLoadingMessages(){ - const loading=document.getElementById("loadingdiv"); + const loading=document.getElementById("loadingdiv") as HTMLDivElement; loading.innerHTML=""; for(let i=0;i<15;i++){ const div=document.createElement("div"); @@ -682,7 +708,7 @@ class Channel{ if(response.length!==100){ this.allthewayup=true; } - let prev:Message=undefined; + let prev:Message|undefined=undefined; for(const thing of response){ const message=new Message(thing,this); if(prev){ @@ -698,7 +724,7 @@ class Channel{ } } delChannel(json:channeljson){ - const build=[]; + const build:Channel[]=[]; for(const thing of this.children){ if(thing.id!==json.id){ build.push(thing) @@ -788,7 +814,9 @@ class Channel{ } buildmessage(message:Message,next:Message){ const built=message.buildhtml(next); - document.getElementById("messages").prepend(built); + if(built){ + (document.getElementById("messages") as HTMLDivElement).prepend(built); + } } async buildmessages(){ /* @@ -803,17 +831,17 @@ class Channel{ private async tryfocusinfinate(){ if(this.infinitefocus) return; this.infinitefocus=true; - const messages=document.getElementById("channelw"); + const messages=document.getElementById("channelw") as HTMLDivElement; for(const thing of messages.getElementsByClassName("messagecontainer")){ thing.remove(); } - const loading=document.getElementById("loadingdiv"); + const loading=document.getElementById("loadingdiv") as HTMLDivElement; const removetitle=document.getElementById("removetitle"); //messages.innerHTML=""; - let id:SnowFlake; + let id:SnowFlake|undefined; if(this.lastreadmessageid&&this.lastreadmessageid.getObject()){ id=this.lastreadmessageid; - }else if(id=this.findClosest(this.lastreadmessageid)){ + }else if(this.lastreadmessageid&&(id=this.findClosest(this.lastreadmessageid))){ }else if(this.lastmessage&&this.lastmessage.snowflake){ id=this.goBackIds(this.lastmessage.snowflake,50); @@ -842,7 +870,7 @@ class Channel{ //this.infinite.focus(id.id,false); } - private goBackIds(id:SnowFlake,back:number,returnifnotexistant=true):SnowFlake{ + private goBackIds(id:SnowFlake,back:number,returnifnotexistant=true):SnowFlake|undefined{ while(back!==0){ const nextid=this.idToPrev.get(id); if(nextid){ @@ -860,7 +888,7 @@ class Channel{ } private findClosest(snowflake:SnowFlake){ if(!this.lastmessage) return; - let flake=this.lastmessage.snowflake; + let flake:SnowFlake|null|undefined=this.lastmessage.snowflake; if(!snowflake){return}; const time=snowflake.getUnixTime(); let flaketime=flake.getUnixTime() @@ -876,7 +904,7 @@ class Channel{ updateChannel(json:channeljson){ this.type=json.type; this.name=json.name; - this.parent_id=new SnowFlake(json.parent_id,undefined); + this.parent_id=SnowFlake.getSnowFlakeFromID(json.parent_id,Channel); this.parent=null; this.children=[]; this.guild_id=json.guild_id; @@ -885,7 +913,10 @@ class Channel{ 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)); - this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),this.permission_overwrites.get(thing.id)]); + const permisions=this.permission_overwrites.get(thing.id); + if(permisions){ + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),permisions]); + } } this.topic=json.topic; this.nsfw=json.nsfw; @@ -901,7 +932,7 @@ class Channel{ }) } get notification(){ - let notinumber=this.message_notifications; + let notinumber:number|null=this.message_notifications; if(+notinumber===3){notinumber=null;} notinumber??=this.guild.message_notifications; switch(+notinumber){ @@ -915,7 +946,8 @@ class Channel{ return "default"; } } - async sendMessage(content:string,{attachments=[],embeds=[],replyingto=null}){ + async sendMessage(content:string,{attachments=[],embeds=[],replyingto=null}: + {attachments,embeds,replyingto:Message|null}){ let replyjson:any; if(replyingto){ replyjson= @@ -988,7 +1020,7 @@ class Channel{ if(messagez.author===this.localuser.user){ return; } - if(this.localuser.lookingguild.prevchannel===this&&document.hasFocus()){ + if(this.localuser.lookingguild?.prevchannel===this&&document.hasFocus()){ return; } if(this.notification==="all"){ @@ -1006,13 +1038,13 @@ class Channel{ if (!("Notification" in window)) { } else if (Notification.permission === "granted") { - let noticontent=message.content.textContent; + 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; + let imgurl:null|string=null; const images=message.getimages(); if(images.length){ const image = images[0]; @@ -1052,18 +1084,20 @@ class Channel{ } async updateRolePermissions(id:string,perms:Permissions){ const permission=this.permission_overwrites.get(id); - permission.allow=perms.allow; - permission.deny=perms.deny; - await fetch(this.info.api+"/channels/"+this.snowflake+"/permissions/"+id,{ - method:"PUT", - headers:this.headers, - body:JSON.stringify({ - allow:permission.allow.toString(), - deny:permission.deny.toString(), - id:id, - type:0 + if(permission){ + permission.allow=perms.allow; + permission.deny=perms.deny; + await fetch(this.info.api+"/channels/"+this.snowflake+"/permissions/"+id,{ + method:"PUT", + headers:this.headers, + body:JSON.stringify({ + allow:permission.allow.toString(), + deny:permission.deny.toString(), + id:id, + type:0 + }) }) - }) + } } } Channel.setupcontextmenu(); diff --git a/webpage/contextmenu.ts b/webpage/contextmenu.ts index 4f710a8..d76888b 100644 --- a/webpage/contextmenu.ts +++ b/webpage/contextmenu.ts @@ -1,7 +1,7 @@ class Contextmenu{ static currentmenu; name:string; - buttons:[string,Function,string,Function,Function,string][]; + buttons:[string,Function,string|null,Function,Function,string][]; div:HTMLDivElement; static setup(){ Contextmenu.currentmenu=""; @@ -19,8 +19,8 @@ class Contextmenu{ this.name=name; this.buttons=[] } - addbutton(text:string,onclick:Function,img=null,shown=_=>true,enabled=_=>true){ - this.buttons.push([text,onclick,img,shown,enabled,"button"]) + addbutton(text:string,onclick:Function,img:null|string=null,shown=_=>true,enabled=_=>true){ + this.buttons.push([text,onclick,img,shown,enabled,"button"]); return {}; } addsubmenu(text:string,onclick:(e:MouseEvent)=>void,img=null,shown=_=>true,enabled=_=>true){ diff --git a/webpage/direct.ts b/webpage/direct.ts index c67b7db..07ac3ca 100644 --- a/webpage/direct.ts +++ b/webpage/direct.ts @@ -108,7 +108,7 @@ class Group extends Channel{ this.messageids=new Map(); this.permission_overwrites=new Map(); this.lastmessageid=SnowFlake.getSnowFlakeFromID(json.last_message_id,Message); - this.lastmessageid??=new SnowFlake("0",undefined); + this.lastmessageid??=null; this.mentions=0; this.setUpInfiniteScroller(); this.position=Math.max(this.lastmessageid.getUnixTime(),this.snowflake.getUnixTime()); diff --git a/webpage/guild.ts b/webpage/guild.ts index b662a64..d771dfe 100644 --- a/webpage/guild.ts +++ b/webpage/guild.ts @@ -18,7 +18,7 @@ class Guild{ properties roles:Role[]; roleids:Map,Role>; - prevchannel:Channel; + prevchannel:Channel|undefined; message_notifications:number; headchannels:Channel[]; position:number; @@ -75,15 +75,15 @@ class Guild{ const settings=new Settings("Settings for "+this.properties.name); const s1=settings.addButton("roles"); - const permlist=[]; + const permlist:[SnowFlake,Permissions][]=[]; for(const thing of this.roles){ permlist.push([thing.snowflake,thing.permissions]); } s1.options.push(new RoleList(permlist,this,this.updateRolePermissions.bind(this))); settings.show(); } - constructor(json:guildjson|-1,owner:Localuser,member:memberjson|User){ - if(json===-1){ + constructor(json:guildjson|-1,owner:Localuser,member:memberjson|User|null){ + if(json===-1||member===null){ return; } if(json.stickers.length){ @@ -118,7 +118,9 @@ class Guild{ } this.headchannels=[]; for(const thing of this.channels){ - if(thing.resolveparent(this)){ + const parent=thing.resolveparent(this); + console.log(parent,":3") + if(!parent){ this.headchannels.push(thing); } } @@ -333,7 +335,7 @@ class Guild{ headers:this.headers, }) } - unreads(html=undefined){ + unreads(html:HTMLElement|undefined=undefined){ if(html){ this.html=html; }else{ @@ -355,11 +357,14 @@ class Guild{ } } getHTML(){ + console.log("found :3",this.headchannels) //this.printServers(); this.sortchannels(); this.printServers(); const build=document.createElement("div"); + for(const thing of this.headchannels){ + console.log("found :3") build.appendChild(thing.createguildHTML(this.isAdmin())); } return build; diff --git a/webpage/localuser.ts b/webpage/localuser.ts index b3da697..e5ecc13 100644 --- a/webpage/localuser.ts +++ b/webpage/localuser.ts @@ -23,7 +23,6 @@ class Localuser{ initialized:boolean; info:Specialuser["serverurls"]; headers:{"Content-type":string,Authorization:string}; - usersettings:Settings; userConnections:Dialog; devPortal:Dialog; ready:readyjson; @@ -31,10 +30,10 @@ class Localuser{ guildids:Map; user:User; status:string; - channelfocus:Channel; - lookingguild:Guild; + channelfocus:Channel|null; + lookingguild:Guild|null; guildhtml:Map; - ws:WebSocket; + ws:WebSocket|undefined; typing:Map=new Map(); connectionSucceed=0; errorBackoff=0; @@ -51,14 +50,13 @@ class Localuser{ 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.user.setstatus("online"); - this.mfa_enabled=ready.d.user.mfa_enabled; + this.mfa_enabled=ready.d.user.mfa_enabled as boolean; this.userinfo.username=this.user.username; this.userinfo.pfpsrc=this.user.getpfpsrc(); this.status=this.ready.d.user_settings.status; @@ -84,7 +82,7 @@ class Localuser{ for(const thing of ready.d.user_guild_settings.entries){ - this.guildids.get(thing.guild_id).notisetting(thing); + (this.guildids.get(thing.guild_id) as Guild).notisetting(thing); } for(const thing of ready.d.read_state.entries){ @@ -95,12 +93,14 @@ class Localuser{ continue } const guildid=guild.snowflake; - this.guildids.get(guildid.id).channelids[thing.channel_id].readStateInfo(thing); + (this.guildids.get(guildid.id) as Guild).channelids[thing.channel_id].readStateInfo(thing); } } outoffocus():void{ - document.getElementById("servers").innerHTML=""; - document.getElementById("channels").innerHTML=""; + const servers=document.getElementById("servers") as HTMLDivElement; + servers.innerHTML=""; + const channels=document.getElementById("channels") as HTMLDivElement; + channels.innerHTML=""; if(this.channelfocus){ this.channelfocus.infinite.delete(); } @@ -120,33 +120,9 @@ class Localuser{ } swapped=false; async initwebsocket():Promise{ - 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": null,//new Date().getTime() - "activities": [], - "afk": false - } - } - })) - }); - + let returny:()=>void; + const ws= new WebSocket(this.serverurls.gateway.toString()+"?encoding=json&v=9"+(DecompressionStream?"&compress=zlib-stream":""));; + this.ws=ws; let ds:DecompressionStream; let w:WritableStreamDefaultWriter; let r:ReadableStreamDefaultReader; @@ -157,71 +133,99 @@ class Localuser{ w= ds.writable.getWriter(); r=ds.readable.getReader(); arr=new Uint8Array(); - const textdecode=new TextDecoder(); - (async ()=>{ - while(true){ - const read=await r.read(); - const data=textdecode.decode(read.value); - build+=data; - try{ - const temp=JSON.parse(build); - build=""; - if(temp.op===0&&temp.t==="READY"){ - returny(); - } - await this.handleEvent(temp); - }catch{} - } - })(); + } - - - let order=new Promise((res)=>(res())); - - this.ws.addEventListener('message', async (event) => { - const temp2=order; - let res:Function; - order=new Promise((r)=>(res=r)) - await temp2; - let temp:{op:number,t:string}; - try{ - 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; + const promise=new Promise((res)=>{ + returny=res + ws.addEventListener('open', (_event) => { + console.log('WebSocket connected'); + 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": null,//new Date().getTime() + "activities": [], + "afk": false + } } - w.write(arr.buffer); - arr=new Uint8Array(); - return;//had to move the while loop due to me being dumb - }else{ - temp=JSON.parse(event.data); - } - if(temp.op===0&&temp.t==="READY"){ - returny(); - } - await this.handleEvent(temp); - }catch(e){ - console.error(e); - }finally{ - res(); + })) + }); + const textdecode=new TextDecoder(); + if(DecompressionStream){ + (async ()=>{ + while(true){ + const read=await r.read(); + const data=textdecode.decode(read.value); + build+=data; + try{ + const temp=JSON.parse(build); + build=""; + if(temp.op===0&&temp.t==="READY"){ + returny(); + } + await this.handleEvent(temp); + }catch{} + } + })(); } }); - this.ws.addEventListener("close",async event => { + let order=new Promise((res)=>(res())); + + ws.addEventListener('message', async (event) => { + const temp2=order; + order=new Promise(async (res)=>{ + await temp2; + let temp:{op:number,t:string}; + try{ + 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(); + return;//had to move the while loop due to me being dumb + }else{ + temp=JSON.parse(event.data); + } + if(temp.op===0&&temp.t==="READY"){ + returny(); + } + await this.handleEvent(temp); + }catch(e){ + console.error(e); + }finally{ + res(); + } + }) + }); + + ws.addEventListener("close",async event => { this.ws=undefined; console.log("WebSocket closed with code " + event.code); this.unload(); - document.getElementById("loading").classList.remove("doneloading"); - document.getElementById("loading").classList.add("loading"); + (document.getElementById("loading") as HTMLElement).classList.remove("doneloading"); + (document.getElementById("loading") as HTMLElement).classList.add("loading"); this.fetchingmembers=new Map(); this.noncemap=new Map(); this.noncebuild=new Map(); @@ -230,7 +234,7 @@ class Localuser{ else this.errorBackoff++; this.connectionSucceed=0; - document.getElementById("load-desc").innerHTML="Unable to connect to the Spacebar server, retrying in " + Math.round(0.2 + (this.errorBackoff*2.8)) + " seconds..."; + (document.getElementById("load-desc") as HTMLElement).innerHTML="Unable to connect to the Spacebar server, retrying in " + Math.round(0.2 + (this.errorBackoff*2.8)) + " seconds..."; switch(this.errorBackoff){//try to recover from bad domain case 3: const newurls=await getapiurls(this.info.wellknown); @@ -270,16 +274,17 @@ class Localuser{ } setTimeout(() => { if(this.swapped) return; - document.getElementById("load-desc").textContent="Retrying..."; + (document.getElementById("load-desc") as HTMLElement).textContent="Retrying..."; this.initwebsocket().then(() => { this.loaduser(); this.init(); - document.getElementById("loading").classList.add("doneloading"); - document.getElementById("loading").classList.remove("loading"); + const loading=document.getElementById("loading") as HTMLElement; + loading.classList.add("doneloading"); + 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."; + } else (document.getElementById("load-desc") as HTMLElement).textContent="Unable to connect to the Spacebar server. Please try logging out and back in."; }); await promise; @@ -338,9 +343,11 @@ class Localuser{ 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(); + if(guildy){ + this.guildids.delete(temp.d.id); + this.guilds.splice(this.guilds.indexOf(guildy),1); + guildy.html.remove(); + } break; } case "GUILD_CREATE": @@ -348,7 +355,7 @@ class Localuser{ 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")); + (document.getElementById("servers") as HTMLDivElement).insertBefore(guildy.generateGuildIcon(),document.getElementById("bottomseparator")); break; } case "MESSAGE_REACTION_ADD": @@ -379,6 +386,7 @@ class Localuser{ } }else if(temp.op===10){ + if(!this.ws) return; console.log("heartbeat down"); this.heartbeat_interval=temp.d.heartbeat_interval; this.ws.send(JSON.stringify({op:1,d:this.lastSequence})) @@ -391,7 +399,7 @@ class Localuser{ } } heartbeat_interval:number; - resolveChannelFromID(ID:string):Channel{ + resolveChannelFromID(ID:string):Channel|undefined{ let resolve=this.guilds.find(guild => guild.channelids[ID]); if(resolve){ return resolve.channelids[ID]; @@ -400,22 +408,26 @@ class Localuser{ } updateChannel(json:channeljson):void{ SnowFlake.getSnowFlakeFromID(json.guild_id,Guild).getObject().updateChannel(json); - if(json.guild_id===this.lookingguild.id){ + 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){ + 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); + let guild_id=json.guild_id; + guild_id??="@me"; + const guild=this.guildids.get(guild_id); + if(guild){ + guild.delChannel(json); + } - if(json.guild_id===this.lookingguild.id){ + if(json.guild_id===this.lookingguild?.id){ this.loadGuild(json.guild_id); } } @@ -424,20 +436,25 @@ class Localuser{ this.buildservers(); if(location[3]==="channels"){ const guild=this.loadGuild(location[4]); + if(!guild){return;} guild.loadChannel(location[5]); this.channelfocus=guild.channelids[location[5]]; } } loaduser():void{ - document.getElementById("username").textContent=this.user.username; + (document.getElementById("username") as HTMLSpanElement).textContent=this.user.username; (document.getElementById("userpfp") as HTMLImageElement).src=this.user.getpfpsrc(); - document.getElementById("status").textContent=this.status; + (document.getElementById("status") as HTMLSpanElement).textContent=this.status; } isAdmin():boolean{ - return this.lookingguild.isAdmin(); + if(this.lookingguild){ + return this.lookingguild.isAdmin(); + }else{ + return false; + } } - loadGuild(id:string):Guild{ + loadGuild(id:string):Guild|undefined{ let guild=this.guildids.get(id); if(!guild){ guild=this.guildids.get("@me"); @@ -445,18 +462,23 @@ class Localuser{ if(this.lookingguild){ this.lookingguild.html.classList.remove("serveropen"); } + + if(!guild) return; if(guild.html){ guild.html.classList.add("serveropen") } this.lookingguild=guild; - document.getElementById("serverName").textContent=guild.properties.name; + (document.getElementById("serverName") as HTMLElement).textContent=guild.properties.name; //console.log(this.guildids,id) - document.getElementById("channels").innerHTML=""; - document.getElementById("channels").appendChild(guild.getHTML()); + const channels=document.getElementById("channels") as HTMLDivElement; + channels.innerHTML=""; + const html=guild.getHTML(); + channels.appendChild(html); + console.log("found :3",html) return guild; } buildservers():void{ - const serverlist=document.getElementById("servers");// + const serverlist=document.getElementById("servers") as HTMLDivElement;// const outdiv=document.createElement("div"); const img=document.createElement("img"); const div=document.createElement("div"); @@ -465,7 +487,7 @@ class Localuser{ img.src="/icons/home.svg"; img.classList.add("svgtheme","svgicon") img["all"]=this.guildids.get("@me"); - this.guildids.get("@me").html=outdiv; + (this.guildids.get("@me") as Guild).html=outdiv; const unread=document.createElement("div"); unread.classList.add("unread"); outdiv.append(unread); @@ -524,7 +546,7 @@ class Localuser{ createGuild(){ let inviteurl=""; const error=document.createElement("span"); - const fields:{name:string,icon:string}={ + const fields:{name:string,icon:string|null}={ name:"", icon:null, } @@ -566,8 +588,9 @@ class Localuser{ ["vdiv", ["title","Create a guild"], ["fileupload","Icon:",function(event:InputEvent){ - const reader=new FileReader(); const target=event.target as HTMLInputElement; + if(!target.files) return; + const reader=new FileReader(); reader.readAsDataURL(target.files[0]); reader.onload=() => { fields.icon=reader.result as string; @@ -590,7 +613,7 @@ class Localuser{ ]]) full.show(); } - async makeGuild(fields:{name:string,icon:string}){ + async makeGuild(fields:{name:string,icon:string|null}){ return await (await fetch(this.info.api+"/guilds",{ method:"POST", headers:this.headers, @@ -660,7 +683,9 @@ class Localuser{ } messageCreate(messagep):void{ messagep.d.guild_id??="@me"; - this.guildids.get(messagep.d.guild_id).channelids[messagep.d.channel_id].messageCreate(messagep); + const guild=this.guildids.get(messagep.d.guild_id); + if(!guild) return; + guild.channelids[messagep.d.channel_id].messageCreate(messagep); this.unreads(); } unreads():void{ @@ -671,9 +696,10 @@ class Localuser{ } } async typingStart(typing):Promise{ - if(this.channelfocus.id===typing.d.channel_id){ + if(this.channelfocus?.id===typing.d.channel_id){ const guild=this.guildids.get(typing.d.guild_id); + if(!guild) return; const memb=await Member.new(typing.d.member,guild); if(memb.id===this.user.id){ console.log("you is typing") @@ -732,13 +758,13 @@ class Localuser{ }); } rendertyping():void{ - const typingtext=document.getElementById("typing") + const typingtext=document.getElementById("typing") as HTMLDivElement; let build=""; let showing=false; let i=0; const curtime=new Date().getTime()-5000; for(const thing of this.typing.keys()){ - if(this.typing.get(thing)>curtime){ + if(this.typing.get(thing) as number>curtime){ if(i!==0){ build+=", "; } @@ -760,20 +786,20 @@ class Localuser{ } if(showing){ typingtext.classList.remove("hidden"); - document.getElementById("typingtext").textContent=build; + const typingtext2=document.getElementById("typingtext") as HTMLDivElement; + typingtext2.textContent=build; }else{ typingtext.classList.add("hidden"); } } showusersettings(){ const settings=new Settings("Settings"); - this.usersettings=settings; { const userOptions=settings.addButton("User Settings",{ltr:true}); const hypotheticalProfile=document.createElement("div"); - let file=undefined; - let newpronouns:string=undefined; - let newbio:string=undefined; + let file:undefined|File=undefined; + let newpronouns:string|undefined=undefined; + let newbio:string|undefined=undefined; let hypouser=this.user.clone(); let color:string; async function regen(){ @@ -801,7 +827,7 @@ class Localuser{ regen(); } }); - let bfile=undefined; + let bfile:undefined|File|null=undefined; const binput=settingsLeft.addFileInput("Upload banner:",_=>{ if(bfile!==undefined){ this.updatebanner(bfile) @@ -863,7 +889,7 @@ class Localuser{ tas.addSelect("Theme:",_=>{ localStorage.setItem("theme",themes[_]); setTheme(); - },themes,{defaultIndex:themes.indexOf(localStorage.getItem("theme"))}); + },themes,{defaultIndex:themes.indexOf(localStorage.getItem("theme") as string)}); } { const sounds=Voice.sounds; @@ -1217,15 +1243,16 @@ class Localuser{ readonly presences:Map=new Map(); async resolvemember(id:string,guildid:string):Promise{ if(guildid==="@me"){return undefined} - if(!this.waitingmembers.has(guildid)){ - this.waitingmembers.set(guildid,new Map()); + let guildmap=this.waitingmembers.get(guildid); + if(!guildmap){ + guildmap=new Map(); + this.waitingmembers.set(guildid,guildmap); + } - let res:(returns:memberjson|undefined)=>void; - const promise:Promise=new Promise((r)=>{ - res=r; + const promise:Promise=new Promise((res)=>{ + guildmap.set(id,res); + this.getmembers(); }) - this.waitingmembers.get(guildid).set(id,res); - this.getmembers(); return await promise; } fetchingmembers:Map=new Map(); @@ -1233,11 +1260,14 @@ class Localuser{ noncebuild:Map=new Map(); async gotChunk(chunk:{chunk_index:number,chunk_count:number,nonce:string,not_found?:string[],members?:memberjson[],presences:presencejson[]}){ for(const thing of chunk.presences){ - this.presences.set(thing.user.id,thing); + if(thing.user){ + this.presences.set(thing.user.id,thing); + } } console.log(chunk); chunk.members??=[]; const arr=this.noncebuild.get(chunk.nonce); + if(!arr) return; arr[0]=arr[0].concat(chunk.members); if(chunk.not_found){ arr[1]=chunk.not_found; @@ -1247,15 +1277,14 @@ class Localuser{ console.log("got through"); this.noncebuild.delete(chunk.nonce); const func=this.noncemap.get(chunk.nonce) + if(!func) return; func([arr[0],arr[1]]); this.noncemap.delete(chunk.nonce); } } async getmembers(){ - let res:Function - const promise=new Promise(r=>res=r); - setTimeout(res,10); + const promise=new Promise(res=>{setTimeout(res,10)}); await promise;//allow for more to be sent at once :P if(this.ws){ this.waitingmembers.forEach(async (value,guildid)=>{ @@ -1269,35 +1298,39 @@ class Localuser{ this.waitingmembers.delete(guildid); return }; - let res:(r:[memberjson[],string[]])=>void; - const promise:Promise<[memberjson[],string[]]>=new Promise((r)=>{ - res=r; + const promise:Promise<[memberjson[],string[]]>=new Promise((res)=>{ + const nonce=""+Math.floor(Math.random()*100000000000); + this.noncemap.set(nonce,res); + this.noncebuild.set(nonce,[[],[],[]]); + if(!this.ws) return; + this.ws.send(JSON.stringify({ + op:8, + d:{ + user_ids:build, + guild_id:guildid, + limit:100, + nonce, + presences:true + } + })); + this.fetchingmembers.set(guildid,true); + }) - const nonce=""+Math.floor(Math.random()*100000000000); - this.noncemap.set(nonce,res); - this.noncebuild.set(nonce,[[],[],[]]); - this.ws.send(JSON.stringify({ - op:8, - d:{ - user_ids:build, - guild_id:guildid, - limit:100, - nonce, - presences:true - } - })); - this.fetchingmembers.set(guildid,true); const prom=await promise;; const data=prom[0]; for(const thing of data){ if(value.has(thing.id)){ - value.get(thing.id)(thing); + const func=value.get(thing.id); + if(!func) continue; + func(thing); value.delete(thing.id); } } for(const thing of prom[1]){ if(value.has(thing)){ - value.get(thing)(undefined); + const func=value.get(thing) + if(!func) continue; + func(undefined); value.delete(thing); } } @@ -1341,7 +1374,7 @@ let fixsvgtheme:Function; h=(g-b)/(max-min); }else if(g===max){ h=2+(b-r)/(max-min); - }else if(b===max){ + }else{ h=4+(r-g)/(max-min); } }else{ diff --git a/webpage/message.ts b/webpage/message.ts index ce8a79d..6e296d0 100644 --- a/webpage/message.ts +++ b/webpage/message.ts @@ -198,7 +198,7 @@ class Message{ } } getimages(){ - const build=[]; + const build:File[]=[]; for(const thing of this.attachments){ if(thing.content_type.startsWith('image/')){ build.push(thing); diff --git a/webpage/user.ts b/webpage/user.ts index 99ec8b2..5a22ccc 100644 --- a/webpage/user.ts +++ b/webpage/user.ts @@ -20,7 +20,7 @@ class User{ bot:boolean; public_flags: number; accent_color: number; - banner: string; + banner: string|null|undefined; hypotheticalbanner:boolean; premium_since: string; premium_type: number;