From 1608d00beba89c99efaaa3e1aa5883ae2d5ab3f2 Mon Sep 17 00:00:00 2001 From: MathMan05 Date: Tue, 20 Aug 2024 14:17:54 -0500 Subject: [PATCH] Various improvements and checks --- .dist/channel.js | 34 +++++++++---------- .dist/direct.js | 23 +++++++------ .dist/embed.js | 40 +++++++++++++--------- .dist/emoji.js | 2 +- .dist/file.js | 6 ++-- .dist/guild.js | 20 ++++++++--- .dist/index.js | 35 +++++++++++++------- .dist/infiniteScroller.js | 49 +++++++++++++++++++-------- .dist/localuser.js | 4 ++- .dist/markdown.js | 42 ++++++++++++++++------- .dist/member.js | 40 ++++++++++++---------- .dist/message.js | 48 ++++++++++++++++----------- .dist/snowflake.js | 7 +++- .dist/user.js | 37 +++++++++++++-------- webpage/channel.ts | 58 +++++++++++++++----------------- webpage/direct.ts | 33 +++++++++---------- webpage/embed.ts | 45 ++++++++++++++----------- webpage/emoji.ts | 9 +++-- webpage/file.ts | 16 ++++----- webpage/guild.ts | 27 ++++++++++----- webpage/index.ts | 66 +++++++++++++++++++++---------------- webpage/infiniteScroller.ts | 60 ++++++++++++++++++++++----------- webpage/jsontypes.ts | 14 ++++---- webpage/localuser.ts | 5 +-- webpage/markdown.ts | 49 ++++++++++++++++----------- webpage/member.ts | 18 ++++++---- webpage/message.ts | 60 ++++++++++++++++++--------------- webpage/role.ts | 2 +- webpage/snowflake.ts | 7 +++- webpage/user.ts | 45 +++++++++++++++---------- 30 files changed, 541 insertions(+), 360 deletions(-) diff --git a/.dist/channel.js b/.dist/channel.js index beb0f2e..0bbba4b 100644 --- a/.dist/channel.js +++ b/.dist/channel.js @@ -39,6 +39,7 @@ class Channel { infinite; idToPrev = new Map(); idToNext = new Map(); + messages = new Map(); get id() { return this.snowflake.id; } @@ -167,7 +168,7 @@ class Channel { if (this.idToNext.has(snowflake)) { return this.idToNext.get(snowflake)?.id; } - else if (this.lastmessage.id !== id) { + else if (this.lastmessage?.id !== id) { await this.grabAfter(id); return this.idToNext.get(snowflake)?.id; } @@ -177,19 +178,11 @@ class Channel { } }.bind(this), async function (id) { //await new Promise(_=>{setTimeout(_,Math.random()*10)}) - const snowflake = SnowFlake.getSnowFlakeFromID(id, Message); - if (!snowflake.getObject()) { - //await this.grabArround(id); - console.error("Uh..."); - } + const messgage = this.messages.get(id); try { - const prev = this.idToPrev.get(snowflake); - if (prev) { - const messgage = this.messageids.get(prev); - if (messgage) { - const html = snowflake.getObject().buildhtml(messgage); - return html; - } + if (messgage) { + const html = messgage.buildhtml(); + return html; } } catch (e) { @@ -595,10 +588,12 @@ class Channel { }); } setReplying(message) { - if (this.replyingto) { + 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(); @@ -611,7 +606,7 @@ class Channel { span.textContent = "Replying to " + this.replyingto.author.username; const X = document.createElement("button"); X.onclick = _ => { - if (this.replyingto) { + if (this.replyingto?.div) { this.replyingto.div.classList.remove("replying"); } replybox.classList.add("hideReplyBox"); @@ -744,8 +739,7 @@ class Channel { this.children = build; } async grabAfter(id) { - console.log(id, this.lastmessage.id); - if (id === this.lastmessage.id) { + if (id === this.lastmessage?.id) { return; } await fetch(this.info.api + "/channels/" + this.id + "/messages?limit=100&after=" + id, { @@ -1025,8 +1019,8 @@ class Channel { console.log(this.lastmessageid, messagez.snowflake, ":3"); if (this.lastmessageid) { this.idToNext.set(this.lastmessageid, messagez.snowflake); + this.idToPrev.set(messagez.snowflake, this.lastmessageid); } - this.idToPrev.set(messagez.snowflake, this.lastmessageid); this.lastmessageid = messagez.snowflake; this.messageids.set(messagez.snowflake, messagez); if (messagez.author === this.localuser.user) { @@ -1078,7 +1072,9 @@ class Channel { const images = message.getimages(); if (images.length) { const image = images[0]; - imgurl ||= image.proxy_url; + if (image.proxy_url) { + imgurl ||= image.proxy_url; + } imgurl ||= image.url; } const notification = new Notification(this.notititle(message), { diff --git a/.dist/direct.js b/.dist/direct.js index afc49c9..2303890 100644 --- a/.dist/direct.js +++ b/.dist/direct.js @@ -35,12 +35,6 @@ class Direct extends Guild { this.calculateReorder(); this.printServers(); } - sortchannels() { - this.headchannels.sort((a, b) => { - const result = (a.lastmessageid.getUnixTime() - b.lastmessageid.getUnixTime()); - return Number(-result); - }); - } giveMember(_member) { console.error("not a real guild, can't give member object"); } @@ -105,7 +99,10 @@ class Group extends Channel { this.lastmessageid ??= null; this.mentions = 0; this.setUpInfiniteScroller(); - this.position = Math.max(this.lastmessageid.getUnixTime(), this.snowflake.getUnixTime()); + if (this.lastmessageid) { + this.position = this.lastmessageid.getUnixTime(); + } + this.position = -Math.max(this.position, this.snowflake.getUnixTime()); } createguildHTML() { const div = document.createElement("div"); @@ -134,14 +131,16 @@ class Group extends Channel { return; } this.buildmessages(); - history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.snowflake); + history.pushState(null, "", "/channels/" + this.guild_id + "/" + this.id); document.getElementById("channelname").textContent = "@" + this.name; document.getElementById("channelTopic").setAttribute("hidden", ""); document.getElementById("typebox").contentEditable = "" + true; } messageCreate(messagep) { const messagez = new Message(messagep.d, this); - this.idToNext.set(this.lastmessageid, messagez.snowflake); + if (this.lastmessageid) { + this.idToNext.set(this.lastmessageid, messagez.snowflake); + } this.idToPrev.set(messagez.snowflake, this.lastmessageid); this.lastmessageid = messagez.snowflake; this.messageids.set(messagez.snowflake, messagez); @@ -161,7 +160,7 @@ class Group extends 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") { @@ -175,7 +174,7 @@ class Group extends Channel { return message.author.username; } unreads() { - const sentdms = document.getElementById("sentdms"); + const sentdms = document.getElementById("sentdms"); //Need to change sometime let current = null; for (const thing of sentdms.children) { if (thing["all"] === this) { @@ -184,7 +183,7 @@ class Group extends Channel { } if (this.hasunreads) { if (current) { - current.noti.textContent = this.mentions; + current["noti"].textContent = this.mentions; return; } const div = document.createElement("div"); diff --git a/.dist/embed.js b/.dist/embed.js index 0452a46..a531077 100644 --- a/.dist/embed.js +++ b/.dist/embed.js @@ -65,13 +65,15 @@ class Embed { authorline.append(a); embed.append(authorline); } - const title = document.createElement("a"); - title.append(new MarkDown(this.json.title, this.channel).makeHTML()); - if (this.json.url) { - title.href = this.json.url; + if (this.json.title) { + const title = document.createElement("a"); + title.append(new MarkDown(this.json.title, this.channel).makeHTML()); + if (this.json.url) { + title.href = this.json.url; + } + title.classList.add("embedtitle"); + embed.append(title); } - title.classList.add("embedtitle"); - embed.append(title); if (this.json.description) { const p = document.createElement("p"); p.append(new MarkDown(this.json.description, this.channel).makeHTML()); @@ -150,7 +152,7 @@ class Embed { table.classList.add("embed", "linkembed"); const trtop = document.createElement("tr"); table.append(trtop); - { + if (this.json.url && this.json.title) { const td = document.createElement("td"); const a = document.createElement("a"); a.href = this.json.url; @@ -174,9 +176,11 @@ class Embed { } const bottomtr = document.createElement("tr"); const td = document.createElement("td"); - const span = document.createElement("span"); - span.textContent = this.json.description; - td.append(span); + if (this.json.description) { + const span = document.createElement("span"); + span.textContent = this.json.description; + td.append(span); + } bottomtr.append(td); table.append(bottomtr); return table; @@ -194,12 +198,16 @@ class Embed { div.append(provider); } const a = document.createElement("a"); - a.href = this.json.url; - a.textContent = this.json.title; - div.append(a); - const description = document.createElement("p"); - description.textContent = this.json.description; - div.append(description); + if (this.json.url && this.json.url) { + a.href = this.json.url; + a.textContent = this.json.url; + div.append(a); + } + if (this.json.description) { + const description = document.createElement("p"); + description.textContent = this.json.description; + div.append(description); + } if (this.json.thumbnail) { const img = document.createElement("img"); img.classList.add("bigembedimg"); diff --git a/.dist/emoji.js b/.dist/emoji.js index 510bf51..1b3d937 100644 --- a/.dist/emoji.js +++ b/.dist/emoji.js @@ -13,7 +13,7 @@ class Emoji { } get localuser() { if (this.owner instanceof Guild) { - return this.guild.localuser; + return this.owner.localuser; } else { return this.owner; diff --git a/.dist/file.js b/.dist/file.js index 0af8a20..fefacb8 100644 --- a/.dist/file.js +++ b/.dist/file.js @@ -23,7 +23,7 @@ class File { } getHTML(temp = false) { const src = this.proxy_url || this.url; - if (this.width) { + if (this.width && this.height) { let scale = 1; const max = 96 * 3; scale = Math.max(scale, this.width / max); @@ -57,7 +57,7 @@ class File { video.append(source); source.type = this.content_type; video.controls = !temp; - if (this.width) { + if (this.width && this.height) { video.width = this.width; video.height = this.height; } @@ -97,7 +97,7 @@ class File { return new File({ filename: file.name, size: file.size, - id: null, + id: "null", content_type: file.type, width: undefined, height: undefined, diff --git a/.dist/guild.js b/.dist/guild.js index 509b613..1ed1119 100644 --- a/.dist/guild.js +++ b/.dist/guild.js @@ -97,10 +97,21 @@ class Guild { this.roleids.set(roleh.snowflake, roleh); } if (member instanceof User) { - Member.resolveMember(member, this).then(_ => this.member = _); + Member.resolveMember(member, this).then(_ => { + if (_) { + this.member = _; + } + else { + console.error("Member was unable to resolve"); + } + }); } else { - Member.new(member, this).then(_ => this.member = _); + Member.new(member, this).then(_ => { + if (_) { + this.member = _; + } + }); } for (const thing of json.channels) { const temp = new Channel(thing, this); @@ -199,11 +210,10 @@ class Guild { 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; + thing.move_id = null; } if (thisthing.position || thisthing.parent_id) { build.push(thisthing); - console.log(this.channelids[thisthing.parent_id]); } if (thing.children.length > 0) { const things = thing.calculateReorder(); @@ -369,6 +379,8 @@ class Guild { if (thing.hasunreads) { build.read_states.push({ channel_id: thing.snowflake, message_id: thing.lastmessageid, read_state_type: 0 }); thing.lastreadmessageid = thing.lastmessageid; + if (!thing.myhtml) + continue; thing.myhtml.classList.remove("cunread"); } } diff --git a/.dist/index.js b/.dist/index.js index d28ad76..b243d28 100644 --- a/.dist/index.js +++ b/.dist/index.js @@ -38,16 +38,17 @@ function showAccountSwitcher() { userinfo.addEventListener("click", _ => { thisuser.unload(); thisuser.swapped = true; - document.getElementById("loading").classList.remove("doneloading"); - document.getElementById("loading").classList.add("loading"); + const loading = document.getElementById("loading"); + loading.classList.remove("doneloading"); + loading.classList.add("loading"); thisuser = new Localuser(specialuser); users["currentuser"] = specialuser.uid; localStorage.setItem("userinfos", JSON.stringify(users)); thisuser.initwebsocket().then(_ => { thisuser.loaduser(); thisuser.init(); - document.getElementById("loading").classList.add("doneloading"); - document.getElementById("loading").classList.remove("loading"); + loading.classList.add("doneloading"); + loading.classList.remove("loading"); console.log("done loading"); }); userinfo.remove(); @@ -90,8 +91,9 @@ try { thisuser.initwebsocket().then(_ => { thisuser.loaduser(); thisuser.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"); }); } @@ -103,10 +105,14 @@ catch (e) { { const menu = new Contextmenu("create rightclick"); menu.addbutton("Create channel", function () { - thisuser.lookingguild.createchannels(); + if (thisuser.lookingguild) { + thisuser.lookingguild.createchannels(); + } }, null, _ => { return thisuser.isAdmin(); }); menu.addbutton("Create category", function () { - thisuser.lookingguild.createcategory(); + if (thisuser.lookingguild) { + thisuser.lookingguild.createcategory(); + } }, null, _ => { return thisuser.isAdmin(); }); menu.bind(document.getElementById("channels")); } @@ -114,6 +120,8 @@ const pasteimage = document.getElementById("pasteimage"); let replyingto = null; async function enter(event) { const channel = thisuser.channelfocus; + if (!channel || !thisuser.channelfocus) + return; channel.typingstart(); if (event.key === "Enter" && !event.shiftKey) { event.preventDefault(); @@ -124,13 +132,14 @@ async function enter(event) { else { replyingto = thisuser.channelfocus.replyingto; let replying = replyingto; - if (replyingto) { + if (replyingto?.div) { replyingto.div.classList.remove("replying"); } thisuser.channelfocus.replyingto = null; channel.sendMessage(markdown.rawString, { attachments: images, - replyingto: replying, + embeds: [], + replyingto: replying }); thisuser.channelfocus.makereplybox(); } @@ -165,6 +174,8 @@ const imageshtml = []; import { File } from "./file.js"; import { MarkDown } from "./markdown.js"; document.addEventListener('paste', async (e) => { + if (!e.clipboardData) + return; Array.from(e.clipboardData.files).forEach(async (f) => { const file = File.initFromBlob(f); e.preventDefault(); @@ -180,13 +191,13 @@ function userSettings() { } document.getElementById("settings").onclick = userSettings; if (mobile) { - document.getElementById("channelw").onclick = function () { + document.getElementById("channelw").onclick = () => { document.getElementById("channels").parentNode.classList.add("collapse"); document.getElementById("servertd").classList.add("collapse"); document.getElementById("servers").classList.add("collapse"); }; document.getElementById("mobileback").textContent = "#"; - document.getElementById("mobileback").onclick = function () { + document.getElementById("mobileback").onclick = () => { document.getElementById("channels").parentNode.classList.remove("collapse"); document.getElementById("servertd").classList.remove("collapse"); document.getElementById("servers").classList.remove("collapse"); diff --git a/.dist/infiniteScroller.js b/.dist/infiniteScroller.js index ff39331..64104c2 100644 --- a/.dist/infiniteScroller.js +++ b/.dist/infiniteScroller.js @@ -50,6 +50,8 @@ class InfiniteScroller { scrollTop; needsupdate = true; async updatestuff() { + if (!this.scroll) + return; this.timeout = null; this.scrollBottom = this.scroll.scrollHeight - this.scroll.scrollTop - this.scroll.clientHeight; this.scrollTop = this.scroll.scrollTop; @@ -65,6 +67,8 @@ class InfiniteScroller { //this.watchForChange(); } async firstElement(id) { + if (!this.scroll) + return; const html = await this.getHTMLFromID(id); this.scroll.appendChild(html); this.HTMLElements.push([html, id]); @@ -85,14 +89,20 @@ class InfiniteScroller { }; } async watchForTop() { + if (!this.scroll) + return false; let again = false; if (this.scrollTop === 0) { this.scrollTop = 1; this.scroll.scrollTop = 1; } if (this.scrollTop < this.minDist) { - const previd = this.HTMLElements.at(0)[1]; - const nextid = await this.getIDFromOffset(previd, 1); + let nextid; + const firstelm = this.HTMLElements.at(0); + if (firstelm) { + const previd = firstelm[1]; + nextid = await this.getIDFromOffset(previd, 1); + } if (!nextid) { } else { @@ -110,10 +120,12 @@ class InfiniteScroller { ; } if (this.scrollTop > this.maxDist) { - again = true; const html = this.HTMLElements.shift(); - await this.destroyFromID(html[1]); - this.scrollTop -= 60; + if (html) { + again = true; + await this.destroyFromID(html[1]); + this.scrollTop -= 60; + } } if (again) { await this.watchForTop(); @@ -121,11 +133,17 @@ class InfiniteScroller { return again; } async watchForBottom() { + if (!this.scroll) + return false; let again = false; const scrollBottom = this.scrollBottom; if (scrollBottom < this.minDist) { - const previd = this.HTMLElements.at(-1)[1]; - const nextid = await this.getIDFromOffset(previd, -1); + let nextid; + const lastelm = this.HTMLElements.at(-1); + if (lastelm) { + const previd = lastelm[1]; + nextid = await this.getIDFromOffset(previd, -1); + } if (!nextid) { } else { @@ -141,10 +159,12 @@ class InfiniteScroller { ; } if (scrollBottom > this.maxDist) { - again = true; const html = this.HTMLElements.pop(); - await this.destroyFromID(html[1]); - this.scrollBottom -= 60; + if (html) { + await this.destroyFromID(html[1]); + this.scrollBottom -= 60; + again = true; + } } if (again) { await this.watchForBottom(); @@ -154,14 +174,14 @@ class InfiniteScroller { async watchForChange() { try { if (this.currrunning) { - return; + return false; } else { this.currrunning = true; } if (!this.div) { this.currrunning = false; - return; + return false; } const out = await Promise.allSettled([this.watchForTop(), this.watchForBottom()]); const changed = (out[0].value || out[1].value); @@ -177,6 +197,7 @@ class InfiniteScroller { catch (e) { console.error(e); } + return false; } async focus(id, flash = true) { let element; @@ -217,7 +238,9 @@ class InfiniteScroller { await this.destroyFromID(thing[1]); } this.HTMLElements = []; - clearTimeout(this.timeout); + if (this.timeout) { + clearTimeout(this.timeout); + } if (this.div) { this.div.remove(); } diff --git a/.dist/localuser.js b/.dist/localuser.js index 3634b9a..679a27d 100644 --- a/.dist/localuser.js +++ b/.dist/localuser.js @@ -691,6 +691,8 @@ class Localuser { if (!guild) return; const memb = await Member.new(typing.d.member, guild); + if (!memb) + return; if (memb.id === this.user.id) { console.log("you is typing"); return; @@ -834,7 +836,7 @@ class Localuser { }); const bclear = settingsLeft.addButtonInput("Clear banner", "Clear", () => { bfile = null; - hypouser.banner = null; + hypouser.banner = undefined; settingsLeft.changed(); regen(); }); diff --git a/.dist/markdown.js b/.dist/markdown.js index 471d949..0abea4d 100644 --- a/.dist/markdown.js +++ b/.dist/markdown.js @@ -56,7 +56,7 @@ class MarkDown { if (first) { i--; } - let element = null; + let element; let keepys = ""; if (txt[i + 1] === "#") { console.log("test"); @@ -97,17 +97,23 @@ class MarkDown { for (; txt[i] !== "\n" && txt[i] !== undefined; i++) { build.push(txt[i]); } - if (stdsize) { - element = document.createElement("span"); + try { + if (stdsize) { + element = document.createElement("span"); + } + else + continue; + if (keep) { + element.append(keepys); + } + element.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize })); + span.append(element); } - if (keep) { - element.append(keepys); + finally { + i -= 1; + console.log(txt[i]); + continue; } - element.appendChild(this.markdown(build, { keep: keep, stdsize: stdsize })); - span.append(element); - i -= 1; - console.log(txt[i]); - continue; } if (first) { i++; @@ -483,18 +489,24 @@ class MarkDown { } }; box.onpaste = _ => { + if (!_.clipboardData) + return; console.log(_.clipboardData.types); const data = _.clipboardData.getData("text"); document.execCommand('insertHTML', false, data); _.preventDefault(); + if (!box.onkeyup) + return; box.onkeyup(new KeyboardEvent("_")); }; } boxupdate(box) { - var restore = saveCaretPosition(box); + const restore = saveCaretPosition(box); box.innerHTML = ""; box.append(this.makeHTML({ keep: true })); - restore(); + if (restore) { + restore(); + } } static gatherBoxText(element) { if (element.tagName.toLowerCase() === "img") { @@ -521,10 +533,14 @@ class MarkDown { //solution from https://stackoverflow.com/questions/4576694/saving-and-restoring-caret-position-for-contenteditable-div function saveCaretPosition(context) { var selection = window.getSelection(); + if (!selection) + return; var range = selection.getRangeAt(0); range.setStart(context, 0); var len = range.toString().length; return function restore() { + if (!selection) + return; var pos = getTextNodeAtPosition(context, len); selection.removeAllRanges(); var range = new Range(); @@ -535,6 +551,8 @@ function saveCaretPosition(context) { function getTextNodeAtPosition(root, index) { const NODE_TYPE = NodeFilter.SHOW_TEXT; var treeWalker = document.createTreeWalker(root, NODE_TYPE, function next(elem) { + if (!elem.textContent) + return 0; if (index > elem.textContent.length) { index -= elem.textContent.length; return NodeFilter.FILTER_REJECT; diff --git a/.dist/member.js b/.dist/member.js index 68bd5a7..b568dd2 100644 --- a/.dist/member.js +++ b/.dist/member.js @@ -25,9 +25,12 @@ class Member { if (User.userids[memberjson.id]) { this.user = User.userids[memberjson.id]; } - else { + else if (memberjson.user) { this.user = new User(memberjson.user, owner.localuser); } + else { + throw new Error("Missing user object of this member"); + } this.owner = owner; for (const thing of Object.keys(memberjson)) { if (thing === "guild") { @@ -64,9 +67,12 @@ class Member { if (User.userids[memberjson.id]) { user = User.userids[memberjson.id]; } - else { + else if (memberjson.user) { user = new User(memberjson.user, owner.localuser); } + else { + throw new Error("missing user object of this member"); + } if (user.members.has(owner)) { let memb = user.members.get(owner); if (memb === undefined) { @@ -91,22 +97,22 @@ class Member { const maybe = user.members.get(guild); if (!user.members.has(guild)) { const membpromise = guild.localuser.resolvemember(user.id, guild.id); - let res; - const promise = new Promise(r => { res = r; }); + const promise = new Promise(async (res) => { + const membjson = await membpromise; + if (membjson === undefined) { + res(undefined); + return undefined; + } + else { + const member = new Member(membjson, guild); + const map = guild.localuser.presences; + member.getPresence(map.get(member.id)); + map.delete(member.id); + res(member); + return member; + } + }); user.members.set(guild, promise); - const membjson = await membpromise; - if (membjson === undefined) { - res(undefined); - return undefined; - } - else { - const member = new Member(membjson, guild); - const map = guild.localuser.presences; - member.getPresence(map.get(member.id)); - map.delete(member.id); - res(member); - return member; - } } if (maybe instanceof Promise) { return await maybe; diff --git a/.dist/message.js b/.dist/message.js index 96ab4e7..a67f0a5 100644 --- a/.dist/message.js +++ b/.dist/message.js @@ -45,12 +45,6 @@ class Message { this.del = new Promise(_ => { this.resolve = _; }); Message.setupcmenu(); } - static async wipeChanel() { - this.resolve(); - document.getElementById("messages").innerHTML = ""; - await Promise.allSettled([this.resolve]); - this.del = new Promise(_ => { this.resolve = _; }); - } static setupcmenu() { Message.contextmenu.addbutton("Copy raw text", function () { navigator.clipboard.writeText(this.content.rawString); @@ -69,7 +63,7 @@ class Message { }); Message.contextmenu.addbutton("Edit", function () { this.channel.editing = this; - const markdown = (document.getElementById("typebox"))["markdown"]; + const markdown = document.getElementById("typebox")["markdown"]; markdown.txt = this.content.rawString.split(''); markdown.boxupdate(document.getElementById("typebox")); }, null, _ => { return _.author.id === _.localuser.user.id; }); @@ -81,6 +75,7 @@ class Message { this.owner = owner; this.headers = this.owner.headers; this.giveData(messagejson); + this.owner.messages.set(this.id, this); } reactionToggle(emoji) { let remove = false; @@ -121,7 +116,9 @@ class Message { continue; } else if (thing === "member") { - Member.new(messagejson.member, this.guild).then(_ => { this.member = _; }); + Member.new(messagejson.member, this.guild).then(_ => { + this.member = _; + }); continue; } else if (thing === "embeds") { @@ -182,7 +179,7 @@ class Message { return; try { this.div.remove(); - this.div = null; + this.div = undefined; } catch (e) { console.error(e); @@ -221,23 +218,37 @@ class Message { deleteEvent() { if (this.div) { this.div.innerHTML = ""; - this.div = null; + this.div = undefined; } const prev = this.channel.idToPrev.get(this.snowflake); const next = this.channel.idToNext.get(this.snowflake); - this.channel.idToNext.set(prev, next); - this.channel.idToPrev.set(next, prev); + if (prev) { + this.channel.idToPrev.delete(this.snowflake); + } + if (next) { + this.channel.idToNext.delete(this.snowflake); + } + if (prev && next) { + this.channel.idToPrev.set(next, prev); + this.channel.idToNext.set(prev, next); + } this.channel.messageids.delete(this.snowflake); - const regen = prev.getObject(); - if (regen) { - regen.generateMessage(); + if (prev && prev.getObject()) { + prev.getObject().generateMessage(); } if (this.channel.lastmessage === this) { - this.channel.lastmessage = prev.getObject(); + if (prev) { + this.channel.lastmessage = prev.getObject(); + } + else { + this.channel.lastmessage = undefined; + } } } reactdiv; - generateMessage(premessage = null) { + generateMessage(premessage = undefined) { + if (!this.div) + return; if (!premessage) { premessage = this.channel.idToPrev.get(this.snowflake)?.getObject(); } @@ -467,13 +478,12 @@ class Message { } } } - buildhtml(premessage) { + buildhtml(premessage = undefined) { if (this.div) { console.error(`HTML for ${this.snowflake} already exists, aborting`); return; } try { - //premessage??=messages.lastChild; const div = document.createElement("div"); this.div = div; this.messageevents(div); diff --git a/.dist/snowflake.js b/.dist/snowflake.js index a5b90dc..14fd114 100644 --- a/.dist/snowflake.js +++ b/.dist/snowflake.js @@ -33,7 +33,7 @@ class SnowFlake { } /** * Just to clarify bc TS, it returns a SnowFlake\ which is what you entered with the type parameter - * + * @deprecated **/ static getSnowFlakeFromID(id, type) { if (!SnowFlake.SnowFlakes.get(type)) { @@ -56,6 +56,11 @@ class SnowFlake { return snowflake; } } + /** + * @deprecated + * + * + */ static hasSnowFlakeFromID(id, type) { if (!SnowFlake.SnowFlakes.get(type)) { return false; diff --git a/.dist/user.js b/.dist/user.js index 86d13d2..39c4581 100644 --- a/.dist/user.js +++ b/.dist/user.js @@ -191,12 +191,19 @@ class User { html.after(error); return; } - _.bind(html); + if (_) { + _.bind(html); + } }).catch(_ => { console.log(_); }); } - this.profileclick(html, guild); + if (guild) { + this.profileclick(html, guild); + } + else { + this.profileclick(html); + } User.contextmenu.bind(html, this); } static async resolve(id, localuser) { @@ -269,18 +276,20 @@ class User { return; for (const id of this.badge_ids) { const badgejson = await this.getBadge(id); - const badge = document.createElement(badgejson.link ? "a" : "div"); - badge.classList.add("badge"); - const img = document.createElement("img"); - img.src = badgejson.icon; - badge.append(img); - const span = document.createElement("span"); - span.textContent = badgejson.description; - badge.append(span); - if (badge instanceof HTMLAnchorElement) { - badge.href = badgejson.link; + if (badgejson) { + const badge = document.createElement(badgejson.link ? "a" : "div"); + badge.classList.add("badge"); + const img = document.createElement("img"); + img.src = badgejson.icon; + badge.append(img); + const span = document.createElement("span"); + span.textContent = badgejson.description; + badge.append(span); + if (badge instanceof HTMLAnchorElement) { + badge.href = badgejson.link; + } + badgediv.append(badge); } - badgediv.append(badge); } })(); { @@ -337,7 +346,7 @@ class User { } return div; } - profileclick(obj, guild) { + profileclick(obj, guild = undefined) { obj.onclick = e => { this.buildprofile(e.clientX, e.clientY, guild); e.stopPropagation(); diff --git a/webpage/channel.ts b/webpage/channel.ts index 845d1a5..a7ec694 100644 --- a/webpage/channel.ts +++ b/webpage/channel.ts @@ -19,7 +19,7 @@ declare global { } } class Channel{ - editing:Message; + editing:Message|null; type:number; owner:Guild; headers:Localuser["headers"]; @@ -46,8 +46,9 @@ class Channel{ static contextmenu=new Contextmenu("channel menu"); replyingto:Message|null; infinite:InfiniteScroller; - idToPrev:Map,SnowFlake|null>=new Map(); - idToNext:Map,SnowFlake|null>=new Map(); + idToPrev:Map,SnowFlake>=new Map(); + idToNext:Map,SnowFlake>=new Map(); + messages:Map=new Map(); get id(){ return this.snowflake.id; } @@ -180,7 +181,7 @@ class Channel{ }else{ if(this.idToNext.has(snowflake)){ return this.idToNext.get(snowflake)?.id; - }else if(this.lastmessage.id!==id){ + }else if(this.lastmessage?.id!==id){ await this.grabAfter(id); return this.idToNext.get(snowflake)?.id; }else{ @@ -190,21 +191,12 @@ class Channel{ }.bind(this), async function(this:Channel,id:string){ //await new Promise(_=>{setTimeout(_,Math.random()*10)}) - const snowflake=SnowFlake.getSnowFlakeFromID(id,Message); - if(!snowflake.getObject()){ - //await this.grabArround(id); - console.error("Uh...") - } + const messgage=this.messages.get(id); try{ - const prev=this.idToPrev.get(snowflake); - if(prev){ - const messgage=this.messageids.get(prev); - if(messgage){ - const html=snowflake.getObject().buildhtml(messgage); - return html; - } + if(messgage){ + const html=messgage.buildhtml(); + return html; } - }catch(e){ console.error(e); } @@ -600,13 +592,15 @@ class Channel{ }) } setReplying(message:Message){ - if(this.replyingto){ - this.replyingto.div.classList.remove("replying"); - } - this.replyingto=message; - console.log(message); - this.replyingto.div.classList.add("replying"); - this.makereplybox(); + + 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(){ @@ -617,7 +611,7 @@ class Channel{ span.textContent="Replying to "+this.replyingto.author.username; const X=document.createElement("button"); X.onclick=_=>{ - if(this.replyingto){ + if(this.replyingto?.div){ this.replyingto.div.classList.remove("replying"); } replybox.classList.add("hideReplyBox"); @@ -707,7 +701,7 @@ class Channel{ loading.append(div); } } - lastmessage:Message; + lastmessage:Message|undefined; async putmessages(){ if(this.allthewayup){return}; if(this.lastreadmessageid&&this.lastreadmessageid.getObject()){ @@ -746,8 +740,7 @@ class Channel{ this.children=build; } async grabAfter(id:string){ - console.log(id,this.lastmessage.id) - if(id===this.lastmessage.id){ + if(id===this.lastmessage?.id){ return; } await fetch(this.info.api+"/channels/"+this.id+"/messages?limit=100&after="+id,{ @@ -960,7 +953,7 @@ class Channel{ } } async sendMessage(content:string,{attachments=[],embeds=[],replyingto=null}: - {attachments,embeds,replyingto:Message|null}){ + {attachments:Blob[],embeds,replyingto:Message|null}){ let replyjson:any; if(replyingto){ replyjson= @@ -1012,8 +1005,9 @@ class Channel{ console.log(this.lastmessageid,messagez.snowflake,":3"); if(this.lastmessageid){ this.idToNext.set(this.lastmessageid,messagez.snowflake); + this.idToPrev.set(messagez.snowflake,this.lastmessageid); } - this.idToPrev.set(messagez.snowflake,this.lastmessageid); + this.lastmessageid=messagez.snowflake; this.messageids.set(messagez.snowflake,messagez); @@ -1063,7 +1057,9 @@ class Channel{ const images=message.getimages(); if(images.length){ const image = images[0]; - imgurl||=image.proxy_url; + if(image.proxy_url){ + imgurl||=image.proxy_url; + } imgurl||=image.url; } const notification = new Notification(this.notititle(message),{ diff --git a/webpage/direct.ts b/webpage/direct.ts index 94f5846..5fc543b 100644 --- a/webpage/direct.ts +++ b/webpage/direct.ts @@ -39,12 +39,6 @@ class Direct extends Guild{ this.calculateReorder(); this.printServers(); } - sortchannels(){ - this.headchannels.sort((a,b)=>{ - const result=(a.lastmessageid.getUnixTime()-b.lastmessageid.getUnixTime()); - return Number(-result); - }); - } giveMember(_member:memberjson){ console.error("not a real guild, can't give member object") } @@ -111,7 +105,10 @@ class Group extends Channel{ this.lastmessageid??=null; this.mentions=0; this.setUpInfiniteScroller(); - this.position=Math.max(this.lastmessageid.getUnixTime(),this.snowflake.getUnixTime()); + if(this.lastmessageid){ + this.position=this.lastmessageid.getUnixTime() + } + this.position=-Math.max(this.position,this.snowflake.getUnixTime()); } createguildHTML(){ const div=document.createElement("div") @@ -140,14 +137,16 @@ class Group extends Channel{ return; } this.buildmessages(); - history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.snowflake); - document.getElementById("channelname").textContent="@"+this.name; - document.getElementById("channelTopic").setAttribute("hidden",""); - document.getElementById("typebox").contentEditable=""+true; + history.pushState(null, "","/channels/"+this.guild_id+"/"+this.id); + (document.getElementById("channelname") as HTMLElement).textContent="@"+this.name; + (document.getElementById("channelTopic") as HTMLElement).setAttribute("hidden",""); + (document.getElementById("typebox") as HTMLDivElement).contentEditable=""+true; } messageCreate(messagep){ const messagez=new Message(messagep.d,this); - this.idToNext.set(this.lastmessageid,messagez.snowflake); + if(this.lastmessageid){ + this.idToNext.set(this.lastmessageid,messagez.snowflake); + } this.idToPrev.set(messagez.snowflake,this.lastmessageid); this.lastmessageid=messagez.snowflake; this.messageids.set(messagez.snowflake,messagez); @@ -166,7 +165,7 @@ class Group extends 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"){ @@ -179,15 +178,15 @@ class Group extends Channel{ return message.author.username; } unreads(){ - const sentdms=document.getElementById("sentdms"); - let current=null; + const sentdms=document.getElementById("sentdms") as HTMLDivElement;//Need to change sometime + let current:HTMLElement|null=null; for(const thing of sentdms.children){ if(thing["all"]===this){ - current=thing; + current=thing as HTMLElement; } } if(this.hasunreads){ - if(current){current.noti.textContent=this.mentions;return;} + if(current){current["noti"].textContent=this.mentions;return;} const div=document.createElement("div"); div.classList.add("servernoti"); const noti=document.createElement("div"); diff --git a/webpage/embed.ts b/webpage/embed.ts index 8b96f27..fc176e7 100644 --- a/webpage/embed.ts +++ b/webpage/embed.ts @@ -62,7 +62,7 @@ class Embed{ authorline.append(img); } const a=document.createElement("a"); - a.textContent=this.json.author.name + a.textContent=this.json.author.name as string; if(this.json.author.url){ a.href=this.json.author.url } @@ -70,14 +70,15 @@ class Embed{ authorline.append(a); embed.append(authorline); } - const title=document.createElement("a"); - title.append(new MarkDown(this.json.title,this.channel).makeHTML()); - if(this.json.url){ - title.href=this.json.url; + if(this.json.title){ + const title=document.createElement("a"); + title.append(new MarkDown(this.json.title,this.channel).makeHTML()); + if(this.json.url){ + title.href=this.json.url; + } + title.classList.add("embedtitle"); + embed.append(title); } - title.classList.add("embedtitle"); - embed.append(title); - if(this.json.description){ const p=document.createElement("p"); p.append(new MarkDown(this.json.description,this.channel).makeHTML()); @@ -155,7 +156,7 @@ class Embed{ table.classList.add("embed","linkembed"); const trtop=document.createElement("tr"); table.append(trtop); - { + if(this.json.url&&this.json.title){ const td=document.createElement("td"); const a=document.createElement("a"); a.href=this.json.url; @@ -179,9 +180,11 @@ class Embed{ } const bottomtr=document.createElement("tr"); const td=document.createElement("td"); - const span=document.createElement("span"); - span.textContent=this.json.description; - td.append(span); + if(this.json.description){ + const span=document.createElement("span"); + span.textContent=this.json.description; + td.append(span); + } bottomtr.append(td); table.append(bottomtr) return table; @@ -200,14 +203,16 @@ class Embed{ div.append(provider); } const a=document.createElement("a"); - a.href=this.json.url; - a.textContent=this.json.title; - div.append(a); - - const description=document.createElement("p"); - description.textContent=this.json.description; - div.append(description); - + if(this.json.url&&this.json.url){ + a.href=this.json.url; + a.textContent=this.json.url; + div.append(a); + } + if(this.json.description){ + const description=document.createElement("p"); + description.textContent=this.json.description; + div.append(description); + } if(this.json.thumbnail){ const img=document.createElement("img"); img.classList.add("bigembedimg"); diff --git a/webpage/emoji.ts b/webpage/emoji.ts index d63b4cd..53de254 100644 --- a/webpage/emoji.ts +++ b/webpage/emoji.ts @@ -1,5 +1,6 @@ import { Contextmenu } from "./contextmenu.js"; import { Guild } from "./guild.js"; +import { emojijson } from "./jsontypes.js"; import { Localuser } from "./localuser.js"; class Emoji{ @@ -21,7 +22,7 @@ class Emoji{ } get localuser(){ if(this.owner instanceof Guild){ - return this.guild.localuser; + return this.owner.localuser; }else{ return this.owner; } @@ -81,7 +82,11 @@ class Emoji{ for(;cats!==0;cats--){ const name=readString16(); - const emojis=[]; + const emojis:{ + name:string, + skin_tone_support:boolean, + emoji:string + }[]=[]; let emojinumber=read16(); for(;emojinumber!==0;emojinumber--){ //console.log(emojis); diff --git a/webpage/file.ts b/webpage/file.ts index 601fd89..6027b91 100644 --- a/webpage/file.ts +++ b/webpage/file.ts @@ -3,16 +3,16 @@ import { Dialog } from "./dialog.js"; import { filejson } from "./jsontypes.js"; class File{ - owner:Message; + owner:Message|null; id:string; filename:string; content_type:string; - width:number; - height:number; - proxy_url:string; + width:number|undefined; + height:number|undefined; + proxy_url:string|undefined; url:string; size:number; - constructor(fileJSON:filejson,owner:Message){ + constructor(fileJSON:filejson,owner:Message|null){ this.owner=owner; this.id=fileJSON.id; this.filename=fileJSON.filename; @@ -26,7 +26,7 @@ class File{ } getHTML(temp:boolean=false):HTMLElement{ const src=this.proxy_url||this.url; - if(this.width){ + if(this.width&&this.height){ let scale=1; const max=96*3; scale=Math.max(scale,this.width/max); @@ -59,7 +59,7 @@ class File{ video.append(source); source.type=this.content_type; video.controls=!temp; - if(this.width){ + if(this.width&&this.height){ video.width=this.width; video.height=this.height; } @@ -97,7 +97,7 @@ class File{ return new File({ filename:file.name, size:file.size, - id:null, + id:"null", content_type:file.type, width:undefined, height:undefined, diff --git a/webpage/guild.ts b/webpage/guild.ts index d771dfe..5183dbb 100644 --- a/webpage/guild.ts +++ b/webpage/guild.ts @@ -9,6 +9,7 @@ import {Permissions} from "./permissions.js"; import { SnowFlake } from "./snowflake.js"; import { channeljson, guildjson, emojijson, memberjson } from "./jsontypes.js"; import { User } from "./user.js"; +import { Message } from "./message.js"; class Guild{ owner:Localuser; headers:Localuser["headers"]; @@ -106,9 +107,19 @@ class Guild{ this.roleids.set(roleh.snowflake,roleh); } if(member instanceof User){ - Member.resolveMember(member,this).then(_=>this.member=_); + Member.resolveMember(member,this).then(_=>{ + if(_){ + this.member=_ + }else{ + console.error("Member was unable to resolve"); + } + }); }else{ - Member.new(member,this).then(_=>this.member=_); + Member.new(member,this).then(_=>{ + if(_){ + this.member=_ + } + }); } for(const thing of json.channels){ @@ -200,9 +211,9 @@ class Guild{ } calculateReorder(){ let position=-1; - let build=[]; + let build:{id:SnowFlake,position:number|undefined,parent_id:SnowFlake|undefined}[]=[]; for(const thing of this.headchannels){ - 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<=position){ thing.position=(thisthing.position=position+1); } @@ -211,11 +222,10 @@ class Guild{ 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; + thing.move_id=null; } if(thisthing.position||thisthing.parent_id){ build.push(thisthing); - console.log(this.channelids[thisthing.parent_id]); } if(thing.children.length>0){ const things=thing.calculateReorder() @@ -373,11 +383,12 @@ class Guild{ return this.member.isAdmin() } async markAsRead(){ - const build={read_states:[]}; + const build:{read_states:{channel_id:SnowFlake,message_id:SnowFlake|null,read_state_type:number}[]}={read_states:[]}; for(const thing of this.channels){ if(thing.hasunreads){ build.read_states.push({channel_id:thing.snowflake,message_id:thing.lastmessageid,read_state_type:0}); thing.lastreadmessageid=thing.lastmessageid; + if(!thing.myhtml) continue; thing.myhtml.classList.remove("cunread"); } } @@ -395,7 +406,7 @@ class Guild{ } return this.member.hasRole(r); } - loadChannel(ID:string=undefined){ + loadChannel(ID:string|undefined=undefined){ if(ID&&this.channelids[ID]){ this.channelids[ID].getHTML(); return; diff --git a/webpage/index.ts b/webpage/index.ts index 972190e..4549167 100644 --- a/webpage/index.ts +++ b/webpage/index.ts @@ -47,16 +47,17 @@ function showAccountSwitcher(){ userinfo.addEventListener("click",_=>{ thisuser.unload(); thisuser.swapped=true; - document.getElementById("loading").classList.remove("doneloading"); - document.getElementById("loading").classList.add("loading"); + const loading=document.getElementById("loading") as HTMLDivElement; + loading.classList.remove("doneloading"); + loading.classList.add("loading"); thisuser=new Localuser(specialuser); users["currentuser"]=specialuser.uid; localStorage.setItem("userinfos",JSON.stringify(users)); thisuser.initwebsocket().then(_=>{ thisuser.loaduser(); thisuser.init(); - document.getElementById("loading").classList.add("doneloading"); - document.getElementById("loading").classList.remove("loading"); + loading.classList.add("doneloading"); + loading.classList.remove("loading"); console.log("done loading") }); @@ -81,12 +82,12 @@ function showAccountSwitcher(){ document.body.append(table); } { - const userinfo=document.getElementById("userinfo"); + const userinfo=document.getElementById("userinfo") as HTMLDivElement; userinfo.addEventListener("click",_=>{ _.stopImmediatePropagation(); showAccountSwitcher(); }) - const switchaccounts=document.getElementById("switchaccounts"); + const switchaccounts=document.getElementById("switchaccounts") as HTMLDivElement; switchaccounts.addEventListener("click",_=>{ _.stopImmediatePropagation(); showAccountSwitcher(); @@ -100,13 +101,14 @@ try{ thisuser.initwebsocket().then(_=>{ thisuser.loaduser(); thisuser.init(); - document.getElementById("loading").classList.add("doneloading"); - document.getElementById("loading").classList.remove("loading"); + const loading=document.getElementById("loading") as HTMLDivElement; + loading.classList.add("doneloading"); + loading.classList.remove("loading"); console.log("done loading") }); }catch(e){ console.error(e); - document.getElementById("load-desc").textContent="Account unable to start"; + (document.getElementById("load-desc") as HTMLSpanElement).textContent="Account unable to start"; thisuser=new Localuser(-1); } @@ -114,19 +116,24 @@ try{ { const menu=new Contextmenu("create rightclick"); menu.addbutton("Create channel",function(){ - thisuser.lookingguild.createchannels(); + if(thisuser.lookingguild){ + thisuser.lookingguild.createchannels(); + } },null,_=>{return thisuser.isAdmin()}) menu.addbutton("Create category",function(){ - thisuser.lookingguild.createcategory(); + if(thisuser.lookingguild){ + thisuser.lookingguild.createcategory(); + } },null,_=>{return thisuser.isAdmin()}) - menu.bind(document.getElementById("channels")) + menu.bind(document.getElementById("channels") as HTMLDivElement) } -const pasteimage=document.getElementById("pasteimage"); -let replyingto=null; +const pasteimage=document.getElementById("pasteimage") as HTMLDivElement; +let replyingto:Message|null=null; async function enter(event){ const channel=thisuser.channelfocus + if(!channel||!thisuser.channelfocus) return; channel.typingstart(); if(event.key === "Enter"&&!event.shiftKey){ event.preventDefault(); @@ -136,19 +143,20 @@ async function enter(event){ }else{ replyingto= thisuser.channelfocus.replyingto; let replying=replyingto; - if(replyingto){ + if(replyingto?.div){ replyingto.div.classList.remove("replying"); } thisuser.channelfocus.replyingto=null; channel.sendMessage(markdown.rawString,{ attachments:images, - replyingto:replying, + embeds:[], + replyingto:replying }) thisuser.channelfocus.makereplybox(); } while(images.length!=0){ images.pop(); - pasteimage.removeChild(imageshtml.pop()); + pasteimage.removeChild(imageshtml.pop() as HTMLElement); } typebox.innerHTML=""; return; @@ -175,11 +183,13 @@ function getguildinfo(){ */ const images:Blob[]=[]; -const imageshtml=[]; +const imageshtml:HTMLElement[]=[]; import { File } from "./file.js"; import { MarkDown } from "./markdown.js"; +import { Message } from "./message.js"; document.addEventListener('paste', async (e) => { + if(!e.clipboardData) return; Array.from(e.clipboardData.files).forEach(async (f) => { const file=File.initFromBlob(f); e.preventDefault(); @@ -195,19 +205,19 @@ setTheme(); function userSettings(){ thisuser.showusersettings(); } -document.getElementById("settings").onclick=userSettings; +(document.getElementById("settings") as HTMLImageElement).onclick=userSettings; if(mobile){ - document.getElementById("channelw").onclick=function(){ - (document.getElementById("channels").parentNode as HTMLElement).classList.add("collapse"); - document.getElementById("servertd").classList.add("collapse"); - document.getElementById("servers").classList.add("collapse"); + (document.getElementById("channelw") as HTMLDivElement).onclick=()=>{ + ((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.add("collapse"); + (document.getElementById("servertd") as HTMLDivElement).classList.add("collapse"); + (document.getElementById("servers") as HTMLDivElement).classList.add("collapse"); } - document.getElementById("mobileback").textContent="#"; - document.getElementById("mobileback").onclick=function(){ - (document.getElementById("channels").parentNode as HTMLElement).classList.remove("collapse"); - document.getElementById("servertd").classList.remove("collapse"); - document.getElementById("servers").classList.remove("collapse"); + (document.getElementById("mobileback") as HTMLDivElement).textContent="#"; + (document.getElementById("mobileback") as HTMLDivElement).onclick=()=>{ + ((document.getElementById("channels") as HTMLDivElement).parentNode as HTMLElement).classList.remove("collapse"); + (document.getElementById("servertd") as HTMLDivElement).classList.remove("collapse"); + (document.getElementById("servers") as HTMLDivElement).classList.remove("collapse"); } } diff --git a/webpage/infiniteScroller.ts b/webpage/infiniteScroller.ts index 3d47d4f..87911a2 100644 --- a/webpage/infiniteScroller.ts +++ b/webpage/infiniteScroller.ts @@ -1,20 +1,20 @@ class InfiniteScroller{ - readonly getIDFromOffset:(ID:string,offset:number)=>Promise; + readonly getIDFromOffset:(ID:string,offset:number)=>Promise; readonly getHTMLFromID:(ID:string)=>Promise; readonly destroyFromID:(ID:string)=>Promise; readonly reachesBottom:()=>void; private readonly minDist=3000; private readonly maxDist=8000; HTMLElements:[HTMLElement,string][]=[]; - div:HTMLDivElement; - scroll:HTMLDivElement; + div:HTMLDivElement|null; + scroll:HTMLDivElement|null; constructor(getIDFromOffset:InfiniteScroller["getIDFromOffset"],getHTMLFromID:InfiniteScroller["getHTMLFromID"],destroyFromID:InfiniteScroller["destroyFromID"],reachesBottom:InfiniteScroller["reachesBottom"]=()=>{}){ this.getIDFromOffset=getIDFromOffset; this.getHTMLFromID=getHTMLFromID; this.destroyFromID=destroyFromID; this.reachesBottom=reachesBottom; } - timeout:NodeJS.Timeout; + timeout:NodeJS.Timeout|null; async getDiv(initialId:string,bottom=true):Promise{ const div=document.createElement("div"); div.classList.add("messagecontainer"); @@ -52,6 +52,7 @@ class InfiniteScroller{ scrollTop:number; needsupdate=true; async updatestuff(){ + if(!this.scroll) return; this.timeout=null; this.scrollBottom = this.scroll.scrollHeight - this.scroll.scrollTop - this.scroll.clientHeight; this.scrollTop=this.scroll.scrollTop; @@ -67,6 +68,7 @@ class InfiniteScroller{ //this.watchForChange(); } async firstElement(id:string){ + if(!this.scroll) return; const html=await this.getHTMLFromID(id); this.scroll.appendChild(html); this.HTMLElements.push([html,id]); @@ -87,14 +89,21 @@ class InfiniteScroller{ } } private async watchForTop():Promise{ + if(!this.scroll) return false; let again=false; if(this.scrollTop===0){ this.scrollTop=1; this.scroll.scrollTop=1; } if(this.scrollTopthis.maxDist){ - again=true; + const html=this.HTMLElements.shift(); - await this.destroyFromID(html[1]); - this.scrollTop-=60; + if(html){ + again=true; + await this.destroyFromID(html[1]); + this.scrollTop-=60; + } } if(again){ await this.watchForTop(); @@ -123,13 +135,17 @@ class InfiniteScroller{ return again; } async watchForBottom():Promise{ + if(!this.scroll) return false; let again=false; const scrollBottom = this.scrollBottom; if(scrollBottomthis.maxDist){ - again=true; + const html=this.HTMLElements.pop(); - await this.destroyFromID(html[1]); - this.scrollBottom-=60; + if(html){ + await this.destroyFromID(html[1]); + this.scrollBottom-=60; + again=true; + } } if(again){ await this.watchForBottom(); @@ -158,11 +177,11 @@ class InfiniteScroller{ try{ if(this.currrunning){ - return; + return false; }else{ this.currrunning=true; } - if(!this.div){this.currrunning=false;return} + if(!this.div){this.currrunning=false;return false} const out=await Promise.allSettled([this.watchForTop(),this.watchForBottom()]) as {value:boolean}[]; const changed=(out[0].value||out[1].value); if(null===this.timeout&&changed){ @@ -174,9 +193,10 @@ class InfiniteScroller{ }catch(e){ console.error(e); } + return false; } async focus(id:string,flash=true){ - let element:HTMLElement; + let element:HTMLElement|undefined; for(const thing of this.HTMLElements){ if(thing[1]===id){ element=thing[0]; @@ -213,7 +233,9 @@ class InfiniteScroller{ await this.destroyFromID(thing[1]); } this.HTMLElements=[]; - clearTimeout(this.timeout); + if(this.timeout){ + clearTimeout(this.timeout); + } if(this.div){ this.div.remove(); } diff --git a/webpage/jsontypes.ts b/webpage/jsontypes.ts index 83d5609..1cbc190 100644 --- a/webpage/jsontypes.ts +++ b/webpage/jsontypes.ts @@ -130,7 +130,7 @@ type userjson={ public_flags: number, avatar: string, accent_color: number, - banner: string, + banner?: string, bio: string, bot: boolean, premium_since: string, @@ -157,10 +157,10 @@ type memberjson= { last_message_id?: boolean//What??? } type emojijson={ - name:string, - id?:string, - animated?:boolean - } + name:string, + id?:string, + animated?:boolean +} type guildjson={ application_command_counts: {[key:string]:number}, @@ -290,8 +290,8 @@ type filejson={ id:string, filename:string, content_type:string, - width:number, - height:number, + width?:number, + height?:number, proxy_url:string|undefined, url:string, size:number diff --git a/webpage/localuser.ts b/webpage/localuser.ts index e5ecc13..c2b524f 100644 --- a/webpage/localuser.ts +++ b/webpage/localuser.ts @@ -365,7 +365,7 @@ class Localuser{ const guild=SnowFlake.getSnowFlakeFromID(temp.d.guild_id,Guild).getObject(); let thing:Member|{id:string}; if(temp.d.member){ - thing=await Member.new(temp.d.member,guild); + thing=await Member.new(temp.d.member,guild) as Member; }else{ thing={id:temp.d.user_id} } @@ -701,6 +701,7 @@ class Localuser{ const guild=this.guildids.get(typing.d.guild_id); if(!guild) return; const memb=await Member.new(typing.d.member,guild); + if(!memb) return; if(memb.id===this.user.id){ console.log("you is typing") return; @@ -844,7 +845,7 @@ class Localuser{ }); const bclear=settingsLeft.addButtonInput("Clear banner","Clear",()=>{ bfile=null; - hypouser.banner = null; + hypouser.banner = undefined; settingsLeft.changed(); regen(); }) diff --git a/webpage/markdown.ts b/webpage/markdown.ts index bb192e6..1ce84be 100644 --- a/webpage/markdown.ts +++ b/webpage/markdown.ts @@ -57,7 +57,7 @@ class MarkDown{ if(first){ i--; } - let element=null; + let element:HTMLElement; let keepys=""; if(txt[i+1]==="#"){ @@ -92,21 +92,24 @@ class MarkDown{ if(!first&&!stdsize){ span.appendChild(document.createElement("br")); } - const build=[]; + const build:string[]=[]; for(;txt[i]!=="\n"&&txt[i]!==undefined;i++){ build.push(txt[i]); } - if(stdsize){ - element=document.createElement("span"); + try{ + if(stdsize){ + element=document.createElement("span"); + }else continue; + if(keep){ + element.append(keepys); + } + element.appendChild(this.markdown(build,{keep:keep,stdsize:stdsize})); + span.append(element); + }finally{ + i-=1; + console.log(txt[i]); + continue; } - if(keep){ - element.append(keepys); - } - element.appendChild(this.markdown(build,{keep:keep,stdsize:stdsize})); - span.append(element); - i-=1; - console.log(txt[i]); - continue; } if(first){ i++; @@ -194,7 +197,7 @@ class MarkDown{ count++; } } - let build=[]; + let build:string[]=[]; let find=0; let j=i+count; for(;txt[j]!==undefined&&find!==count;j++){ @@ -248,7 +251,7 @@ class MarkDown{ count++; } } - let build=[]; + let build:string[]=[]; let find=0; let j=i+count; for(;txt[j]!==undefined&&find!==count;j++){ @@ -295,7 +298,7 @@ class MarkDown{ if(txt[i]==="~"&&txt[i+1]==="~"){ let count=2; - let build=[]; + let build:string[]=[]; let find=0; let j=i+2; for(;txt[j]!==undefined&&find!==count;j++){ @@ -325,7 +328,7 @@ class MarkDown{ } if(txt[i]==="|"&&txt[i+1]==="|"){ let count=2; - let build=[]; + let build:string[]=[]; let find=0; let j=i+2; for(;txt[j]!==undefined&&find!==count;j++){ @@ -372,8 +375,7 @@ class MarkDown{ if (found) { appendcurrent(); i=j; - - const parts=build.join("").match(/^$/); + const parts=build.join("").match(/^$/) as RegExpMatchArray; const dateInput=new Date(Number.parseInt(parts[1]) * 1000); let time=""; if (Number.isNaN(dateInput.getTime())) time=build.join(""); @@ -449,19 +451,23 @@ class MarkDown{ } }; box.onpaste=_=>{ + if(!_.clipboardData) return; console.log(_.clipboardData.types) const data=_.clipboardData.getData("text"); document.execCommand('insertHTML', false, data); _.preventDefault(); + if(!box.onkeyup) return; box.onkeyup(new KeyboardEvent("_")) } } boxupdate(box:HTMLElement){ - var restore = saveCaretPosition(box); + const restore = saveCaretPosition(box); box.innerHTML=""; box.append(this.makeHTML({keep:true})) - restore(); + if(restore){ + restore(); + } } static gatherBoxText(element:HTMLElement){ if(element.tagName.toLowerCase()==="img"){ @@ -492,11 +498,13 @@ class MarkDown{ //solution from https://stackoverflow.com/questions/4576694/saving-and-restoring-caret-position-for-contenteditable-div function saveCaretPosition(context){ var selection = window.getSelection(); + if(!selection) return; var range = selection.getRangeAt(0); range.setStart( context, 0 ); var len = range.toString().length; return function restore(){ + if(!selection) return; var pos = getTextNodeAtPosition(context, len); selection.removeAllRanges(); var range = new Range(); @@ -509,6 +517,7 @@ function saveCaretPosition(context){ function getTextNodeAtPosition(root, index){ const NODE_TYPE = NodeFilter.SHOW_TEXT; var treeWalker = document.createTreeWalker(root, NODE_TYPE, function next(elem) { + if(!elem.textContent) return 0; if(index > elem.textContent.length){ index -= elem.textContent.length; return NodeFilter.FILTER_REJECT diff --git a/webpage/member.ts b/webpage/member.ts index 8578bfb..1e4166d 100644 --- a/webpage/member.ts +++ b/webpage/member.ts @@ -28,8 +28,10 @@ class Member{ private constructor(memberjson:memberjson,owner:Guild){ if(User.userids[memberjson.id]){ this.user=User.userids[memberjson.id]; - }else{ + }else if(memberjson.user){ this.user=new User(memberjson.user,owner.localuser); + }else{ + throw new Error("Missing user object of this member"); } this.owner=owner; for(const thing of Object.keys(memberjson)){ @@ -60,12 +62,14 @@ class Member{ get info(){ return this.owner.info; } - static async new(memberjson:memberjson,owner:Guild):Promise{ + static async new(memberjson:memberjson,owner:Guild):Promise{ let user:User; if(User.userids[memberjson.id]){ user=User.userids[memberjson.id]; - }else{ + }else if(memberjson.user){ user=new User(memberjson.user,owner.localuser); + }else{ + throw new Error("missing user object of this member"); } if(user.members.has(owner)){ let memb=user.members.get(owner) @@ -88,9 +92,7 @@ class Member{ const maybe=user.members.get(guild); if(!user.members.has(guild)){ const membpromise=guild.localuser.resolvemember(user.id,guild.id); - let res:Function; - const promise=new Promise(r=>{res=r}) - user.members.set(guild,promise); + const promise=new Promise( async res=>{ const membjson=await membpromise; if(membjson===undefined){ res(undefined); @@ -103,6 +105,8 @@ class Member{ res(member); return member; } + }) + user.members.set(guild,promise); } if(maybe instanceof Promise){ return await maybe; @@ -110,7 +114,7 @@ class Member{ return maybe } } - public getPresence(presence:presencejson|null){ + public getPresence(presence:presencejson|undefined){ this.user.getPresence(presence); } /** diff --git a/webpage/message.ts b/webpage/message.ts index 6e296d0..c125b92 100644 --- a/webpage/message.ts +++ b/webpage/message.ts @@ -8,7 +8,7 @@ import {Localuser} from "./localuser.js"; import { Role } from "./role.js"; import {File} from "./file.js"; import { SnowFlake } from "./snowflake.js"; -import { messagejson } from "./jsontypes.js"; +import { memberjson, messagejson } from "./jsontypes.js"; import {Emoji} from "./emoji.js"; class Message{ @@ -40,8 +40,8 @@ class Message{ return this.weakdiv?.deref(); } //*/ - div:HTMLDivElement; - member:Member; + div:HTMLDivElement|undefined; + member:Member|undefined; reactions:messagejson["reactions"]; get id(){ return this.snowflake.id; @@ -50,12 +50,6 @@ class Message{ this.del=new Promise(_=>{this.resolve=_}); Message.setupcmenu(); } - static async wipeChanel(){ - this.resolve(); - document.getElementById("messages").innerHTML=""; - await Promise.allSettled([this.resolve]); - this.del=new Promise(_=>{this.resolve=_}) - } static setupcmenu(){ Message.contextmenu.addbutton("Copy raw text",function(this:Message){ navigator.clipboard.writeText(this.content.rawString); @@ -74,9 +68,9 @@ class Message{ }); Message.contextmenu.addbutton("Edit",function(this:Message){ this.channel.editing=this; - const markdown=(document.getElementById("typebox"))["markdown"] as MarkDown; + const markdown=(document.getElementById("typebox") as HTMLDivElement)["markdown"] as MarkDown; markdown.txt=this.content.rawString.split(''); - markdown.boxupdate(document.getElementById("typebox")); + markdown.boxupdate(document.getElementById("typebox") as HTMLDivElement); },null,_=>{return _.author.id===_.localuser.user.id}); Message.contextmenu.addbutton("Delete message",function(this:Message){ this.delete(); @@ -86,7 +80,7 @@ class Message{ this.owner=owner; this.headers=this.owner.headers; this.giveData(messagejson); - + this.owner.messages.set(this.id,this); } reactionToggle(emoji:string|Emoji){ @@ -125,7 +119,9 @@ class Message{ this.snowflake=new SnowFlake(messagejson.id,this); continue; }else if(thing==="member"){ - Member.new(messagejson.member,this.guild).then(_=>{this.member=_}); + Member.new(messagejson.member as memberjson,this.guild).then(_=>{ + this.member=_ as Member; + }); continue; }else if(thing ==="embeds"){ this.embeds=[]; @@ -185,7 +181,7 @@ class Message{ if(!this.div) return; try{ this.div.remove(); - this.div=null; + this.div=undefined; }catch(e){ console.error(e) } @@ -222,23 +218,35 @@ class Message{ deleteEvent(){ if(this.div){ this.div.innerHTML=""; - this.div=null; + this.div=undefined; + } + const prev=this.channel.idToPrev.get(this.snowflake) as SnowFlake | null; + const next=this.channel.idToNext.get(this.snowflake) as SnowFlake | null; + if(prev){ + this.channel.idToPrev.delete(this.snowflake) + } + if(next){ + this.channel.idToNext.delete(this.snowflake) + } + if(prev&&next){ + this.channel.idToPrev.set(next,prev); + this.channel.idToNext.set(prev,next); } - const prev=this.channel.idToPrev.get(this.snowflake); - const next=this.channel.idToNext.get(this.snowflake); - this.channel.idToNext.set(prev,next); - this.channel.idToPrev.set(next,prev); this.channel.messageids.delete(this.snowflake); - const regen=prev.getObject(); - if(regen){ - regen.generateMessage(); + if(prev&&prev.getObject()){ + prev.getObject().generateMessage(); } if(this.channel.lastmessage===this){ - this.channel.lastmessage=prev.getObject(); + if(prev){ + this.channel.lastmessage=prev.getObject(); + }else{ + this.channel.lastmessage=undefined; + } } } reactdiv:WeakRef; - generateMessage(premessage:Message=null){ + generateMessage(premessage:Message|undefined=undefined){ + if(!this.div) return; if(!premessage){ premessage=this.channel.idToPrev.get(this.snowflake)?.getObject(); } @@ -469,13 +477,11 @@ class Message{ } } } - buildhtml(premessage:Message){ + buildhtml(premessage:Message|undefined=undefined){ if(this.div){console.error(`HTML for ${this.snowflake} already exists, aborting`);return;} try{ - //premessage??=messages.lastChild; const div=document.createElement("div"); this.div=div; - this.messageevents(div); return this.generateMessage(premessage); }catch(e){ diff --git a/webpage/role.ts b/webpage/role.ts index 808c60a..4a58382 100644 --- a/webpage/role.ts +++ b/webpage/role.ts @@ -38,7 +38,7 @@ class Role{ get localuser():Localuser{ return this.guild.localuser; } - getColor():string{ + getColor():string|null{ if(this.color===0){return null}; return `#${this.color.toString(16)}`; } diff --git a/webpage/snowflake.ts b/webpage/snowflake.ts index 81480ac..eb5e048 100644 --- a/webpage/snowflake.ts +++ b/webpage/snowflake.ts @@ -32,7 +32,7 @@ class SnowFlake{ } /** * Just to clarify bc TS, it returns a SnowFlake\ which is what you entered with the type parameter - * + * @deprecated **/ static getSnowFlakeFromID(id:string,type: abstract new(...args: never) => T): SnowFlake{ if(!SnowFlake.SnowFlakes.get(type)){ @@ -56,6 +56,11 @@ class SnowFlake{ return snowflake; } } + /** + * @deprecated + * + * + */ static hasSnowFlakeFromID(id:string,type:any){ if(!SnowFlake.SnowFlakes.get(type)){ return false; diff --git a/webpage/user.ts b/webpage/user.ts index 5a22ccc..72c0aee 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|null|undefined; + banner: string|undefined; hypotheticalbanner:boolean; premium_since: string; premium_type: number; @@ -46,7 +46,7 @@ class User{ badge_ids:this.badge_ids },this.owner) } - public getPresence(presence:presencejson|null){ + public getPresence(presence:presencejson|undefined){ if(presence){ this.setstatus(presence.status); }else{ @@ -183,7 +183,7 @@ class User{ this.changepfp(json.avatar); } } - bind(html:HTMLElement,guild:Guild=null,error=true){ + bind(html:HTMLElement,guild:Guild|null=null,error=true){ if(guild&&guild.id!=="@me"){ Member.resolveMember(this,guild).then(_=>{ if(_===undefined&&error){ @@ -193,12 +193,19 @@ class User{ html.after(error); return; } - _.bind(html); + if(_){ + _.bind(html); + } }).catch(_=>{ console.log(_) }); } - this.profileclick(html,guild); + if(guild){ + this.profileclick(html,guild); + }else{ + this.profileclick(html); + } + User.contextmenu.bind(html,this); } static async resolve(id:string,localuser:Localuser){ @@ -230,7 +237,7 @@ class User{ createjankpromises(){ new Promise(_=>{}) } - async buildprofile(x:number,y:number,guild:Guild=null){ + async buildprofile(x:number,y:number,guild:Guild|null=null){ if(Contextmenu.currentmenu!=""){ Contextmenu.currentmenu.remove(); } @@ -271,18 +278,20 @@ class User{ if(!this.badge_ids) return; for(const id of this.badge_ids){ const badgejson=await this.getBadge(id); - const badge=document.createElement(badgejson.link?"a":"div"); - badge.classList.add("badge") - const img=document.createElement("img"); - img.src=badgejson.icon; - badge.append(img); - const span=document.createElement("span"); - span.textContent=badgejson.description; - badge.append(span); - if(badge instanceof HTMLAnchorElement){ - badge.href=badgejson.link; + if(badgejson){ + const badge=document.createElement(badgejson.link?"a":"div"); + badge.classList.add("badge") + const img=document.createElement("img"); + img.src=badgejson.icon; + badge.append(img); + const span=document.createElement("span"); + span.textContent=badgejson.description; + badge.append(span); + if(badge instanceof HTMLAnchorElement){ + badge.href=badgejson.link; + } + badgediv.append(badge); } - badgediv.append(badge); } })() { @@ -341,7 +350,7 @@ class User{ } return div; } - profileclick(obj:HTMLElement,guild:Guild){ + profileclick(obj:HTMLElement,guild:Guild|undefined=undefined){ obj.onclick=e=>{ this.buildprofile(e.clientX,e.clientY,guild); e.stopPropagation();