diff --git a/.dist/channel.js b/.dist/channel.js
index 668463a..376dadd 100644
--- a/.dist/channel.js
+++ b/.dist/channel.js
@@ -58,7 +58,7 @@ class Channel {
this.deleteChannel();
}, null, _ => { console.log(_); return _.isAdmin(); });
this.contextmenu.addbutton("Edit channel", function () {
- this.editChannel(this);
+ this.editChannel();
}, null, _ => { return _.isAdmin(); });
this.contextmenu.addbutton("Make invite", function () {
this.createInvite();
@@ -189,7 +189,9 @@ class Channel {
}.bind(this), async function (id) {
const message = SnowFlake.getSnowFlakeFromID(id, Message).getObject();
try {
- message.deleteDiv();
+ if (message) {
+ message.deleteDiv();
+ }
}
catch (e) {
console.error(e);
@@ -344,9 +346,9 @@ class Channel {
addchannel.textContent = "+";
addchannel.classList.add("addchannel");
caps.appendChild(addchannel);
- addchannel.onclick = function () {
+ addchannel.onclick = _ => {
this.guild.createchannels(this.createChannel.bind(this));
- }.bind(this);
+ };
this.coatDropDiv(decdiv, childrendiv);
}
div.appendChild(caps);
@@ -550,8 +552,7 @@ class Channel {
});
console.log(full);
full.hide();
- }]
- ]
+ }]]
]);
full.show();
console.log(full);
@@ -798,6 +799,9 @@ class Channel {
return;
this.infinitefocus = true;
const messages = document.getElementById("channelw");
+ for (const thing of messages.getElementsByClassName("messagecontainer")) {
+ thing.remove();
+ }
const loading = document.getElementById("loadingdiv");
const removetitle = document.getElementById("removetitle");
//messages.innerHTML="";
diff --git a/.dist/dialog.js b/.dist/dialog.js
index fc2f3ab..11ee249 100644
--- a/.dist/dialog.js
+++ b/.dist/dialog.js
@@ -238,7 +238,7 @@ class Dialog {
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
- this.background.onclick = function () { this.hide(); }.bind(this);
+ this.background.onclick = _ => { this.hide(); };
}
hide() {
document.body.removeChild(this.background);
diff --git a/.dist/direct.js b/.dist/direct.js
index c17c507..56bd414 100644
--- a/.dist/direct.js
+++ b/.dist/direct.js
@@ -105,6 +105,7 @@ class Group extends Channel {
this.lastmessageid ??= new SnowFlake("0", undefined);
this.mentions = 0;
this.setUpInfiniteScroller();
+ this.position = Math.max(this.lastmessageid.getUnixTime(), this.snowflake.getUnixTime());
}
createguildHTML() {
const div = document.createElement("div");
diff --git a/.dist/guild.js b/.dist/guild.js
index 5e97801..9a83b45 100644
--- a/.dist/guild.js
+++ b/.dist/guild.js
@@ -77,6 +77,9 @@ class Guild {
if (json === -1) {
return;
}
+ if (json.stickers.length) {
+ console.log(json.stickers, ":3");
+ }
this.emojis = json.emojis;
this.owner = owner;
this.headers = this.owner.headers;
@@ -125,15 +128,12 @@ class Guild {
noti
],
["button", "", "submit", _ => {
- fetch(this.info.api + "/users/@me/guilds/settings", {
+ //
+ fetch(this.info.api + `/users/@me/guilds/${this.id}/settings/`, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
- "guilds": {
- [this.id]: {
- "message_notifications": noti
- }
- }
+ "message_notifications": noti
})
});
this.message_notifications = noti;
@@ -446,8 +446,7 @@ class Guild {
console.log(name, category);
func(name, category);
channelselect.hide();
- }.bind(this)]
- ]);
+ }.bind(this)]]);
channelselect.show();
}
createcategory() {
@@ -458,12 +457,11 @@ class Guild {
console.log(this);
name = this.value;
}],
- ["button", "", "submit", function () {
+ ["button", "", "submit", () => {
console.log(name, category);
this.createChannel(name, category);
channelselect.hide();
- }]
- ]);
+ }]]);
channelselect.show();
}
delChannel(json) {
diff --git a/.dist/index.js b/.dist/index.js
index 2c36ff7..d28ad76 100644
--- a/.dist/index.js
+++ b/.dist/index.js
@@ -85,6 +85,7 @@ function showAccountSwitcher() {
}
let thisuser;
try {
+ console.log(users.users, users.currentuser);
thisuser = new Localuser(users.users[users.currentuser]);
thisuser.initwebsocket().then(_ => {
thisuser.loaduser();
@@ -94,7 +95,8 @@ try {
console.log("done loading");
});
}
-catch {
+catch (e) {
+ console.error(e);
document.getElementById("load-desc").textContent = "Account unable to start";
thisuser = new Localuser(-1);
}
@@ -151,11 +153,13 @@ typebox.addEventListener("keydown", event => {
});
console.log(typebox);
typebox.onclick = console.log;
-function getguildinfo() {
- const path = window.location.pathname.split("/");
- const channel = path[3];
- this.ws.send(JSON.stringify({ op: 14, d: { guild_id: path[2], channels: { [channel]: [[0, 99]] } } }));
+/*
+function getguildinfo(){
+ const path=window.location.pathname.split("/");
+ const channel=path[3];
+ this.ws.send(JSON.stringify({op: 14, d: {guild_id: path[2], channels: {[channel]: [[0, 99]]}}}));
}
+*/
const images = [];
const imageshtml = [];
import { File } from "./file.js";
diff --git a/.dist/infiniteScroller.js b/.dist/infiniteScroller.js
index 9407073..ff39331 100644
--- a/.dist/infiniteScroller.js
+++ b/.dist/infiniteScroller.js
@@ -164,7 +164,7 @@ class InfiniteScroller {
return;
}
const out = await Promise.allSettled([this.watchForTop(), this.watchForBottom()]);
- const changed = (out[0] || out[1]);
+ const changed = (out[0].value || out[1].value);
if (null === this.timeout && changed) {
this.timeout = setTimeout(this.updatestuff.bind(this), 300);
}
@@ -217,7 +217,7 @@ class InfiniteScroller {
await this.destroyFromID(thing[1]);
}
this.HTMLElements = [];
- clearInterval(this.timeout);
+ clearTimeout(this.timeout);
if (this.div) {
this.div.remove();
}
diff --git a/.dist/localuser.js b/.dist/localuser.js
index 97ec9f7..9d774a7 100644
--- a/.dist/localuser.js
+++ b/.dist/localuser.js
@@ -3,7 +3,7 @@ import { Direct } from "./direct.js";
import { Voice } from "./audio.js";
import { User } from "./user.js";
import { Dialog } from "./dialog.js";
-import { getBulkInfo, setTheme } from "./login.js";
+import { getapiurls, getBulkInfo, setTheme } from "./login.js";
import { SnowFlake } from "./snowflake.js";
import { Message } from "./message.js";
import { Member } from "./member.js";
@@ -11,6 +11,7 @@ import { Settings } from "./settings.js";
import { MarkDown } from "./markdown.js";
const wsCodesRetry = new Set([4000, 4003, 4005, 4007, 4008, 4009]);
class Localuser {
+ badges = new Map();
lastSequence = null;
token;
userinfo;
@@ -205,7 +206,7 @@ class Localuser {
res();
}
});
- this.ws.addEventListener("close", event => {
+ this.ws.addEventListener("close", async (event) => {
this.ws = undefined;
console.log("WebSocket closed with code " + event.code);
this.unload();
@@ -221,6 +222,41 @@ class Localuser {
this.errorBackoff++;
this.connectionSucceed = 0;
document.getElementById("load-desc").innerHTML = "Unable to connect to the Spacebar server, retrying in " + Math.round(0.2 + (this.errorBackoff * 2.8)) + " seconds...";
+ switch (this.errorBackoff) { //try to recover from bad domain
+ case 3:
+ const newurls = await getapiurls(this.info.wellknown);
+ if (newurls) {
+ this.info = newurls;
+ this.serverurls = newurls;
+ this.userinfo.json.serverurls = this.info;
+ this.userinfo.updateLocal();
+ break;
+ }
+ case 4:
+ {
+ const newurls = await getapiurls(new URL(this.info.wellknown).origin);
+ if (newurls) {
+ this.info = newurls;
+ this.serverurls = newurls;
+ this.userinfo.json.serverurls = this.info;
+ this.userinfo.updateLocal();
+ break;
+ }
+ }
+ case 5:
+ {
+ const breakappart = new URL(this.info.wellknown).origin.split(".");
+ const url = "https://" + breakappart[breakappart.length - 2] + "." + breakappart[breakappart.length - 1];
+ const newurls = await getapiurls(url);
+ if (newurls) {
+ this.info = newurls;
+ this.serverurls = newurls;
+ this.userinfo.json.serverurls = this.info;
+ this.userinfo.updateLocal();
+ }
+ break;
+ }
+ }
setTimeout(() => {
if (this.swapped)
return;
@@ -618,7 +654,7 @@ class Localuser {
}
async typingStart(typing) {
if (this.channelfocus.id === typing.d.channel_id) {
- const guild = SnowFlake.getSnowFlakeFromID(typing.d.guild_id, Guild).getObject();
+ const guild = this.guildids.get(typing.d.guild_id);
const memb = await Member.new(typing.d.member, guild);
if (memb.id === this.user.id) {
console.log("you is typing");
@@ -881,8 +917,7 @@ class Localuser {
this.mfa_enabled = true;
}
});
- }]
- ]);
+ }]]);
console.log("here :3");
addmodel.show();
});
diff --git a/.dist/login.js b/.dist/login.js
index 7091388..aa10fc8 100644
--- a/.dist/login.js
+++ b/.dist/login.js
@@ -17,6 +17,41 @@ function getBulkUsers() {
}
return json;
}
+function trimswitcher() {
+ const json = getBulkInfo();
+ const map = new Map();
+ for (const thing in json.users) {
+ const user = json.users[thing];
+ console.log(user, json.users);
+ let wellknown = user.serverurls.wellknown;
+ if (wellknown[wellknown.length - 1] !== "/") {
+ wellknown += "/";
+ }
+ wellknown += user.username;
+ if (map.has(wellknown)) {
+ const otheruser = map.get(wellknown);
+ if (otheruser[1].serverurls.wellknown[otheruser[1].serverurls.wellknown.length - 1] === "/") {
+ delete json.users[otheruser[0]];
+ map.set(wellknown, [thing, user]);
+ }
+ else {
+ delete json.users[thing];
+ }
+ }
+ else {
+ map.set(wellknown, [thing, user]);
+ }
+ }
+ for (const thing in json.users) {
+ if (thing[thing.length - 1] === "/") {
+ const user = json.users[thing];
+ delete json.users[thing];
+ json.users[thing.slice(0, -1)] = user;
+ }
+ }
+ localStorage.setItem("userinfos", JSON.stringify(json));
+ console.log(json);
+}
function getBulkInfo() {
return JSON.parse(localStorage.getItem("userinfos"));
}
@@ -141,6 +176,7 @@ async function getapiurls(str) {
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
+ login: url.toString()
};
}
catch {
@@ -201,7 +237,7 @@ async function login(username, password, captcha) {
try {
const info = JSON.parse(localStorage.getItem("instanceinfo"));
const api = info.login + (info.login.startsWith("/") ? "/" : "");
- return await fetch(api + 'auth/login', options).then(response => response.json())
+ return await fetch(api + '/auth/login', options).then(response => response.json())
.then((response) => {
console.log(response, response.message);
if ("Invalid Form Body" === response.message) {
@@ -246,6 +282,8 @@ async function login(username, password, captcha) {
}
else {
console.warn(response);
+ if (!response.token)
+ return;
adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }).username = username;
const redir = new URLSearchParams(window.location.search).get("goback");
if (redir) {
@@ -260,6 +298,8 @@ async function login(username, password, captcha) {
}
else {
console.warn(response);
+ if (!response.token)
+ return;
adduser({ serverurls: JSON.parse(localStorage.getItem("instanceinfo")), email: username, token: response.token }).username = username;
const redir = new URLSearchParams(window.location.search).get("goback");
if (redir) {
@@ -347,3 +387,4 @@ if (switchurl) {
}
}
export { checkInstance };
+trimswitcher();
diff --git a/.dist/member.js b/.dist/member.js
index 83683e8..68bd5a7 100644
--- a/.dist/member.js
+++ b/.dist/member.js
@@ -17,7 +17,7 @@ class Member {
this.contextmenu.addbutton("Message user", function () {
fetch(this.info.api + "/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.id] }),
- headers: this.headers
+ headers: this.localuser.headers
});
});
}
diff --git a/.dist/message.js b/.dist/message.js
index d61fa81..96ab4e7 100644
--- a/.dist/message.js
+++ b/.dist/message.js
@@ -70,7 +70,7 @@ class Message {
Message.contextmenu.addbutton("Edit", function () {
this.channel.editing = this;
const markdown = (document.getElementById("typebox"))["markdown"];
- markdown.txt = this.content.rawString;
+ markdown.txt = this.content.rawString.split('');
markdown.boxupdate(document.getElementById("typebox"));
}, null, _ => { return _.author.id === _.localuser.user.id; });
Message.contextmenu.addbutton("Delete message", function () {
diff --git a/.dist/user.js b/.dist/user.js
index 46ef539..86d13d2 100644
--- a/.dist/user.js
+++ b/.dist/user.js
@@ -121,6 +121,32 @@ class User {
async resolvemember(guild) {
return await Member.resolveMember(this, guild);
}
+ async getUserProfile() {
+ return (await fetch(`${this.info.api}/users/${this.id.replace("#clone", "")}/profile?with_mutual_guilds=true&with_mutual_friends=true`, {
+ headers: this.localuser.headers
+ })).json();
+ }
+ resolving = false;
+ async getBadge(id) {
+ console.log(id, ":3");
+ if (this.localuser.badges.has(id)) {
+ return this.localuser.badges.get(id);
+ }
+ else {
+ if (this.resolving) {
+ await this.resolving;
+ return this.localuser.badges.get(id);
+ }
+ const prom = await this.getUserProfile();
+ this.resolving = prom;
+ const badges = prom.badges;
+ this.resolving = false;
+ for (const thing of badges) {
+ this.localuser.badges.set(thing.id, thing);
+ }
+ return this.localuser.badges.get(id);
+ }
+ }
buildpfp() {
const pfp = document.createElement('img');
pfp.src = this.getpfpsrc();
@@ -235,6 +261,28 @@ class User {
this.setstatus("online");
div.classList.add("hypoprofile", "flexttb");
}
+ const badgediv = document.createElement("div");
+ badgediv.classList.add("badges");
+ (async () => {
+ console.log(this.badge_ids, ":3");
+ 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;
+ }
+ badgediv.append(badge);
+ }
+ })();
{
const pfp = await this.buildstatuspfp();
div.appendChild(pfp);
@@ -246,6 +294,7 @@ class User {
const usernamehtml = document.createElement("h2");
usernamehtml.textContent = this.username;
userbody.appendChild(usernamehtml);
+ userbody.appendChild(badgediv);
const discrimatorhtml = document.createElement("h3");
discrimatorhtml.classList.add("tag");
discrimatorhtml.textContent = this.username + "#" + this.discriminator;
diff --git a/tsconfig.json b/tsconfig.json
index b4b481e..9dba0e0 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -6,7 +6,8 @@
"strict": false,
"esModuleInterop": true,
"outDir": "./.dist",
- "removeComments": false
+ "removeComments": false,
+ "noImplicitThis":true
},
"include": [
"./webpage/*.ts"
diff --git a/webpage/channel.ts b/webpage/channel.ts
index c8c7958..0142a62 100644
--- a/webpage/channel.ts
+++ b/webpage/channel.ts
@@ -52,30 +52,30 @@ class Channel{
return this.snowflake.id;
}
static setupcontextmenu(){
- this.contextmenu.addbutton("Copy channel id",function(){
+ this.contextmenu.addbutton("Copy channel id",function(this:Channel){
console.log(this)
navigator.clipboard.writeText(this.id);
});
- this.contextmenu.addbutton("Mark as read",function(){
+ this.contextmenu.addbutton("Mark as read",function(this:Channel){
console.log(this)
this.readbottom();
});
- this.contextmenu.addbutton("Settings[temp]",function(){
+ this.contextmenu.addbutton("Settings[temp]",function(this:Channel){
this.generateSettings();
});
- this.contextmenu.addbutton("Delete channel",function(){
+ this.contextmenu.addbutton("Delete channel",function(this:Channel){
console.log(this)
this.deleteChannel();
},null,_=>{console.log(_);return _.isAdmin()});
- this.contextmenu.addbutton("Edit channel",function(){
- this.editChannel(this);
+ this.contextmenu.addbutton("Edit channel",function(this:Channel){
+ this.editChannel();
},null,_=>{return _.isAdmin()});
- this.contextmenu.addbutton("Make invite",function(){
+ this.contextmenu.addbutton("Make invite",function(this:Channel){
this.createInvite();
},null,(_:Channel)=>{
return _.hasPermission("CREATE_INSTANT_INVITE")&&_.type!==4
@@ -203,7 +203,9 @@ class Channel{
async function(this:Channel,id:string){
const message=SnowFlake.getSnowFlakeFromID(id,Message).getObject();
try{
- message.deleteDiv();
+ if(message){
+ message.deleteDiv();
+ }
}catch(e){console.error(e)}finally{}
}.bind(this),
this.readbottom.bind(this)
@@ -355,9 +357,9 @@ class Channel{
addchannel.textContent="+";
addchannel.classList.add("addchannel");
caps.appendChild(addchannel);
- addchannel.onclick=function(){
+ addchannel.onclick=_=>{
this.guild.createchannels(this.createChannel.bind(this));
- }.bind(this);
+ }
this.coatDropDiv(decdiv,childrendiv);
}
div.appendChild(caps)
@@ -536,9 +538,9 @@ class Channel{
const full=new Dialog(
["hdiv",
["vdiv",
- ["textbox","Channel name:",this.name,function(){name=this.value}],
- ["mdbox","Channel topic:",this.topic,function(){topic=this.value}],
- ["checkbox","NSFW Channel",this.nsfw,function(){nsfw=this.checked}],
+ ["textbox","Channel name:",this.name,function(this:HTMLInputElement){name=this.value}],
+ ["mdbox","Channel topic:",this.topic,function(this:HTMLTextAreaElement){topic=this.value}],
+ ["checkbox","NSFW Channel",this.nsfw,function(this:HTMLInputElement){nsfw=this.checked}],
["button","","submit",()=>{
fetch(this.info.api+"/channels/"+thisid,{
method:"PATCH",
@@ -809,6 +811,9 @@ class Channel{
if(this.infinitefocus) return;
this.infinitefocus=true;
const messages=document.getElementById("channelw");
+ for(const thing of messages.getElementsByClassName("messagecontainer")){
+ thing.remove();
+ }
const loading=document.getElementById("loadingdiv");
const removetitle=document.getElementById("removetitle");
//messages.innerHTML="";
diff --git a/webpage/dialog.ts b/webpage/dialog.ts
index 985031a..def7c25 100644
--- a/webpage/dialog.ts
+++ b/webpage/dialog.ts
@@ -241,7 +241,7 @@ class Dialog{
this.background.classList.add("background");
document.body.appendChild(this.background);
document.body.appendChild(this.html);
- this.background.onclick = function(){this.hide();}.bind(this);
+ this.background.onclick = _=>{this.hide()};
}
hide(){
document.body.removeChild(this.background);
diff --git a/webpage/direct.ts b/webpage/direct.ts
index a00361e..af92dae 100644
--- a/webpage/direct.ts
+++ b/webpage/direct.ts
@@ -111,6 +111,7 @@ class Group extends Channel{
this.lastmessageid??=new SnowFlake("0",undefined);
this.mentions=0;
this.setUpInfiniteScroller();
+ this.position=Math.max(this.lastmessageid.getUnixTime(),this.snowflake.getUnixTime());
}
createguildHTML(){
const div=document.createElement("div")
diff --git a/webpage/guild.ts b/webpage/guild.ts
index 8bef811..b662a64 100644
--- a/webpage/guild.ts
+++ b/webpage/guild.ts
@@ -31,33 +31,33 @@ class Guild{
}
static contextmenu=new Contextmenu("guild menu");
static setupcontextmenu(){
- Guild.contextmenu.addbutton("Copy Guild id",function(){
+ Guild.contextmenu.addbutton("Copy Guild id",function(this:Guild){
console.log(this)
navigator.clipboard.writeText(this.id);
});
- Guild.contextmenu.addbutton("Mark as read",function(){
+ Guild.contextmenu.addbutton("Mark as read",function(this:Guild){
console.log(this)
this.markAsRead();
});
- Guild.contextmenu.addbutton("Notifications",function(){
+ Guild.contextmenu.addbutton("Notifications",function(this:Guild){
console.log(this)
this.setnotifcation();
});
- Guild.contextmenu.addbutton("Leave guild",function(){
+ Guild.contextmenu.addbutton("Leave guild",function(this:Guild){
this.confirmleave();
},null,function(_){return _.properties.owner_id!==_.member.user.id});
- Guild.contextmenu.addbutton("Delete guild",function(){
+ Guild.contextmenu.addbutton("Delete guild",function(this:Guild){
this.confirmDelete();
},null,function(_){return _.properties.owner_id===_.member.user.id});
- Guild.contextmenu.addbutton("Create invite",function(){
+ Guild.contextmenu.addbutton("Create invite",function(this:Guild){
console.log(this);
},null,_=>true,_=>false);
- Guild.contextmenu.addbutton("Settings[temp]",function(){
+ Guild.contextmenu.addbutton("Settings[temp]",function(this:Guild){
this.generateSettings();
});
/* -----things left for later-----
@@ -86,6 +86,9 @@ class Guild{
if(json===-1){
return;
}
+ if(json.stickers.length){
+ console.log(json.stickers,":3")
+ }
this.emojis = json.emojis
this.owner=owner;
this.headers=this.owner.headers;
@@ -136,15 +139,12 @@ class Guild{
noti
],
["button","","submit",_=>{
- fetch(this.info.api+"/users/@me/guilds/settings",{
+ //
+ fetch(this.info.api+`/users/@me/guilds/${this.id}/settings/`,{
method:"PATCH",
headers:this.headers,
body:JSON.stringify({
- "guilds":{
- [this.id]:{
- "message_notifications": noti
- }
- }
+ "message_notifications": noti
})
})
this.message_notifications=noti;
@@ -296,7 +296,7 @@ class Guild{
["textbox",
"Name of server:",
"",
- function(){
+ function(this:HTMLInputElement){
confirmname=this.value;
}
]
@@ -447,7 +447,7 @@ class Guild{
},
1
],
- ["textbox","Name of channel","",function(){
+ ["textbox","Name of channel","",function(this:HTMLInputElement){
console.log(this)
name=this.value
}],
@@ -464,11 +464,11 @@ class Guild{
let category=4;
const channelselect=new Dialog(
["vdiv",
- ["textbox","Name of category","",function(){
+ ["textbox","Name of category","",function(this:HTMLInputElement){
console.log(this);
name=this.value;
}],
- ["button","","submit",function(){
+ ["button","","submit",()=>{
console.log(name,category)
this.createChannel(name,category);
channelselect.hide();
diff --git a/webpage/index.ts b/webpage/index.ts
index e84ca69..972190e 100644
--- a/webpage/index.ts
+++ b/webpage/index.ts
@@ -95,6 +95,7 @@ function showAccountSwitcher(){
}
let thisuser:Localuser;
try{
+ console.log(users.users,users.currentuser)
thisuser=new Localuser(users.users[users.currentuser]);
thisuser.initwebsocket().then(_=>{
thisuser.loaduser();
@@ -103,7 +104,8 @@ try{
document.getElementById("loading").classList.remove("loading");
console.log("done loading")
});
-}catch{
+}catch(e){
+ console.error(e);
document.getElementById("load-desc").textContent="Account unable to start";
thisuser=new Localuser(-1);
}
@@ -164,13 +166,13 @@ typebox.addEventListener("keydown",event=>{
console.log(typebox)
typebox.onclick=console.log;
-
+/*
function getguildinfo(){
const path=window.location.pathname.split("/");
const channel=path[3];
this.ws.send(JSON.stringify({op: 14, d: {guild_id: path[2], channels: {[channel]: [[0, 99]]}}}));
}
-
+*/
const images:Blob[]=[];
const imageshtml=[];
diff --git a/webpage/infiniteScroller.ts b/webpage/infiniteScroller.ts
index 951d902..3d47d4f 100644
--- a/webpage/infiniteScroller.ts
+++ b/webpage/infiniteScroller.ts
@@ -163,8 +163,8 @@ class InfiniteScroller{
this.currrunning=true;
}
if(!this.div){this.currrunning=false;return}
- const out=await Promise.allSettled([this.watchForTop(),this.watchForBottom()])
- const changed=(out[0]||out[1]);
+ 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){
this.timeout=setTimeout(this.updatestuff.bind(this),300);
}
@@ -213,7 +213,7 @@ class InfiniteScroller{
await this.destroyFromID(thing[1]);
}
this.HTMLElements=[];
- clearInterval(this.timeout);
+ clearTimeout(this.timeout);
if(this.div){
this.div.remove();
}
diff --git a/webpage/jsontypes.ts b/webpage/jsontypes.ts
index 0d1a312..83d5609 100644
--- a/webpage/jsontypes.ts
+++ b/webpage/jsontypes.ts
@@ -137,7 +137,7 @@ type userjson={
premium_type: number,
theme_colors: string,
pronouns: string,
- badge_ids: string,
+ badge_ids: string[],
}
type memberjson= {
index?:number,
diff --git a/webpage/localuser.ts b/webpage/localuser.ts
index 6ab9684..b3da697 100644
--- a/webpage/localuser.ts
+++ b/webpage/localuser.ts
@@ -4,7 +4,7 @@ import {Direct} from "./direct.js";
import {Voice} from "./audio.js";
import {User} from "./user.js";
import {Dialog} from "./dialog.js";
-import {getBulkInfo, setTheme, Specialuser} from "./login.js";
+import {getapiurls, getBulkInfo, setTheme, Specialuser} from "./login.js";
import { SnowFlake } from "./snowflake.js";
import { Message } from "./message.js";
import { channeljson, guildjson, memberjson, presencejson, readyjson } from "./jsontypes.js";
@@ -15,6 +15,7 @@ import { MarkDown } from "./markdown.js";
const wsCodesRetry=new Set([4000,4003,4005,4007,4008,4009]);
class Localuser{
+ badges:Map=new Map();
lastSequence:number|null=null;
token:string;
userinfo:Specialuser;
@@ -214,7 +215,7 @@ class Localuser{
}
});
- this.ws.addEventListener("close", event => {
+ this.ws.addEventListener("close",async event => {
this.ws=undefined;
console.log("WebSocket closed with code " + event.code);
@@ -230,7 +231,43 @@ class Localuser{
this.connectionSucceed=0;
document.getElementById("load-desc").innerHTML="Unable to connect to the Spacebar server, retrying in " + Math.round(0.2 + (this.errorBackoff*2.8)) + " seconds...";
+ switch(this.errorBackoff){//try to recover from bad domain
+ case 3:
+ const newurls=await getapiurls(this.info.wellknown);
+ if(newurls){
+ this.info=newurls;
+ this.serverurls=newurls;
+ this.userinfo.json.serverurls=this.info;
+ this.userinfo.updateLocal();
+ break
+ }
+ case 4:
+ {
+ const newurls=await getapiurls(new URL(this.info.wellknown).origin);
+ if(newurls){
+ this.info=newurls;
+ this.serverurls=newurls;
+ this.userinfo.json.serverurls=this.info;
+ this.userinfo.updateLocal();
+ break
+ }
+
+ }
+ case 5:
+ {
+ const breakappart=new URL(this.info.wellknown).origin.split(".");
+ const url="https://"+breakappart[breakappart.length-2]+"."+breakappart[breakappart.length-1]
+ const newurls=await getapiurls(url);
+ if(newurls){
+ this.info=newurls;
+ this.serverurls=newurls;
+ this.userinfo.json.serverurls=this.info;
+ this.userinfo.updateLocal();
+ }
+ break
+ }
+ }
setTimeout(() => {
if(this.swapped) return;
document.getElementById("load-desc").textContent="Retrying...";
@@ -497,7 +534,7 @@ class Localuser{
["textbox",
"Invite Link/Code",
"",
- function(){
+ function(this:HTMLInputElement){
inviteurl=this.value;
}
],
@@ -635,7 +672,8 @@ class Localuser{
}
async typingStart(typing):Promise{
if(this.channelfocus.id===typing.d.channel_id){
- const guild=SnowFlake.getSnowFlakeFromID(typing.d.guild_id,Guild).getObject()
+
+ const guild=this.guildids.get(typing.d.guild_id);
const memb=await Member.new(typing.d.member,guild);
if(memb.id===this.user.id){
console.log("you is typing")
@@ -878,8 +916,8 @@ class Localuser{
["title","2FA set up"],
["text","Copy this secret into your totp(time-based one time password) app"],
["text",`Your secret is: ${secret} and it's 6 digits, with a 30 second token period`],
- ["textbox","Account password:","",function(){password=this.value}],
- ["textbox","Code:","",function(){code=this.value}],
+ ["textbox","Account password:","",function(this:HTMLInputElement){password=this.value}],
+ ["textbox","Code:","",function(this:HTMLInputElement){code=this.value}],
["button","","Submit",()=>{
fetch(this.info.api+"/users/@me/mfa/totp/enable/",{
method:"POST",
diff --git a/webpage/login.ts b/webpage/login.ts
index f01575b..45d3d56 100644
--- a/webpage/login.ts
+++ b/webpage/login.ts
@@ -18,6 +18,39 @@ function getBulkUsers(){
}
return json;
}
+function trimswitcher(){
+ const json=getBulkInfo()
+ const map=new Map();
+ for(const thing in json.users){
+ const user=json.users[thing];
+ console.log(user,json.users);
+ let wellknown=user.serverurls.wellknown;
+ if(wellknown[wellknown.length-1]!=="/"){
+ wellknown+="/";
+ }
+ wellknown+=user.username;
+ if(map.has(wellknown)){
+ const otheruser=map.get(wellknown);
+ if(otheruser[1].serverurls.wellknown[otheruser[1].serverurls.wellknown.length-1]==="/"){
+ delete json.users[otheruser[0]];
+ map.set(wellknown,[thing,user]);
+ }else{
+ delete json.users[thing];
+ }
+ }else{
+ map.set(wellknown,[thing,user]);
+ }
+ }
+ for(const thing in json.users){
+ if(thing[thing.length-1]==="/"){
+ const user=json.users[thing];
+ delete json.users[thing];
+ json.users[thing.slice(0, -1)]=user;
+ }
+ }
+ localStorage.setItem("userinfos",JSON.stringify(json));
+ console.log(json);
+}
function getBulkInfo(){
return JSON.parse(localStorage.getItem("userinfos"));
@@ -121,7 +154,7 @@ function adduser(user){
const instancein=document.getElementById("instancein") as HTMLInputElement;
let timeout;
let instanceinfo;
-async function getapiurls(str:string):Promise<{api:string,cdn:string,gateway:string,wellknown:string}|false>{
+async function getapiurls(str:string):Promise<{api:string,cdn:string,gateway:string,wellknown:string,login:string}|false>{
if(str[str.length-1]!=="/"){
str+="/"
}
@@ -141,6 +174,7 @@ async function getapiurls(str:string):Promise<{api:string,cdn:string,gateway:str
gateway: info.gateway,
cdn: info.cdn,
wellknown: str,
+ login:url.toString()
};
}catch{
return false;
@@ -199,7 +233,7 @@ async function login(username:string, password:string, captcha:string){
try{
const info=JSON.parse(localStorage.getItem("instanceinfo"));
const api=info.login+(info.login.startsWith("/")?"/":"");
- return await fetch(api+'auth/login',options).then(response=>response.json())
+ return await fetch(api+'/auth/login',options).then(response=>response.json())
.then((response) => {
console.log(response,response.message)
if("Invalid Form Body"===response.message){
@@ -229,7 +263,7 @@ async function login(username:string, password:string, captcha:string){
console.log(response);
if(response.ticket){
let onetimecode="";
- new Dialog(["vdiv",["title","2FA code:"],["textbox","","",function(){onetimecode=this.value}],["button","","Submit",function(){
+ new Dialog(["vdiv",["title","2FA code:"],["textbox","","",function(this:HTMLInputElement){onetimecode=this.value}],["button","","Submit",function(){
fetch(api+"/auth/mfa/totp",{
method:"POST",
headers:{
@@ -244,6 +278,7 @@ async function login(username:string, password:string, captcha:string){
alert(response.message)
}else{
console.warn(response);
+ if(!response.token) return;
adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:username,token:response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
@@ -256,6 +291,7 @@ async function login(username:string, password:string, captcha:string){
}]]).show();
}else{
console.warn(response);
+ if(!response.token) return;
adduser({serverurls:JSON.parse(localStorage.getItem("instanceinfo")),email:username,token:response.token}).username=username;
const redir=new URLSearchParams(window.location.search).get("goback");
if(redir){
@@ -343,3 +379,4 @@ if(switchurl){
}
}
export {checkInstance};
+trimswitcher();
diff --git a/webpage/member.ts b/webpage/member.ts
index dbbd865..8578bfb 100644
--- a/webpage/member.ts
+++ b/webpage/member.ts
@@ -14,14 +14,14 @@ class Member{
nick:string;
static contextmenu:Contextmenu=new Contextmenu("User Menu");
static setUpContextMenu(){
- this.contextmenu.addbutton("Copy user id",function(){
+ this.contextmenu.addbutton("Copy user id",function(this:Member){
navigator.clipboard.writeText(this.id);
});
- this.contextmenu.addbutton("Message user",function(){
+ this.contextmenu.addbutton("Message user",function(this:Member){
fetch(this.info.api+"/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
- headers: this.headers
+ headers: this.localuser.headers
});
});
}
diff --git a/webpage/message.ts b/webpage/message.ts
index c36e1f8..ce8a79d 100644
--- a/webpage/message.ts
+++ b/webpage/message.ts
@@ -57,13 +57,13 @@ class Message{
this.del=new Promise(_=>{this.resolve=_})
}
static setupcmenu(){
- Message.contextmenu.addbutton("Copy raw text",function(){
+ Message.contextmenu.addbutton("Copy raw text",function(this:Message){
navigator.clipboard.writeText(this.content.rawString);
});
Message.contextmenu.addbutton("Reply",function(this:Message,div:HTMLDivElement){
this.channel.setReplying(this);
});
- Message.contextmenu.addbutton("Copy message id",function(){
+ Message.contextmenu.addbutton("Copy message id",function(this:Message){
navigator.clipboard.writeText(this.id);
});
Message.contextmenu.addsubmenu("Add reaction",function(this:Message,e){
@@ -72,13 +72,13 @@ class Message{
this.reactionToggle(_);
});
});
- Message.contextmenu.addbutton("Edit",function(){
+ Message.contextmenu.addbutton("Edit",function(this:Message){
this.channel.editing=this;
const markdown=(document.getElementById("typebox"))["markdown"] as MarkDown;
- markdown.txt=this.content.rawString;
+ markdown.txt=this.content.rawString.split('');
markdown.boxupdate(document.getElementById("typebox"));
},null,_=>{return _.author.id===_.localuser.user.id});
- Message.contextmenu.addbutton("Delete message",function(){
+ Message.contextmenu.addbutton("Delete message",function(this:Message){
this.delete();
},null,_=>{return _.canDelete()})
}
diff --git a/webpage/style.css b/webpage/style.css
index 53e7fec..7b2dd09 100644
--- a/webpage/style.css
+++ b/webpage/style.css
@@ -1782,7 +1782,7 @@ form div{
justify-content: center;
padding: .5in .2in;
gap: .1in;
- width: 5.in;
+ width: 5in;
}
#AcceptInvite{
padding: .1in .2in;
@@ -1929,3 +1929,28 @@ form div{
#channelTopic {
margin-left: 10px;
}
+
+.badge{
+ display:flex;
+ color:white;
+ width:fit-content;
+ img{
+ width: .1in;
+ height: .1in;
+ }
+ background: var(--profile-bg);
+ padding: .04in;
+ border-radius: .07in;
+ font-size: .12in;
+ align-items: center;
+ border: solid .01in var(--black);
+ box-sizing: border-box;
+}
+.badges{
+ width:fit-content;
+ display: flex;
+ flex-direction: row;
+ flex-wrap: wrap;
+ justify-content: flex-start;
+ align-items: center;
+}
diff --git a/webpage/user.ts b/webpage/user.ts
index 6415ad7..99ec8b2 100644
--- a/webpage/user.ts
+++ b/webpage/user.ts
@@ -25,7 +25,7 @@ class User{
premium_since: string;
premium_type: number;
theme_colors: string;
- badge_ids: string;
+ badge_ids: string[];
members: WeakMap>=new WeakMap();
private status:string;
clone(){
@@ -71,7 +71,7 @@ class User{
this.contextmenu.addbutton("Copy user id",function(this:User){
navigator.clipboard.writeText(this.id);
});
- this.contextmenu.addbutton("Message user",function(){
+ this.contextmenu.addbutton("Message user",function(this:User){
fetch(this.info.api+"/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
@@ -121,6 +121,34 @@ class User{
async resolvemember(guild:Guild){
return await Member.resolveMember(this,guild);
}
+
+ async getUserProfile(){
+ return (await fetch(`${this.info.api}/users/${this.id.replace("#clone","")}/profile?with_mutual_guilds=true&with_mutual_friends=true`,{
+ headers:this.localuser.headers
+ })).json()
+ }
+ resolving:false|Promise=false;
+ async getBadge(id:string){
+ console.log(id,":3")
+ if(this.localuser.badges.has(id)){
+ return this.localuser.badges.get(id);
+ }else{
+ if(this.resolving)
+ {
+ await this.resolving;
+ return this.localuser.badges.get(id);
+ }
+
+ const prom=await this.getUserProfile();
+ this.resolving=prom;
+ const badges=prom.badges;
+ this.resolving=false;
+ for(const thing of badges){
+ this.localuser.badges.set(thing.id,thing);
+ }
+ return this.localuser.badges.get(id);
+ }
+ }
buildpfp(){
const pfp=document.createElement('img');
pfp.src=this.getpfpsrc();
@@ -236,7 +264,27 @@ class User{
this.setstatus("online");
div.classList.add("hypoprofile","flexttb");
}
-
+ const badgediv=document.createElement("div");
+ badgediv.classList.add("badges");
+ (async ()=>{
+ console.log(this.badge_ids,":3")
+ 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;
+ }
+ badgediv.append(badge);
+ }
+ })()
{
const pfp=await this.buildstatuspfp();
div.appendChild(pfp);
@@ -248,7 +296,7 @@ class User{
const usernamehtml=document.createElement("h2");
usernamehtml.textContent=this.username;
userbody.appendChild(usernamehtml);
-
+ userbody.appendChild(badgediv);
const discrimatorhtml=document.createElement("h3");
discrimatorhtml.classList.add("tag");
discrimatorhtml.textContent=this.username+"#"+this.discriminator;