diff --git a/.dist/channel.js b/.dist/channel.js index 22b1527..ee7119a 100644 --- a/.dist/channel.js +++ b/.dist/channel.js @@ -5,6 +5,7 @@ import { Contextmenu } from "./contextmenu.js"; import { Fullscreen } from "./fullscreen.js"; import { Permissions } from "./permissions.js"; import { Settings, RoleList } from "./settings.js"; +import { Role } from "./role.js"; import { InfiniteScroller } from "./infiniteScroller.js"; import { SnowFlake } from "./snowflake.js"; class Channel { @@ -78,7 +79,7 @@ class Channel { this.infinite = new InfiniteScroller(async function (id, offset) { const snowflake = SnowFlake.getSnowFlakeFromID(id, Message); if (offset === 1) { - if (this.idToPrev.get(snowflake)) { + if (this.idToPrev.has(snowflake)) { return this.idToPrev.get(snowflake)?.id; } else { @@ -87,13 +88,25 @@ class Channel { } } else { - return this.idToNext.get(snowflake)?.id; + if (this.idToNext.has(snowflake)) { + return this.idToNext.get(snowflake)?.id; + } + else if (this.lastmessage.id !== id) { + await this.grabAfter(id); + return this.idToNext.get(snowflake)?.id; + } + else { + console.log("at bottom"); + } } - }.bind(this), function (id) { + }.bind(this), async function (id) { let res; const promise = new Promise(_ => { res = _; }); const snowflake = SnowFlake.getSnowFlakeFromID(id, Message); - const html = this.messageids.get(snowflake).buildhtml(this.messageids.get(this.idToPrev.get(snowflake)), promise); + if (!snowflake.getObject()) { + await this.grabArround(id); + } + const html = snowflake.getObject().buildhtml(this.messageids.get(this.idToPrev.get(snowflake)), promise); ids[id] = res; return html; }.bind(this), async function (id) { @@ -125,7 +138,7 @@ class Channel { } ; this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); - this.permission_overwritesar.push([thing.id, this.permission_overwrites.get(thing.id)]); + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), this.permission_overwrites.get(thing.id)]); } this.topic = json.topic; this.nsfw = json.nsfw; @@ -528,7 +541,7 @@ class Channel { return; } this.makereplybox(); - this.buildmessages(); + await this.buildmessages(); history.pushState(null, null, "/channels/" + this.guild_id + "/" + this.snowflake); document.getElementById("channelname").textContent = "#" + this.name; console.log(this); @@ -566,69 +579,111 @@ class Channel { delChannel(json) { const build = []; for (const thing of this.children) { - if (thing.snowflake !== json.id) { + if (thing.id !== json.id) { build.push(thing); } } this.children = build; } - async grabBefore(id) { - if (this.allthewayup) { + async grabAfter(id) { + console.log(id, this.lastmessage.id); + if (id === this.lastmessage.id) { return; } - await fetch(this.info.api.toString() + "/channels/" + this.snowflake + "/messages?before=" + id + "&limit=100", { + await fetch(this.info.api.toString() + "/channels/" + this.id + "/messages?limit=100&after=" + id, { headers: this.headers }).then((j) => { return j.json(); }).then(response => { - let next; - if (response.length === 0) { - this.allthewayup = true; - } let previd = SnowFlake.getSnowFlakeFromID(id, Message); for (const i in response) { let messager; - if (!next) { + let willbreak = false; + if (!SnowFlake.hasSnowFlakeFromID(response[i].id, Message)) { messager = new Message(response[i], this); } else { - messager = next; + messager = SnowFlake.getSnowFlakeFromID(response[i].id, Message).getObject(); + willbreak = true; } - if (response[+i + 1] !== undefined) { - next = new Message(response[+i + 1], this); - } - else { - next = undefined; - console.log("ohno", +i + 1); - } - if (this.messageids.get(messager.snowflake) === undefined) { - this.idToNext.set(messager.snowflake, previd); - this.idToPrev.set(previd, messager.snowflake); - previd = messager.snowflake; - this.messageids.set(messager.snowflake, messager); - } - else { - console.log("How???"); + this.idToPrev.set(messager.snowflake, previd); + this.idToNext.set(previd, messager.snowflake); + previd = messager.snowflake; + this.messageids.set(messager.snowflake, messager); + if (willbreak) { + break; } } //out.buildmessages(); }); return; } + topid; + async grabBefore(id) { + if (this.topid && id === this.topid.id) { + return; + } + await fetch(this.info.api.toString() + "/channels/" + this.snowflake + "/messages?before=" + id + "&limit=100", { + headers: this.headers + }).then((j) => { return j.json(); }).then((response) => { + if (response.length < 100) { + this.allthewayup = true; + if (response.length === 0) { + this.topid = SnowFlake.getSnowFlakeFromID(id, Message); + } + } + let previd = SnowFlake.getSnowFlakeFromID(id, Message); + for (const i in response) { + let messager; + let willbreak = false; + if (!SnowFlake.hasSnowFlakeFromID(response[i].id, Message)) { + messager = new Message(response[i], this); + } + else { + console.log("flaky"); + messager = SnowFlake.getSnowFlakeFromID(response[i].id, Message).getObject(); + willbreak = true; + } + this.idToNext.set(messager.snowflake, previd); + this.idToPrev.set(previd, messager.snowflake); + previd = messager.snowflake; + this.messageids.set(messager.snowflake, messager); + if (+i === response.length - 1 && response.length < 100) { + this.topid = previd; + } + if (willbreak) { + break; + } + } + }); + return; + } + /** + * Please dont use this, its not implemented. + **/ + async grabArround(id) { + throw new Error("please don't call this, no one has implmented it :P"); + } buildmessage(message, next) { const built = message.buildhtml(next); document.getElementById("messages").prepend(built); } - buildmessages() { + async buildmessages() { const messages = document.getElementById("channelw"); messages.innerHTML = ""; let id; - if (this.messageids.get(this.lastreadmessageid)) { + if (this.lastreadmessageid && this.lastreadmessageid.getObject()) { id = this.lastreadmessageid; } else if (this.lastmessage.snowflake) { id = this.goBackIds(this.lastmessage.snowflake, 50); console.log("shouldn't"); } - messages.append(this.infinite.getDiv(id.id)); + console.log(this.lastreadmessageid, id.id); + messages.append(await this.infinite.getDiv(id.id)); + this.infinite.updatestuff(); + this.infinite.watchForChange().then(async (_) => { + await new Promise(resolve => setTimeout(resolve, 100)); + this.infinite.focus(id.id, false); //if someone could figure out how to make this work correctly without this, that's be great :P + }); } goBackIds(id, back) { while (back !== 0) { @@ -659,7 +714,7 @@ class Channel { } ; this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny)); - this.permission_overwritesar.push([thing.id, this.permission_overwrites.get(thing.id)]); + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id, Role), this.permission_overwrites.get(thing.id)]); } this.topic = json.topic; this.nsfw = json.nsfw; @@ -745,6 +800,7 @@ class Channel { return; } const messagez = new Message(messagep.d, this); + this.lastmessage = messagez; console.log(this.lastmessageid, messagez.snowflake, ":3"); this.idToNext.set(this.lastmessageid, messagez.snowflake); this.idToPrev.set(messagez.snowflake, this.lastmessageid); @@ -762,7 +818,9 @@ class Channel { } } this.guild.unreads(); - this.infinite.addedBottom(); + if (this === this.localuser.channelfocus) { + this.infinite.addedBottom(); + } if (messagez.author === this.localuser.user) { return; } diff --git a/.dist/direct.js b/.dist/direct.js index d6cb754..091cc4c 100644 --- a/.dist/direct.js +++ b/.dist/direct.js @@ -7,7 +7,6 @@ class Direct extends Guild { constructor(json, owner) { super(-1, owner, null); this.message_notifications = 0; - console.log(json); this.owner = owner; if (!this.localuser) { console.error("Owner was not included, please fix"); @@ -41,7 +40,7 @@ class Direct extends Guild { return Number(-result); }); } - giveMember(member) { + giveMember(_member) { console.error("not a real guild, can't give member object"); } getRole(ID) { @@ -169,13 +168,11 @@ class Group extends Channel { const noti = document.createElement("div"); noti.classList.add("unread", "notiunread", "pinged"); noti.textContent = "" + this.mentions; - console.log(this.mentions); div["noti"] = noti; div.append(noti); const buildpfp = this.user.buildpfp(); div["all"] = this; buildpfp.classList.add("mentioned"); - console.log(this); div.append(buildpfp); sentdms.append(div); div.onclick = _ => { diff --git a/.dist/embed.js b/.dist/embed.js index 76fcbf3..5033ecd 100644 --- a/.dist/embed.js +++ b/.dist/embed.js @@ -40,7 +40,6 @@ class Embed { return this.guild.localuser; } generateRich() { - console.log(this.json); const div = document.createElement("div"); if (this.json.color) { div.style.backgroundColor = "#" + this.json.color.toString(16); diff --git a/.dist/file.js b/.dist/file.js index 90979c1..2e80120 100644 --- a/.dist/file.js +++ b/.dist/file.js @@ -10,7 +10,6 @@ class File { url; size; constructor(fileJSON, owner) { - console.log(fileJSON); this.owner = owner; this.id = fileJSON.id; this.filename = fileJSON.filename; diff --git a/.dist/infiniteScroller.js b/.dist/infiniteScroller.js index ec34c33..3cf1cf3 100644 --- a/.dist/infiniteScroller.js +++ b/.dist/infiniteScroller.js @@ -15,7 +15,7 @@ class InfiniteScroller { this.reachesBottom = reachesBottom; } interval; - getDiv(initialId, bottom = true) { + async getDiv(initialId, bottom = true) { const div = document.createElement("div"); div.classList.add("messagecontainer"); //div.classList.add("flexttb") @@ -28,10 +28,10 @@ class InfiniteScroller { this.scroll.addEventListener("scroll", this.watchForChange.bind(this)); new ResizeObserver(this.watchForChange.bind(this)).observe(div); new ResizeObserver(this.watchForChange.bind(this)).observe(scroll); - this.firstElement(initialId); + await this.firstElement(initialId); this.updatestuff(); - this.watchForChange().then(_ => { - this.scroll.scrollTop = this.scroll.scrollHeight; + await this.watchForChange().then(_ => { + this.updatestuff(); }); return div; } @@ -45,8 +45,8 @@ class InfiniteScroller { } //this.watchForChange(); } - firstElement(id) { - const html = this.getHTMLFromID(id); + async firstElement(id) { + const html = await this.getHTMLFromID(id); this.scroll.append(html); this.HTMLElements.push([html, id]); } @@ -59,23 +59,8 @@ class InfiniteScroller { this.scroll.scrollTop = this.scroll.scrollHeight; } } - async watchForChange() { - if (this.currrunning) { - return; - } - else { - this.currrunning = true; - } + async watchForTop() { let again = false; - if (!this.div) { - this.currrunning = false; - return; - } - /* - if(this.scrollTop===0){ - this.scrollTop=10; - } - */ if (this.scrollTop === 0) { this.scrollTop = 1; this.scroll.scrollTop = 1; @@ -87,7 +72,7 @@ class InfiniteScroller { } else { again = true; - const html = this.getHTMLFromID(nextid); + const html = await this.getHTMLFromID(nextid); this.scroll.prepend(html); this.HTMLElements.unshift([html, nextid]); this.scrollTop += 60; @@ -100,6 +85,12 @@ class InfiniteScroller { await this.destroyFromID(html[1]); this.scrollTop -= 60; } + if (again) { + await this.watchForTop(); + } + } + async watchForBottom() { + let again = false; const scrollBottom = this.scrollBottom; if (scrollBottom < this.minDist) { const previd = this.HTMLElements.at(-1)[1]; @@ -108,7 +99,7 @@ class InfiniteScroller { } else { again = true; - const html = this.getHTMLFromID(nextid); + const html = await this.getHTMLFromID(nextid); this.scroll.append(html); this.HTMLElements.push([html, nextid]); this.scrollBottom += 60; @@ -124,12 +115,52 @@ class InfiniteScroller { await this.destroyFromID(html[1]); this.scrollBottom -= 60; } - this.currrunning = false; if (again) { - await this.watchForChange(); + await this.watchForBottom(); } + } + async watchForChange() { + if (this.currrunning) { + return; + } + else { + this.currrunning = true; + } + if (!this.div) { + this.currrunning = false; + return; + } + await Promise.allSettled([this.watchForBottom(), this.watchForTop()]); this.currrunning = false; } + async focus(id, flash = true) { + let element; + for (const thing of this.HTMLElements) { + if (thing[1] === id) { + element = thing[0]; + } + } + console.log(element, id, ":3"); + if (element) { + element.scrollIntoView(); + if (flash) { + element.classList.remove("jumped"); + await new Promise(resolve => setTimeout(resolve, 100)); + element.classList.add("jumped"); + } + } + else { + for (const thing of this.HTMLElements) { + await this.destroyFromID(thing[1]); + } + this.HTMLElements = []; + await this.firstElement(id); + this.updatestuff(); + await this.watchForChange(); + await new Promise(resolve => setTimeout(resolve, 100)); + await this.focus(id, true); + } + } async delete() { for (const thing of this.HTMLElements) { await this.destroyFromID(thing[1]); diff --git a/.dist/jsontypes.js b/.dist/jsontypes.js new file mode 100644 index 0000000..cb0ff5c --- /dev/null +++ b/.dist/jsontypes.js @@ -0,0 +1 @@ +export {}; diff --git a/.dist/localuser.js b/.dist/localuser.js index d66cc47..7e95b6d 100644 --- a/.dist/localuser.js +++ b/.dist/localuser.js @@ -85,8 +85,8 @@ class Localuser { this.typing = []; } outoffocus() { - document.getElementById("servers").textContent = ""; - document.getElementById("channels").textContent = ""; + document.getElementById("servers").innerHTML = ""; + document.getElementById("channels").innerHTML = ""; if (this.channelfocus) { this.channelfocus.infinite.delete(); } @@ -99,7 +99,7 @@ class Localuser { this.outoffocus(); this.guilds = []; this.guildids = new Map(); - this.ws.close(4000); + this.ws.close(4001); } async initwebsocket() { let returny = null; @@ -114,7 +114,7 @@ class Localuser { "capabilities": 16381, "properties": { "browser": "Jank Client", - "client_build_number": 0, + "client_build_number": 0, //might update this eventually lol "release_channel": "Custom", "browser_user_agent": navigator.userAgent }, @@ -262,7 +262,7 @@ class Localuser { delChannel(json) { json.guild_id ??= "@me"; this.guildids.get(json.guild_id).delChannel(json); - if (json.guild_id === this.lookingguild.snowflake) { + if (json.guild_id === this.lookingguild.id) { this.loadGuild(json.guild_id); } } @@ -288,7 +288,6 @@ class Localuser { if (!guild) { guild = this.guildids.get("@me"); } - console.log(this.guildids, id, guild); if (this.lookingguild) { this.lookingguild.html.classList.remove("serveropen"); } @@ -345,7 +344,6 @@ class Localuser { div.classList.add("home", "servericon"); serverlist.appendChild(div); div.onclick = _ => { - console.log("clicked :3"); this.createGuild(); }; const guildDiscoveryContainer = document.createElement("div"); @@ -356,7 +354,6 @@ class Localuser { this.guildDiscovery(); }); } - console.log("test"); this.unreads(); } createGuild() { @@ -369,7 +366,6 @@ class Localuser { "Invite Link/Code", "", function () { - console.log(this); inviteurl = this.value; } ], @@ -389,7 +385,6 @@ class Localuser { method: "POST", headers: this.headers, }).then(r => r.json()).then(_ => { - console.log(_); if (_.message) { error.textContent = _.message; } @@ -463,13 +458,11 @@ class Localuser { this.unreads(); } unreads() { - console.log(this.guildhtml); for (const thing of this.guilds) { if (thing.id === "@me") { continue; } const html = this.guildhtml.get(thing.id); - console.log(html); thing.unreads(html); } } @@ -506,7 +499,6 @@ class Localuser { updatepfp(file) { var reader = new FileReader(); reader.readAsDataURL(file); - console.log(this.headers); reader.onload = () => { fetch(this.info.api.toString() + "/users/@me", { method: "PATCH", @@ -515,7 +507,6 @@ class Localuser { avatar: reader.result, }) }); - console.log(reader.result); }; } updatepronouns(pronouns) { @@ -559,7 +550,6 @@ class Localuser { else { build += " is typing"; } - console.log(typingtext.classList); if (showing) { typingtext.classList.remove("hidden"); document.getElementById("typingtext").textContent = build; @@ -573,7 +563,7 @@ class Localuser { let file = null; let newprouns = null; let newbio = null; - let hypouser = new User(this.user, this, true); + let hypouser = this.user.clone(); function regen() { hypotheticalProfile.textContent = ""; const hypoprofile = hypouser.buildprofile(-1, -1); @@ -627,7 +617,7 @@ class Localuser { ] ], _ => { }, function () { console.log(this); - hypouser = new User(this.user, this); + hypouser = this.user.clone(); regen(); file = null; newprouns = null; diff --git a/.dist/login.js b/.dist/login.js index 4230c5b..05826b0 100644 --- a/.dist/login.js +++ b/.dist/login.js @@ -175,13 +175,19 @@ async function login(username, password, captcha) { console.log(response); if (response.captcha_sitekey) { const capt = document.getElementById("h-captcha"); - const capty = document.createElement("div"); - capty.classList.add("h-captcha"); - capty.setAttribute("data-sitekey", response.captcha_sitekey); - const script = document.createElement("script"); - script.src = "https://js.hcaptcha.com/1/api.js"; - capt.append(script); - capt.append(capty); + if (!capt.children.length) { + const capty = document.createElement("div"); + capty.classList.add("h-captcha"); + capty.setAttribute("data-sitekey", response.captcha_sitekey); + const script = document.createElement("script"); + script.src = "https://js.hcaptcha.com/1/api.js"; + capt.append(script); + capt.append(capty); + } + else { + eval("hcaptcha.reset()"); + } + return; } else { adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }); diff --git a/.dist/member.js b/.dist/member.js index c2f20e3..0e9d5c4 100644 --- a/.dist/member.js +++ b/.dist/member.js @@ -9,6 +9,7 @@ class Member { user; roles; error; + id; static contextmenu = new Contextmenu("User Menu"); static setUpContextMenu() { this.contextmenu.addbutton("Copy user id", function () { @@ -27,11 +28,12 @@ class Member { let membery = memberjson; this.roles = []; if (!error) { - if (memberjson.guild_member) { + if (memberjson["guild_member"]) { + memberjson = memberjson; membery = memberjson.guild_member; - this.user = memberjson.user; } } + membery = membery; for (const thing of Object.keys(membery)) { if (thing === "guild") { continue; @@ -52,7 +54,11 @@ class Member { this.user = memberjson; } else { - this.user = new User(this.user, owner.localuser); + if (SnowFlake.getSnowFlakeFromID(this?.id, User)) { + this.user = SnowFlake.getSnowFlakeFromID(this.id, User).getObject(); + return; + } + this.user = new User(membery.user, owner.localuser); } } get guild() { diff --git a/.dist/message.js b/.dist/message.js index dcf32cf..55f2993 100644 --- a/.dist/message.js +++ b/.dist/message.js @@ -22,6 +22,7 @@ class Message { static del; static resolve; div; + member; get id() { return this.snowflake.id; } @@ -43,7 +44,7 @@ class Message { this.channel.setReplying(this); }); Message.contextmenu.addbutton("Copy message id", function () { - navigator.clipboard.writeText(this.id.id); + navigator.clipboard.writeText(this.id); }); Message.contextmenu.addbutton("Edit", function () { this.channel.editing = this; @@ -77,15 +78,28 @@ class Message { this.snowflake = new SnowFlake(messagejson.id, this); continue; } + else if (thing === "member") { + this.member = new Member(messagejson.member, this.guild); + continue; + } + else if (thing === "embeds") { + this.embeds = []; + for (const thing in messagejson.embeds) { + console.log(thing, messagejson.embeds); + this.embeds[thing] = new Embed(messagejson.embeds[thing], this); + } + continue; + } this[thing] = messagejson[thing]; } - for (const thing in this.embeds) { - console.log(thing, this.embeds); - this.embeds[thing] = new Embed(this.embeds[thing], this); + this.author = new User(messagejson.author, this.localuser); + for (const thing in messagejson.mentions) { + this.mentions[thing] = new User(messagejson.mentions[thing], this.localuser); } - this.author = new User(this.author, this.localuser); - for (const thing in this.mentions) { - this.mentions[thing] = new User(this.mentions[thing], this.localuser); + if (!this.member && this.guild.id !== "@me") { + this.author.resolvemember(this.guild).then(_ => { + this.member = _; + }); } if (this.mentions.length || this.mention_roles.length) { //currently mention_roles isn't implemented on the spacebar servers console.log(this.mentions, this.mention_roles); @@ -227,6 +241,10 @@ class Message { username.textContent = author.username; author.bind(username); }); + reply.onclick = _ => { + console.log("this got clicked :3"); + this.channel.infinite.focus(this.message_reference.message_id); + }; div.appendChild(replyline); } build.classList.add("message"); @@ -300,6 +318,7 @@ class Message { messagedwrap.appendChild(attach); } if (this.embeds.length) { + console.log(this.embeds); const embeds = document.createElement("div"); embeds.classList.add("flexltr"); for (const thing of this.embeds) { diff --git a/.dist/register.js b/.dist/register.js index 5478eaa..176197c 100644 --- a/.dist/register.js +++ b/.dist/register.js @@ -14,13 +14,14 @@ async function registertry(e) { const password = elements[3].value; const dateofbirth = elements[5].value; const apiurl = new URL(JSON.parse(localStorage.getItem("instanceinfo")).api); - fetch(apiurl + "/auth/register", { + await fetch(apiurl + "/auth/register", { body: JSON.stringify({ date_of_birth: dateofbirth, email: email, username: username, password: password, consent: elements[6].checked, + captcha_key: elements[7]?.value }), headers: { "content-type": "application/json" @@ -28,9 +29,42 @@ async function registertry(e) { method: "POST" }).then(e => { e.json().then(e => { + if (e.captcha_sitekey) { + const capt = document.getElementById("h-captcha"); + if (!capt.children.length) { + const capty = document.createElement("div"); + capty.classList.add("h-captcha"); + capty.setAttribute("data-sitekey", e.captcha_sitekey); + const script = document.createElement("script"); + script.src = "https://js.hcaptcha.com/1/api.js"; + capt.append(script); + capt.append(capty); + } + else { + eval("hcaptcha.reset()"); + } + return; + } if (!e.token) { console.log(e); - document.getElementById("wrong").textContent = e.errors[Object.keys(e.errors)[0]]._errors[0].message; + if (e.errors.consent) { + error(elements[6], e.errors.consent._errors[0].message); + } + else if (e.errors.password) { + error(elements[3], "Password: " + e.errors.password._errors[0].message); + } + else if (e.errors.username) { + error(elements[2], "Username: " + e.errors.username._errors[0].message); + } + else if (e.errors.email) { + error(elements[1], "Email: " + e.errors.email._errors[0].message); + } + else if (e.errors.date_of_birth) { + error(elements[5], "Date of Birth: " + e.errors.date_of_birth._errors[0].message); + } + else { + document.getElementById("wrong").textContent = e.errors[Object.keys(e.errors)[0]]._errors[0].message; + } } else { localStorage.setItem("token", e.token); @@ -41,6 +75,21 @@ async function registertry(e) { //document.getElementById("wrong").textContent=h; // console.log(h); } +function error(e, message) { + const p = e.parentElement; + let element = p.getElementsByClassName("suberror")[0]; + if (!element) { + const div = document.createElement("div"); + div.classList.add("suberror", "suberrora"); + p.append(div); + element = div; + } + else { + element.classList.remove("suberror"); + setTimeout(_ => { element.classList.add("suberror"); }, 100); + } + element.textContent = message; +} let TOSa = document.getElementById("TOSa"); async function tosLogic() { const apiurl = new URL(JSON.parse(localStorage.getItem("instanceinfo")).api); diff --git a/.dist/snowflake.js b/.dist/snowflake.js index a01ce89..1e97301 100644 --- a/.dist/snowflake.js +++ b/.dist/snowflake.js @@ -15,8 +15,13 @@ class SnowFlake { } if (SnowFlake.SnowFlakes.get(obj.constructor).get(id)) { const snowflake = SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref(); - snowflake.obj = obj; - return snowflake; + if (snowflake) { + snowflake.obj = obj; + return snowflake; + } + else { + SnowFlake.SnowFlakes.get(obj.constructor).delete(id); + } } this.id = id; SnowFlake.SnowFlakes.get(obj.constructor).set(id, new WeakRef(this)); @@ -33,7 +38,13 @@ class SnowFlake { } const snowflake = SnowFlake.SnowFlakes.get(type).get(id); if (snowflake) { - return snowflake.deref(); + const obj = snowflake.deref(); + if (obj) { + return obj; + } + else { + SnowFlake.SnowFlakes.get(type).delete(id); + } } { const snowflake = new SnowFlake(id, undefined); @@ -42,6 +53,24 @@ class SnowFlake { return snowflake; } } + static hasSnowFlakeFromID(id, type) { + if (!SnowFlake.SnowFlakes.get(type)) { + return false; + } + const flake = SnowFlake.SnowFlakes.get(type).get(id); + if (flake) { + const flake2 = flake.deref()?.getObject(); + if (flake2) { + return true; + } + else { + return false; + } + } + else { + return false; + } + } getUnixTime() { return Number((BigInt(this.id) >> 22n) + 1420070400000n); } diff --git a/.dist/user.js b/.dist/user.js index 7a4c50e..1403982 100644 --- a/.dist/user.js +++ b/.dist/user.js @@ -14,6 +14,31 @@ class User { discriminator; pronouns; bot; + public_flags; + accent_color; + banner; + premium_since; + premium_type; + theme_colors; + badge_ids; + clone() { + return new User({ + username: this.username, + id: this.id + "#clone", + public_flags: this.public_flags, + discriminator: this.discriminator, + avatar: this.avatar, + accent_color: this.accent_color, + banner: this.banner, + bio: this.bio.rawString, + premium_since: this.premium_since, + premium_type: this.premium_type, + bot: this.bot, + theme_colors: this.theme_colors, + pronouns: this.pronouns, + badge_ids: this.badge_ids + }, this.owner); + } get id() { return this.snowflake.id; } @@ -29,13 +54,13 @@ class User { }); }); } - static checkuser(userjson, owner) { - if (User.userids[userjson.id]) { - return User.userids[userjson.id]; + static checkuser(user, owner) { + if (User.userids[user.id]) { + return User.userids[user.id]; } else { - const tempuser = new User(userjson, owner, true); - User.userids[userjson.id] = tempuser; + const tempuser = new User(user, owner, true); + User.userids[user.id] = tempuser; return tempuser; } } @@ -69,7 +94,7 @@ class User { } } async resolvemember(guild) { - await Member.resolve(this, guild); + return await Member.resolve(this, guild); } buildpfp() { const pfp = document.createElement('img'); diff --git a/package-lock.json b/package-lock.json index d803596..360cf57 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "0.1.0", "license": "GPL-3.0", "dependencies": { - "express": "latest" + "express": "^4.19.2" }, "devDependencies": { "@eslint/js": "^9.7.0", diff --git a/webpage/channel.ts b/webpage/channel.ts index 95dc395..7b3f6ab 100644 --- a/webpage/channel.ts +++ b/webpage/channel.ts @@ -10,6 +10,7 @@ import { Settings, RoleList } from "./settings.js"; import { Role } from "./role.js"; import {InfiniteScroller} from "./infiniteScroller.js"; import { SnowFlake } from "./snowflake.js"; +import { channeljson, messagejson, readyjson } from "./jsontypes.js"; declare global { interface NotificationOptions { @@ -93,21 +94,31 @@ class Channel{ 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.get(snowflake)){ + if(this.idToPrev.has(snowflake)){ return this.idToPrev.get(snowflake)?.id; }else{ await this.grabBefore(id); return this.idToPrev.get(snowflake)?.id; } }else{ - return this.idToNext.get(snowflake)?.id; + if(this.idToNext.has(snowflake)){ + return this.idToNext.get(snowflake)?.id; + }else if(this.lastmessage.id!==id){ + await this.grabAfter(id); + return this.idToNext.get(snowflake)?.id; + }else{ + console.log("at bottom") + } } }.bind(this), - function(this:Channel,id:string){ + async function(this:Channel,id:string){ let res:Function; const promise=new Promise(_=>{res=_;}) as Promise; - const snowflake=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake; - const html=this.messageids.get(snowflake).buildhtml(this.messageids.get(this.idToPrev.get(snowflake)),promise); + const snowflake=SnowFlake.getSnowFlakeFromID(id,Message); + if(!snowflake.getObject()){ + await this.grabArround(id); + } + const html=snowflake.getObject().buildhtml(this.messageids.get(this.idToPrev.get(snowflake)),promise); ids[id]=res; return html; }.bind(this), @@ -119,12 +130,11 @@ class Channel{ this.readbottom.bind(this) ); } - constructor(json,owner:Guild){ + constructor(json:channeljson|-1,owner:Guild){ if(json===-1){ return; } - this.editing; this.type=json.type; this.owner=owner; @@ -141,7 +151,7 @@ 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([thing.id,this.permission_overwrites.get(thing.id)]); + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),this.permission_overwrites.get(thing.id)]); } this.topic=json.topic; @@ -163,7 +173,7 @@ class Channel{ get info(){ return this.owner.info; } - readStateInfo(json){ + readStateInfo(json:readyjson["d"]["read_state"]["entries"][0]){ this.lastreadmessageid=SnowFlake.getSnowFlakeFromID(json.last_message_id,Message); this.mentions=json.mention_count; this.mentions??=0; @@ -540,7 +550,7 @@ class Channel{ return; } this.makereplybox(); - this.buildmessages(); + await this.buildmessages(); history.pushState(null, null,"/channels/"+this.guild_id+"/"+this.snowflake); document.getElementById("channelname").textContent="#"+this.name; console.log(this); @@ -572,69 +582,114 @@ class Channel{ } } } - delChannel(json){ + delChannel(json:channeljson){ const build=[]; for(const thing of this.children){ - if(thing.snowflake!==json.id){ + if(thing.id!==json.id){ build.push(thing) } } this.children=build; } - async grabBefore(id:string){ - if(this.allthewayup){ + async grabAfter(id:string){ + console.log(id,this.lastmessage.id) + if(id===this.lastmessage.id){ return; } - - await fetch(this.info.api.toString()+"/channels/"+this.snowflake+"/messages?before="+id+"&limit=100",{ + await fetch(this.info.api.toString()+"/channels/"+this.id+"/messages?limit=100&after="+id,{ headers:this.headers }).then((j)=>{return j.json()}).then(response=>{ - let next:Message; - if(response.length===0){ - this.allthewayup=true; - } - let previd=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake; + let previd:SnowFlake=SnowFlake.getSnowFlakeFromID(id,Message); for(const i in response){ let messager:Message; - if(!next){ + let willbreak=false + if(!SnowFlake.hasSnowFlakeFromID(response[i].id,Message)){ messager=new Message(response[i],this); }else{ - messager=next; + messager=SnowFlake.getSnowFlakeFromID(response[i].id,Message).getObject(); + willbreak=true; } - if(response[+i+1]!==undefined){ - next=new Message(response[+i+1],this); - }else{ - next=undefined; - console.log("ohno",+i+1); - } - if(this.messageids.get(messager.snowflake)===undefined){ - this.idToNext.set(messager.snowflake,previd); - this.idToPrev.set(previd,messager.snowflake); - previd=messager.snowflake; - this.messageids.set(messager.snowflake,messager); - }else{ - console.log("How???") + this.idToPrev.set(messager.snowflake,previd); + this.idToNext.set(previd,messager.snowflake); + previd=messager.snowflake; + this.messageids.set(messager.snowflake,messager); + if(willbreak){ + break; } } //out.buildmessages(); }) return; } + topid:SnowFlake; + async grabBefore(id:string){ + if(this.topid&&id===this.topid.id){ + return; + } + + await fetch(this.info.api.toString()+"/channels/"+this.snowflake+"/messages?before="+id+"&limit=100",{ + headers:this.headers + }).then((j)=>{return j.json()}).then((response:messagejson[])=>{ + if(response.length<100){ + this.allthewayup=true; + if(response.length===0){ + this.topid=SnowFlake.getSnowFlakeFromID(id,Message); + } + } + let previd=SnowFlake.getSnowFlakeFromID(id,Message) as SnowFlake; + for(const i in response){ + let messager:Message; + let willbreak=false; + if(!SnowFlake.hasSnowFlakeFromID(response[i].id,Message)){ + messager=new Message(response[i],this); + }else{ + console.log("flaky") + messager=SnowFlake.getSnowFlakeFromID(response[i].id,Message).getObject(); + willbreak=true; + } + + this.idToNext.set(messager.snowflake,previd); + this.idToPrev.set(previd,messager.snowflake); + previd=messager.snowflake; + this.messageids.set(messager.snowflake,messager); + + if(+i===response.length-1&&response.length<100){ + this.topid=previd; + } + if(willbreak){ + break; + } + } + }) + return; + } + /** + * Please dont use this, its not implemented. + **/ + async grabArround(id:string){//currently unused and no plans to use it yet + throw new Error("please don't call this, no one has implmented it :P") + } buildmessage(message:Message,next:Message){ const built=message.buildhtml(next); document.getElementById("messages").prepend(built); } - buildmessages(){ + async buildmessages(){ const messages=document.getElementById("channelw"); messages.innerHTML=""; let id:SnowFlake; - if(this.messageids.get(this.lastreadmessageid)){ + if(this.lastreadmessageid&&this.lastreadmessageid.getObject()){ id=this.lastreadmessageid; }else if(this.lastmessage.snowflake){ id=this.goBackIds(this.lastmessage.snowflake,50); console.log("shouldn't") } - messages.append(this.infinite.getDiv(id.id)); + console.log(this.lastreadmessageid,id.id); + messages.append(await this.infinite.getDiv(id.id)); + this.infinite.updatestuff(); + this.infinite.watchForChange().then(async _=>{ + await new Promise(resolve => setTimeout(resolve, 100)); + this.infinite.focus(id.id,false);//if someone could figure out how to make this work correctly without this, that's be great :P + }) } private goBackIds(id:SnowFlake,back:number):SnowFlake{ while(back!==0){ @@ -649,7 +704,7 @@ class Channel{ } return id; } - updateChannel(json){ + updateChannel(json:channeljson){ this.type=json.type; this.name=json.name; this.parent_id=new SnowFlake(json.parent_id,undefined); @@ -661,7 +716,7 @@ 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([thing.id,this.permission_overwrites.get(thing.id)]); + this.permission_overwritesar.push([SnowFlake.getSnowFlakeFromID(thing.id,Role),this.permission_overwrites.get(thing.id)]); } this.topic=json.topic; this.nsfw=json.nsfw; @@ -741,6 +796,7 @@ class Channel{ messageCreate(messagep:any):void{ if(!this.hasPermission("VIEW_CHANNEL")){return} const messagez=new Message(messagep.d,this); + this.lastmessage=messagez; console.log(this.lastmessageid,messagez.snowflake,":3"); this.idToNext.set(this.lastmessageid,messagez.snowflake); this.idToPrev.set(messagez.snowflake,this.lastmessageid); @@ -757,7 +813,9 @@ class Channel{ } } this.guild.unreads(); - this.infinite.addedBottom(); + if(this===this.localuser.channelfocus){ + this.infinite.addedBottom(); + } if(messagez.author===this.localuser.user){ return; } diff --git a/webpage/direct.ts b/webpage/direct.ts index 772894c..f0f4a68 100644 --- a/webpage/direct.ts +++ b/webpage/direct.ts @@ -5,12 +5,12 @@ import { Localuser } from "./localuser.js"; import {User} from "./user.js"; import { Member } from "./member.js"; import { SnowFlake } from "./snowflake.js"; +import { dirrectjson, memberjson } from "./jsontypes.js"; class Direct extends Guild{ - constructor(json,owner:Localuser){ + constructor(json:dirrectjson[],owner:Localuser){ super(-1,owner,null); this.message_notifications=0; - console.log(json); this.owner=owner; if(!this.localuser){ console.error("Owner was not included, please fix") @@ -44,13 +44,13 @@ class Direct extends Guild{ return Number(-result); }); } - giveMember(member){ + giveMember(_member:memberjson){ console.error("not a real guild, can't give member object") } - getRole(ID){ + getRole(ID:string){ return null; } - hasRole(r){ + hasRole(r:string){ return false; } isAdmin(){ @@ -64,7 +64,7 @@ class Direct extends Guild{ } class Group extends Channel{ user:User; - constructor(json,owner:Direct){ + constructor(json:dirrectjson,owner:Direct){ super(-1,owner); this.owner=owner; this.headers=this.guild.headers; @@ -166,13 +166,11 @@ class Group extends Channel{ const noti=document.createElement("div"); noti.classList.add("unread","notiunread","pinged"); noti.textContent=""+this.mentions; - console.log(this.mentions) div["noti"]=noti; div.append(noti) const buildpfp=this.user.buildpfp(); div["all"]=this; buildpfp.classList.add("mentioned"); - console.log(this); div.append(buildpfp) sentdms.append(div); div.onclick=_=>{ diff --git a/webpage/embed.ts b/webpage/embed.ts index b17de58..6432c3e 100644 --- a/webpage/embed.ts +++ b/webpage/embed.ts @@ -1,17 +1,18 @@ import {Fullscreen} from "./fullscreen.js"; import {Message} from "./message.js"; import {MarkDown} from "./markdown.js"; +import { embedjson } from "./jsontypes.js"; class Embed{ type:string; owner:Message; - json; - constructor(json, owner:Message){ + json:embedjson; + constructor(json:embedjson, owner:Message){ this.type=this.getType(json); this.owner=owner; this.json=json; } - getType(json){ + getType(json:embedjson){ return json.type||"rich"; } generateHTML(){ @@ -42,7 +43,6 @@ class Embed{ return this.guild.localuser; } generateRich(){ - console.log(this.json) const div=document.createElement("div"); if(this.json.color){ div.style.backgroundColor="#"+this.json.color.toString(16); diff --git a/webpage/file.ts b/webpage/file.ts index d71d384..8687e06 100644 --- a/webpage/file.ts +++ b/webpage/file.ts @@ -1,6 +1,7 @@ import { Message } from "./message.js"; import { Fullscreen } from "./fullscreen.js"; -type filejson= {id:string,filename:string,content_type:string,width:number,height:number,proxy_url:string|undefined,url:string,size:number}; +import { filejson } from "./jsontypes.js"; + class File{ owner:Message; id:string; @@ -12,7 +13,6 @@ class File{ url:string; size:number; constructor(fileJSON:filejson,owner:Message){ - console.log(fileJSON); this.owner=owner; this.id=fileJSON.id; this.filename=fileJSON.filename; diff --git a/webpage/guild.ts b/webpage/guild.ts index 81309fc..e936ad1 100644 --- a/webpage/guild.ts +++ b/webpage/guild.ts @@ -7,6 +7,7 @@ import {Member} from "./member.js"; import {Settings,RoleList} from "./settings.js"; import {Permissions} from "./permissions.js"; import { SnowFlake } from "./snowflake.js"; +import { channeljson, guildjson } from "./jsontypes.js"; class Guild{ owner:Localuser; headers:Localuser["headers"]; @@ -79,7 +80,7 @@ class Guild{ s1.options.push(new RoleList(permlist,this,this.updateRolePermissions.bind(this))); settings.show(); } - constructor(json,owner:Localuser,member){ + constructor(json:guildjson|-1,owner:Localuser,member){ if(json===-1){ return; } @@ -404,7 +405,7 @@ class Guild{ loadGuild(){ this.localuser.loadGuild(this.id); } - updateChannel(json){ + updateChannel(json:channeljson){ SnowFlake.getSnowFlakeFromID(json.id,Channel).getObject().updateChannel(json); this.headchannels=[]; for(const thing of this.channels){ @@ -417,7 +418,7 @@ class Guild{ } this.printServers(); } - createChannelpac(json){ + createChannelpac(json:channeljson){ const thischannel=new Channel(json,this); this.channelids[json.id]=thischannel; this.channels.push(thischannel); @@ -470,7 +471,7 @@ class Guild{ ]); channelselect.show(); } - delChannel(json){ + delChannel(json:channeljson){ const channel=this.channelids[json.id]; delete this.channelids[json.id]; diff --git a/webpage/infiniteScroller.ts b/webpage/infiniteScroller.ts index 6b3be1b..6d5d1b4 100644 --- a/webpage/infiniteScroller.ts +++ b/webpage/infiniteScroller.ts @@ -1,6 +1,6 @@ class InfiniteScroller{ readonly getIDFromOffset:(ID:string,offset:number)=>Promise; - readonly getHTMLFromID:(ID:string)=>HTMLElement; + readonly getHTMLFromID:(ID:string)=>Promise; readonly destroyFromID:(ID:string)=>Promise; readonly reachesBottom:()=>void; private readonly minDist=3000; @@ -15,7 +15,7 @@ class InfiniteScroller{ this.reachesBottom=reachesBottom; } interval:NodeJS.Timeout; - getDiv(initialId:string,bottom=true):HTMLDivElement{ + async getDiv(initialId:string,bottom=true):Promise{ const div=document.createElement("div"); div.classList.add("messagecontainer"); //div.classList.add("flexttb") @@ -30,10 +30,10 @@ class InfiniteScroller{ new ResizeObserver(this.watchForChange.bind(this)).observe(div); new ResizeObserver(this.watchForChange.bind(this)).observe(scroll); - this.firstElement(initialId); + await this.firstElement(initialId) this.updatestuff(); - this.watchForChange().then(_=>{ - this.scroll.scrollTop=this.scroll.scrollHeight; + await this.watchForChange().then(_=>{ + this.updatestuff(); }) return div; } @@ -47,8 +47,8 @@ class InfiniteScroller{ } //this.watchForChange(); } - firstElement(id:string){ - const html=this.getHTMLFromID(id); + async firstElement(id:string){ + const html=await this.getHTMLFromID(id); this.scroll.append(html); this.HTMLElements.push([html,id]); } @@ -61,33 +61,20 @@ class InfiniteScroller{ this.scroll.scrollTop=this.scroll.scrollHeight; } } - async watchForChange():Promise{ - if(this.currrunning){ - return; - }else{ - this.currrunning=true; - } + private async watchForTop():Promise{ let again=false; - if(!this.div){this.currrunning=false;return} - /* - if(this.scrollTop===0){ - this.scrollTop=10; - } - */ if(this.scrollTop===0){ this.scrollTop=1; this.scroll.scrollTop=1; } if(this.scrollTop{ + let again=false; const scrollBottom = this.scrollBottom; if(scrollBottom{ + if(this.currrunning){ + return; + }else{ + this.currrunning=true; + } + if(!this.div){this.currrunning=false;return} + await Promise.allSettled([this.watchForBottom(),this.watchForTop()]) this.currrunning=false; } + async focus(id:string,flash=true){ + let element:HTMLElement; + for(const thing of this.HTMLElements){ + if(thing[1]===id){ + element=thing[0]; + } + } + console.log(element,id,":3"); + if(element){ + element.scrollIntoView(); + if(flash){ + element.classList.remove("jumped"); + await new Promise(resolve => setTimeout(resolve, 100)); + element.classList.add("jumped"); + } + }else{ + for(const thing of this.HTMLElements){ + await this.destroyFromID(thing[1]); + } + this.HTMLElements=[]; + await this.firstElement(id); + this.updatestuff(); + await this.watchForChange(); + await new Promise(resolve => setTimeout(resolve, 100)); + await this.focus(id,true); + } + } async delete():Promise{ for(const thing of this.HTMLElements){ await this.destroyFromID(thing[1]); diff --git a/webpage/jsontypes.ts b/webpage/jsontypes.ts new file mode 100644 index 0000000..f98cb92 --- /dev/null +++ b/webpage/jsontypes.ts @@ -0,0 +1,320 @@ +type readyjson={ + op:number; + t:string; + s:number; + d:{ + v:number; + user:mainuserjson; + user_settings:{ + index: number, + afk_timeout: number, + allow_accessibility_detection: boolean, + animate_emoji: boolean, + animate_stickers: number, + contact_sync_enabled: boolean, + convert_emoticons: boolean, + custom_status: string, + default_guilds_restricted: boolean, + detect_platform_accounts: boolean, + developer_mode: boolean, + disable_games_tab: boolean, + enable_tts_command: boolean, + explicit_content_filter: 0, + friend_discovery_flags: 0, + friend_source_flags: { + all: boolean + },//might be missing things here + gateway_connected: boolean, + gif_auto_play: boolean, + guild_folders: [],//need an example of this not empty + guild_positions: [],//need an example of this not empty + inline_attachment_media: boolean, + inline_embed_media: boolean, + locale: string, + message_display_compact: boolean, + native_phone_integration_enabled: boolean, + render_embeds: boolean, + render_reactions: boolean, + restricted_guilds: [],//need an example of this not empty + show_current_game: boolean, + status: string, + stream_notifications_enabled: boolean, + theme: string, + timezone_offset: number, + view_nsfw_guilds: boolean + }; + guilds:guildjson[]; + relationships:any[]; + read_state:{ + entries:{ + id: string, + channel_id: string, + last_message_id: string, + last_pin_timestamp: string, + mention_count: number //in theory, the server doesn't actually send this as far as I'm aware + }[], + partial: boolean, + version: number + }; + user_guild_settings:{ + entries:{ + channel_overrides: unknown[],//will have to find example + message_notifications: number, + flags: number, + hide_muted_channels: boolean, + mobile_push: boolean, + mute_config: null, + mute_scheduled_events: boolean, + muted: boolean, + notify_highlights: number, + suppress_everyone: boolean, + suppress_roles: boolean, + version: number, + guild_id: string + }[], + partial: boolean, + version: number + }; + private_channels:dirrectjson[]; + session_id:string; + country_code:string; + users:userjson[]; + merged_members:memberjson[]; + sessions:{ + active: boolean, + activities: [],//will need to find example of this + client_info: { + version: number + }, + session_id: string, + status: string + }[]; + resume_gateway_url:string; + consents:{ + personalization: { + consented: boolean + } + }; + experiments: [],//not sure if I need to do this :P + guild_join_requests: [],//need to get examples + connected_accounts: [],//need to get examples + guild_experiments: [],//need to get examples + geo_ordered_rtc_regions: [],//need to get examples + api_code_version: number, + friend_suggestion_count: number, + analytics_token: string, + tutorial: boolean, + session_type: string, + auth_session_id_hash: string, + notification_settings: { + flags: number + } + } +} +type mainuserjson= userjson & { + flags: number, + mfa_enabled?: boolean, + email?: string, + phone?: string, + verified: boolean, + nsfw_allowed: boolean, + premium: boolean, + purchased_flags: number, + premium_usage_flags: number, + disabled: boolean +} +type userjson={ + username: string, + discriminator: string, + id: string, + public_flags: number, + avatar: string, + accent_color: string, + banner: string, + bio: string, + bot: boolean, + premium_since: string, + premium_type: number, + theme_colors: string, + pronouns: string, + badge_ids: string, +} +type memberjson= { + index?:number, + id: string, + user: userjson, + guild_id: string, + guild: { + id: string + }, + nick: string, + roles: string[], + joined_at: string, + premium_since: string, + deaf: boolean, + mute: boolean, + pending: boolean, + last_message_id: boolean +} +type guildjson={ + application_command_counts: {[key:string]:number}, + channels: channeljson[], + data_mode: string, + emojis: [], + guild_scheduled_events: [], + id: string, + large: boolean, + lazy: boolean, + member_count: number, + premium_subscription_count: number, + properties: { + name: string, + description: string, + icon: string, + splash: string, + banner: string, + features: string[], + preferred_locale: string, + owner_id: string, + application_id: string, + afk_channel_id: string, + afk_timeout: number, + system_channel_id: string, + verification_level: number, + explicit_content_filter: number, + default_message_notifications: number, + mfa_level: number, + vanity_url_code: number, + premium_tier: number, + premium_progress_bar_enabled: boolean, + system_channel_flags: number, + discovery_splash: string, + rules_channel_id: string, + public_updates_channel_id: string, + max_video_channel_users: number, + max_members: number, + nsfw_level: number, + hub_type: null, + home_header: null, + id: string, + latest_onboarding_question_id: string, + max_stage_video_channel_users: number, + nsfw: boolean, + safety_alerts_channel_id: string + }, + roles: rolesjson[], + stage_instances: [], + stickers: [], + threads: [], + version: string, + guild_hashes: {}, + joined_at: string +} +type channeljson={ + id: string, + created_at: string, + name: string, + icon: string, + type: number, + last_message_id: string, + guild_id: string, + parent_id: string, + last_pin_timestamp: string, + default_auto_archive_duration: number, + permission_overwrites: { + id:string, + allow:string, + deny:string, + }[], + video_quality_mode: null, + nsfw: boolean, + topic: string, + retention_policy_id: string, + flags: number, + default_thread_rate_limit_per_user: number, + position: number +} +type rolesjson={ + id: string, + guild_id: string, + color: number, + hoist: boolean, + managed: boolean, + mentionable: boolean, + name: string, + permissions: string, + position: number, + icon: string, + unicode_emoji: string, + flags: number +} +type dirrectjson={ + id: string, + flags: number, + last_message_id: string, + type: number, + recipients: userjson[], + is_spam: boolean +} +type messagejson={ + id: string, + channel_id: string, + guild_id: string, + author: userjson, + member?: memberjson, + content: string, + timestamp: string, + edited_timestamp: string, + tts: boolean, + mention_everyone: boolean, + mentions: [], //need examples to fix + mention_roles: [], //need examples to fix + attachments: filejson[], + embeds: embedjson[], + reactions: [], //ToDo + nonce: string, + pinned: boolean, + type: number +} +type filejson={ + id:string, + filename:string, + content_type:string, + width:number, + height:number, + proxy_url:string|undefined, + url:string, + size:number +}; +type embedjson={ + type:string|null, + color?:number, + author:{ + icon_url?:string, + name?:string, + url?:string, + title?:string, + }, + title?:string, + url?:string, + description?:string, + fields?:{ + name:string, + value:string, + inline:boolean, + }[], + footer?:{ + icon_url?:string, + text?:string, + thumbnail?:string, + }, + timestamp?:string, + thumbnail:{ + proxy_url:string, + url:string, + }, + provider:{ + name:string, + } +} +export {readyjson,dirrectjson,channeljson,guildjson,rolesjson,userjson,memberjson,mainuserjson,messagejson,filejson,embedjson}; diff --git a/webpage/localuser.ts b/webpage/localuser.ts index 5b83eed..f2e0ea1 100644 --- a/webpage/localuser.ts +++ b/webpage/localuser.ts @@ -7,6 +7,7 @@ import {Fullscreen} from "./fullscreen.js"; import {setTheme, Specialuser} from "./login.js"; import { SnowFlake } from "./snowflake.js"; import { Message } from "./message.js"; +import { channeljson, readyjson, userjson } from "./jsontypes.js"; const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]); @@ -21,7 +22,7 @@ class Localuser{ usersettings:Fullscreen; userConnections:Fullscreen; devPortal:Fullscreen; - ready; + ready:readyjson; guilds:Guild[]; guildids:Map; user:User; @@ -42,7 +43,7 @@ class Localuser{ this.info=this.serverurls; this.headers={"Content-type": "application/json; charset=UTF-8",Authorization:this.userinfo.token}; } - gottenReady(ready):void{ + gottenReady(ready:readyjson):void{ this.usersettings=null; this.initialized=true; this.ready=ready; @@ -90,8 +91,8 @@ class Localuser{ this.typing=[]; } outoffocus():void{ - document.getElementById("servers").textContent=""; - document.getElementById("channels").textContent=""; + document.getElementById("servers").innerHTML=""; + document.getElementById("channels").innerHTML=""; if(this.channelfocus){ this.channelfocus.infinite.delete(); } @@ -104,7 +105,7 @@ class Localuser{ this.outoffocus(); this.guilds=[]; this.guildids=new Map(); - this.ws.close(4000) + this.ws.close(4001) } async initwebsocket():Promise{ let returny=null @@ -119,7 +120,7 @@ class Localuser{ "capabilities": 16381, "properties": { "browser": "Jank Client", - "client_build_number": 0, + "client_build_number": 0,//might update this eventually lol "release_channel": "Custom", "browser_user_agent": navigator.userAgent }, @@ -154,7 +155,7 @@ class Localuser{ SnowFlake.getSnowFlakeFromID(temp.d.id,Message).getObject().deleteEvent(); break; case "READY": - this.gottenReady(temp); + this.gottenReady(temp as readyjson); this.genusersettings(); returny(); break; @@ -226,7 +227,6 @@ class Localuser{ this.unload(); document.getElementById("loading").classList.remove("doneloading"); document.getElementById("loading").classList.add("loading"); - if (((event.code>1000 && event.code<1016) || wsCodesRetry.has(event.code))) { if (this.connectionSucceed!==0 && Date.now()>this.connectionSucceed+20000) this.errorBackoff=0; else this.errorBackoff++; @@ -258,24 +258,24 @@ class Localuser{ } return undefined; } - updateChannel(json):void{ + updateChannel(json:channeljson):void{ SnowFlake.getSnowFlakeFromID(json.guild_id,Guild).getObject().updateChannel(json); if(json.guild_id===this.lookingguild.id){ this.loadGuild(json.guild_id); } } - createChannel(json):void{ + createChannel(json:channeljson):void{ json.guild_id??="@me"; SnowFlake.getSnowFlakeFromID(json.guild_id,Guild).getObject().createChannelpac(json); if(json.guild_id===this.lookingguild.id){ this.loadGuild(json.guild_id); } } - delChannel(json):void{ + delChannel(json:channeljson):void{ json.guild_id??="@me"; this.guildids.get(json.guild_id).delChannel(json); - if(json.guild_id===this.lookingguild.snowflake){ + if(json.guild_id===this.lookingguild.id){ this.loadGuild(json.guild_id); } } @@ -302,7 +302,6 @@ class Localuser{ if(!guild){ guild=this.guildids.get("@me"); } - console.log(this.guildids,id,guild); if(this.lookingguild){ this.lookingguild.html.classList.remove("serveropen"); } @@ -363,7 +362,6 @@ class Localuser{ div.classList.add("home","servericon") serverlist.appendChild(div) div.onclick=_=>{ - console.log("clicked :3") this.createGuild(); } @@ -376,7 +374,6 @@ class Localuser{ }); } - console.log("test"); this.unreads(); } createGuild(){ @@ -390,7 +387,6 @@ class Localuser{ "Invite Link/Code", "", function(){ - console.log(this) inviteurl=this.value; } ], @@ -410,7 +406,6 @@ class Localuser{ method:"POST", headers:this.headers, }).then(r=>r.json()).then(_=>{ - console.log(_); if(_.message){ error.textContent=_.message; } @@ -492,11 +487,9 @@ class Localuser{ this.unreads(); } unreads():void{ - console.log(this.guildhtml) for(const thing of this.guilds){ if(thing.id==="@me"){continue;} const html=this.guildhtml.get(thing.id); - console.log(html); thing.unreads(html); } } @@ -532,7 +525,6 @@ class Localuser{ updatepfp(file:Blob):void{ var reader = new FileReader(); reader.readAsDataURL(file); - console.log(this.headers); reader.onload = ()=>{ fetch(this.info.api.toString()+"/users/@me",{ method:"PATCH", @@ -541,7 +533,6 @@ class Localuser{ avatar:reader.result, }) }); - console.log(reader.result); }; } @@ -585,7 +576,6 @@ class Localuser{ }else{ build+=" is typing"; } - console.log(typingtext.classList); if(showing){ typingtext.classList.remove("hidden"); document.getElementById("typingtext").textContent=build; @@ -598,7 +588,7 @@ class Localuser{ let file=null; let newprouns=null; let newbio=null; - let hypouser=new User(this.user,this,true); + let hypouser=this.user.clone(); function regen(){ hypotheticalProfile.textContent=""; const hypoprofile=hypouser.buildprofile(-1,-1); @@ -652,9 +642,9 @@ class Localuser{ ["vdiv", ["html",hypotheticalProfile] ] - ],_=>{},function(){ + ],_=>{},function(this:Localuser){ console.log(this); - hypouser=new User(this.user,this); + hypouser=this.user.clone(); regen(); file=null; newprouns=null; diff --git a/webpage/login.ts b/webpage/login.ts index 18513ec..1a4881c 100644 --- a/webpage/login.ts +++ b/webpage/login.ts @@ -173,15 +173,21 @@ async function login(username:string, password:string, captcha:string){ console.log(response); if(response.captcha_sitekey){ - const capt=document.getElementById("h-captcha"); - const capty=document.createElement("div"); - capty.classList.add("h-captcha"); - capty.setAttribute("data-sitekey", response.captcha_sitekey); - const script=document.createElement("script"); - script.src="https://js.hcaptcha.com/1/api.js"; - capt.append(script); - capt.append(capty); + const capt=document.getElementById("h-captcha"); + if(!capt.children.length){ + const capty=document.createElement("div"); + capty.classList.add("h-captcha"); + + capty.setAttribute("data-sitekey", response.captcha_sitekey); + const script=document.createElement("script"); + script.src="https://js.hcaptcha.com/1/api.js"; + capt.append(script); + capt.append(capty); + }else{ + eval("hcaptcha.reset()"); + } + return; }else{ adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:username,token:response.token}); window.location.href = '/channels/@me'; diff --git a/webpage/member.ts b/webpage/member.ts index 6469695..5f43bcd 100644 --- a/webpage/member.ts +++ b/webpage/member.ts @@ -3,6 +3,7 @@ import {Role} from "./role.js"; import {Guild} from "./guild.js"; import { Contextmenu } from "./contextmenu.js"; import { SnowFlake } from "./snowflake.js"; +import { memberjson, userjson } from "./jsontypes.js"; class Member{ static already={}; @@ -10,6 +11,7 @@ class Member{ user:User; roles:Role[]; error:boolean; + id:string; static contextmenu:Contextmenu=new Contextmenu("User Menu"); static setUpContextMenu(){ this.contextmenu.addbutton("Copy user id",function(){ @@ -23,17 +25,18 @@ class Member{ }); }); } - constructor(memberjson,owner:Guild,error=false){ + constructor(memberjson:memberjson|User|{guild_member:memberjson,user:userjson},owner:Guild,error=false){ this.error=error; this.owner=owner; let membery=memberjson; this.roles=[]; if(!error){ - if(memberjson.guild_member){ + if(memberjson["guild_member"]){ + memberjson=memberjson as {guild_member:memberjson,user:userjson}; membery=memberjson.guild_member; - this.user=memberjson.user; } } + membery=membery as User|memberjson; for(const thing of Object.keys(membery)){ if(thing==="guild"){continue} if(thing==="owner"){continue} @@ -49,7 +52,11 @@ class Member{ if(error){ this.user=memberjson as User; }else{ - this.user=new User(this.user,owner.localuser); + if(SnowFlake.getSnowFlakeFromID(this?.id,User)){ + this.user=SnowFlake.getSnowFlakeFromID(this.id,User).getObject(); + return; + } + this.user=new User((membery as memberjson).user,owner.localuser); } } get guild(){ @@ -61,7 +68,7 @@ class Member{ get info(){ return this.owner.info; } - static async resolve(unkown:User|object|string,guild:Guild):Promise{ + static async resolve(unkown:User|memberjson|string,guild:Guild):Promise{ if(!(guild instanceof Guild)){ console.error(guild) } @@ -73,7 +80,7 @@ class Member{ }else if(typeof unkown===typeof ""){ id=new SnowFlake(unkown as string,undefined); }else{ - return new Member(unkown,guild); + return new Member(unkown as User|memberjson,guild); } if(guild.id==="@me"){return null} if(!Member.already[guild.id]){ diff --git a/webpage/message.ts b/webpage/message.ts index 475d91f..988d415 100644 --- a/webpage/message.ts +++ b/webpage/message.ts @@ -8,6 +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"; class Message{ static contextmenu=new Contextmenu("message menu"); @@ -26,6 +27,7 @@ class Message{ static del:Promise; static resolve:Function; div:HTMLDivElement; + member:Member; get id(){ return this.snowflake.id; } @@ -47,7 +49,7 @@ class Message{ this.channel.setReplying(this); }); Message.contextmenu.addbutton("Copy message id",function(){ - navigator.clipboard.writeText(this.id.id); + navigator.clipboard.writeText(this.id); }); Message.contextmenu.addbutton("Edit",function(){ this.channel.editing=this; @@ -59,13 +61,13 @@ class Message{ this.delete(); },null,_=>{return _.canDelete()}) } - constructor(messagejson,owner:Channel){ + constructor(messagejson:messagejson,owner:Channel){ this.owner=owner; this.headers=this.owner.headers; this.giveData(messagejson); } - giveData(messagejson){ + giveData(messagejson:messagejson){ for(const thing of Object.keys(messagejson)){ if(thing==="attachments"){ this.attachments=[]; @@ -79,16 +81,29 @@ class Message{ }else if(thing ==="id"){ this.snowflake=new SnowFlake(messagejson.id,this); continue; + }else if(thing==="member"){ + this.member=new Member(messagejson.member,this.guild); + continue; + }else if(thing ==="embeds"){ + this.embeds=[]; + for(const thing in messagejson.embeds){ + console.log(thing,messagejson.embeds) + this.embeds[thing]=new Embed(messagejson.embeds[thing],this); + } + continue; } this[thing]=messagejson[thing]; } - for(const thing in this.embeds){ - console.log(thing,this.embeds) - this.embeds[thing]=new Embed(this.embeds[thing],this); + + + this.author=new User(messagejson.author,this.localuser); + for(const thing in messagejson.mentions){ + this.mentions[thing]=new User(messagejson.mentions[thing],this.localuser); } - this.author=new User(this.author,this.localuser); - for(const thing in this.mentions){ - this.mentions[thing]=new User(this.mentions[thing],this.localuser); + if(!this.member&&this.guild.id!=="@me"){ + this.author.resolvemember(this.guild).then(_=>{ + this.member=_; + }) } if(this.mentions.length||this.mention_roles.length){//currently mention_roles isn't implemented on the spacebar servers console.log(this.mentions,this.mention_roles) @@ -231,6 +246,10 @@ class Message{ username.textContent=author.username; author.bind(username); }); + reply.onclick=_=>{ + console.log("this got clicked :3") + this.channel.infinite.focus(this.message_reference.message_id); + } div.appendChild(replyline); } build.classList.add("message"); @@ -304,6 +323,7 @@ class Message{ messagedwrap.appendChild(attach) } if(this.embeds.length){ + console.log(this.embeds); const embeds = document.createElement("div") embeds.classList.add("flexltr"); for(const thing of this.embeds){ diff --git a/webpage/register.html b/webpage/register.html index e75e400..528e0ad 100644 --- a/webpage/register.html +++ b/webpage/register.html @@ -8,30 +8,45 @@

Create an account


-
-

-

+
+
+

+ +
+
+
+ +
-
-

+
+
+ +
+
+
+ +
-
-

+
+
+ +
-
-

+
+
+ +
-
-

+
+ I agree to the Terms of Service: + +
-
-

+

+
- - I agree to the Terms of Service: -
-


- +
+
Already have an account?
diff --git a/webpage/register.ts b/webpage/register.ts index 4a72f0d..e1f9e0c 100644 --- a/webpage/register.ts +++ b/webpage/register.ts @@ -16,13 +16,14 @@ async function registertry(e){ const dateofbirth=elements[5].value; const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api) - fetch(apiurl+"/auth/register",{ + await fetch(apiurl+"/auth/register",{ body:JSON.stringify({ date_of_birth:dateofbirth, email:email, username:username, password:password, consent:elements[6].checked, + captcha_key:elements[7]?.value }), headers:{ "content-type": "application/json" @@ -30,9 +31,37 @@ async function registertry(e){ method:"POST" }).then(e=>{ e.json().then(e=>{ + if(e.captcha_sitekey){ + const capt=document.getElementById("h-captcha"); + if(!capt.children.length){ + const capty=document.createElement("div"); + capty.classList.add("h-captcha"); + + capty.setAttribute("data-sitekey", e.captcha_sitekey); + const script=document.createElement("script"); + script.src="https://js.hcaptcha.com/1/api.js"; + capt.append(script); + capt.append(capty); + }else{ + eval("hcaptcha.reset()"); + } + return; + } if(!e.token){ console.log(e); - document.getElementById("wrong").textContent=e.errors[Object.keys(e.errors)[0]]._errors[0].message; + if(e.errors.consent){ + error(elements[6],e.errors.consent._errors[0].message); + }else if(e.errors.password){ + error(elements[3],"Password: "+e.errors.password._errors[0].message); + }else if(e.errors.username){ + error(elements[2],"Username: "+e.errors.username._errors[0].message); + }else if(e.errors.email){ + error(elements[1],"Email: "+e.errors.email._errors[0].message); + }else if(e.errors.date_of_birth){ + error(elements[5],"Date of Birth: "+e.errors.date_of_birth._errors[0].message); + }else{ + document.getElementById("wrong").textContent=e.errors[Object.keys(e.errors)[0]]._errors[0].message; + } }else{ localStorage.setItem("token",e.token); window.location.href = '/channels/@me'; @@ -42,6 +71,20 @@ async function registertry(e){ //document.getElementById("wrong").textContent=h; // console.log(h); } +function error(e:HTMLFormElement,message:string){ + const p=e.parentElement; + let element=p.getElementsByClassName("suberror")[0] as HTMLElement; + if(!element){ + const div=document.createElement("div"); + div.classList.add("suberror","suberrora"); + p.append(div); + element=div; + }else{ + element.classList.remove("suberror"); + setTimeout(_=>{element.classList.add("suberror")},100); + } + element.textContent=message; +} let TOSa=document.getElementById("TOSa"); async function tosLogic(){ const apiurl=new URL(JSON.parse(localStorage.getItem("instanceinfo")).api) diff --git a/webpage/role.ts b/webpage/role.ts index 6eeed36..808c60a 100644 --- a/webpage/role.ts +++ b/webpage/role.ts @@ -3,6 +3,7 @@ import {Permissions} from "./permissions.js"; import {Localuser} from "./localuser.js"; import {Guild} from "./guild.js"; import { SnowFlake } from "./snowflake.js"; +import { rolesjson } from "./jsontypes.js"; class Role{ permissions:Permissions; owner:Guild; @@ -18,7 +19,7 @@ class Role{ get id(){ return this.snowflake.id; } - constructor(json, owner:Guild){ + constructor(json:rolesjson, owner:Guild){ this.headers=owner.headers; this.info=owner.info; for(const thing of Object.keys(json)){ diff --git a/webpage/snowflake.ts b/webpage/snowflake.ts index 2a9193d..76c755f 100644 --- a/webpage/snowflake.ts +++ b/webpage/snowflake.ts @@ -1,7 +1,7 @@ -class SnowFlake{ +class SnowFlake{ public readonly id:string; private static readonly SnowFlakes:Map>>>=new Map(); - private static readonly FinalizationRegistry=new FinalizationRegistry((a:[string,any])=>{ + private static readonly FinalizationRegistry=new FinalizationRegistry((a:[string,WeakKey])=>{ SnowFlake.SnowFlakes.get(a[1]).delete(a[0]); }); private obj:x; @@ -15,8 +15,12 @@ class SnowFlake{ } if(SnowFlake.SnowFlakes.get(obj.constructor).get(id)){ const snowflake=SnowFlake.SnowFlakes.get(obj.constructor).get(id).deref(); - snowflake.obj=obj; - return snowflake; + if(snowflake){ + snowflake.obj=obj; + return snowflake; + }else{ + SnowFlake.SnowFlakes.get(obj.constructor).delete(id); + } } this.id=id; SnowFlake.SnowFlakes.get(obj.constructor).set(id,new WeakRef(this)); @@ -33,7 +37,12 @@ class SnowFlake{ } const snowflake=SnowFlake.SnowFlakes.get(type).get(id); if(snowflake){ - return snowflake.deref(); + const obj=snowflake.deref(); + if(obj){ + return obj; + }else{ + SnowFlake.SnowFlakes.get(type).delete(id); + } } { const snowflake=new SnowFlake(id,undefined); @@ -44,6 +53,22 @@ class SnowFlake{ return snowflake; } } + static hasSnowFlakeFromID(id:string,type:any){ + if(!SnowFlake.SnowFlakes.get(type)){ + return false; + } + const flake=SnowFlake.SnowFlakes.get(type).get(id); + if(flake){ + const flake2=flake.deref()?.getObject(); + if(flake2){ + return true; + }else{ + return false; + } + }else{ + return false; + } + } getUnixTime():number{ return Number((BigInt(this.id)>>22n)+1420070400000n); } diff --git a/webpage/style.css b/webpage/style.css index 8abf1b0..5a44d5d 100644 --- a/webpage/style.css +++ b/webpage/style.css @@ -74,6 +74,22 @@ th { .messagediv:hover { background-color: var(--message-bg-hover); } + +.jumped{ + animation-duration: .5s; + animation-name: jumped; +} +@keyframes jumped{ + 0% { + background-color: transparent; + } + 50% { + background-color: var(--message-jump); + } + 100% { + background-color: transparent; + } +} .messagediv{ overflow: hidden; max-width:100%; @@ -83,7 +99,7 @@ th { flex-wrap: nowrap; flex-direction: column; max-height: 20in; - padding: .02in .2in 0 .1in; + padding: .02in .1in 0 .1in; flex-shrink: 0; width: 100%; box-sizing: border-box; @@ -546,7 +562,7 @@ hr { .replypfp { border-radius: 50%; width: .2in; - height: 2.in; + height: .2in; padding: .05in; user-select: none; cursor: pointer; @@ -567,6 +583,7 @@ hr { /* display: inline-block !important; */ width: 25vw; grid-column: 2; + cursor: pointer; } .replytext pre { /* padding: 0 .05in; */ @@ -837,6 +854,10 @@ select{ border-style: solid; border-color: var(--login-border); text-align: center; + height: 90%; + display: flex; + flex-direction: column; + justify-content: center; } #logindiv input { @@ -1398,7 +1419,7 @@ span { .messagediv .flexttb{ display:flex; overflow: hidden; - flex-wrap: wrap; + flex-wrap: nowrap; width: 100%; flex-direction: column; max-height:100in; @@ -1441,3 +1462,56 @@ span { content:'You can\'t chat here'; color:color-mix(in hsl, var(--primary-bg),var(--primary-text)); } +.scroller{ + padding-bottom: .2in; +} +.suberror{ + animation: goout 6s forwards; +} +.suberrora{ + background:var(--channel-hover); + border-radius:.1in; + position:absolute; + border:solid var(--primary-text) .02in; + color:color-mix(in hsl,var(--yellow),var(--red)); + font-weight:bold; + opacity:0; + cursor:default; + /* height: .4in; */ + display: flex; + flex-direction: column; + align-items: flex-start; + justify-content: space-evenly; + padding: .075in; + box-sizing: border-box; +} +@keyframes goout { + 0%{ + opacity:0; + } + 5%{ + opacity:1; + } + 90%{ + opacity:1; + } + 100%{ + opacity:0; + } +} + +#register{ + display:flex; + flex-direction: column; + align-items: center; + flex-grow: 1; + justify-content: space-between; +} +.dontgrow{ + flex-grow:0; + width: 1.6in; + text-align: center; +} +form div{ + width:100%; +} \ No newline at end of file diff --git a/webpage/themes.css b/webpage/themes.css index 46cc594..d4e5e25 100644 --- a/webpage/themes.css +++ b/webpage/themes.css @@ -3,6 +3,7 @@ /* Default value */ --red: red; --green: green; + --yellow:yellow; } .Dark-theme { /* thanks to TomatoCake for the updated CSS vars and such*/ color-scheme: dark; @@ -51,6 +52,7 @@ --embed: #1a1823; --link: #8888ff; --discovery-bg: #37373b; + --message-jump:#7678b0; } .WHITE-theme { color-scheme: light; @@ -101,6 +103,7 @@ --embed: #f2f3f5; --link: #3333ee; --discovery-bg: #c6c6d8; + --message-jump:#52558a; } .Light-theme { color-scheme: light; @@ -159,4 +162,5 @@ --embed: #cdccd1; --link: #5566cc; --discovery-bg: #c6c6d8; + --message-jump:#52558a; } diff --git a/webpage/user.ts b/webpage/user.ts index 9db689a..4591e43 100644 --- a/webpage/user.ts +++ b/webpage/user.ts @@ -5,6 +5,7 @@ import {Contextmenu} from "./contextmenu.js"; import {Localuser} from "./localuser.js"; import {Guild} from "./guild.js"; import { SnowFlake } from "./snowflake.js"; +import { userjson } from "./jsontypes.js"; class User{ static userids={}; @@ -17,6 +18,31 @@ class User{ discriminator:string; pronouns:string; bot:boolean; + public_flags: number; + accent_color: string; + banner: string; + premium_since: string; + premium_type: number; + theme_colors: string; + badge_ids: string; + clone(){ + return new User({ + username:this.username, + id:this.id+"#clone", + public_flags:this.public_flags, + discriminator:this.discriminator, + avatar:this.avatar, + accent_color:this.accent_color, + banner:this.banner, + bio:this.bio.rawString, + premium_since:this.premium_since, + premium_type:this.premium_type, + bot:this.bot, + theme_colors:this.theme_colors, + pronouns:this.pronouns, + badge_ids:this.badge_ids + },this.owner) + } get id(){ return this.snowflake.id; } @@ -33,12 +59,12 @@ class User{ }); }) } - static checkuser(userjson,owner:Localuser){ - if(User.userids[userjson.id]){ - return User.userids[userjson.id]; + static checkuser(user:User|userjson,owner:Localuser):User{ + if(User.userids[user.id]){ + return User.userids[user.id]; }else{ - const tempuser=new User(userjson,owner,true) - User.userids[userjson.id]=tempuser; + const tempuser=new User(user as userjson,owner,true) + User.userids[user.id]=tempuser; return tempuser; } } @@ -48,7 +74,7 @@ class User{ get localuser(){ return this.owner; } - constructor(userjson,owner:Localuser,dontclone=false){ + constructor(userjson:userjson,owner:Localuser,dontclone=false){ this.owner=owner; if(!owner){console.error("missing localuser")} if(dontclone){ @@ -69,7 +95,7 @@ class User{ } } async resolvemember(guild:Guild){ - await Member.resolve(this,guild); + return await Member.resolve(this,guild); } buildpfp(){ const pfp=document.createElement('img'); @@ -78,7 +104,7 @@ class User{ pfp.classList.add("userid:"+this.id); return pfp; } - userupdate(json){ + userupdate(json:userjson){ if(json.avatar!==this.avatar){ console.log this.changepfp(json.avatar);