adds support for role settings and various fixes

This commit is contained in:
MathMan05 2024-06-30 22:05:14 -05:00
parent 96b2dbb21c
commit 8fe0c9f46b
20 changed files with 1199 additions and 183 deletions

View file

@ -5,6 +5,8 @@ import { Contextmenu } from "./contextmenu.js";
import { Fullscreen } from "./fullscreen.js"; import { Fullscreen } from "./fullscreen.js";
import { markdown } from "./markdown.js"; import { markdown } from "./markdown.js";
import { Permissions } from "./permissions.js"; import { Permissions } from "./permissions.js";
import { Settings, RoleList } from "./settings.js";
Settings;
class Channel { class Channel {
editing; editing;
type; type;
@ -19,6 +21,7 @@ class Channel {
guild_id; guild_id;
messageids; messageids;
permission_overwrites; permission_overwrites;
permission_overwritesar;
topic; topic;
nsfw; nsfw;
position; position;
@ -33,22 +36,38 @@ class Channel {
static contextmenu = new Contextmenu("channel menu"); static contextmenu = new Contextmenu("channel menu");
replyingto; replyingto;
static setupcontextmenu() { static setupcontextmenu() {
Channel.contextmenu.addbutton("Copy channel id", function () { this.contextmenu.addbutton("Copy channel id", function () {
console.log(this); console.log(this);
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Channel.contextmenu.addbutton("Mark as read", function () { this.contextmenu.addbutton("Mark as read", function () {
console.log(this); console.log(this);
this.readbottom(); this.readbottom();
}); });
Channel.contextmenu.addbutton("Delete channel", function () { this.contextmenu.addbutton("Settings[temp]", function () {
this.generateSettings();
});
this.contextmenu.addbutton("Delete channel", function () {
console.log(this); console.log(this);
this.deleteChannel(); this.deleteChannel();
}, null, _ => { console.log(_); return _.isAdmin(); }); }, null, _ => { console.log(_); return _.isAdmin(); });
Channel.contextmenu.addbutton("Edit channel", function () { this.contextmenu.addbutton("Edit channel", function () {
this.editChannel(this); this.editChannel(this);
}, null, _ => { return _.isAdmin(); }); }, null, _ => { return _.isAdmin(); });
} }
generateSettings() {
this.sortPerms();
const settings = new Settings("Settings for " + this.name);
const s1 = settings.addButton("roles");
s1.options.push(new RoleList(this.permission_overwritesar, this.guild, this.updateRolePermissions.bind(this), true));
settings.show();
}
sortPerms() {
this.permission_overwritesar.sort((a, b) => {
const order = this.guild.roles.findIndex(_ => _.id === a[0]) - this.guild.roles.findIndex(_ => _.id === b[0]);
return order;
});
}
constructor(JSON, owner) { constructor(JSON, owner) {
if (JSON === -1) { if (JSON === -1) {
return; return;
@ -66,8 +85,15 @@ class Channel {
this.guild_id = JSON.guild_id; this.guild_id = JSON.guild_id;
this.messageids = {}; this.messageids = {};
this.permission_overwrites = {}; this.permission_overwrites = {};
this.permission_overwritesar = [];
for (const thing of JSON.permission_overwrites) { for (const thing of JSON.permission_overwrites) {
console.log(thing);
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
;
this.permission_overwrites[thing.id] = new Permissions(thing.allow, thing.deny); this.permission_overwrites[thing.id] = new Permissions(thing.allow, thing.deny);
this.permission_overwritesar.push([thing.id, this.permission_overwrites[thing.id]]);
} }
this.topic = JSON.topic; this.topic = JSON.topic;
this.nsfw = JSON.nsfw; this.nsfw = JSON.nsfw;
@ -117,6 +143,9 @@ class Channel {
return false; return false;
} }
get canMessage() { get canMessage() {
if ((0 === this.permission_overwritesar.length) && this.hasPermission("MANAGE_CHANNELS")) {
this.addRoleToPerms(this.guild.roles.find(_ => _.name === "@everyone"));
}
return this.hasPermission("SEND_MESSAGES"); return this.hasPermission("SEND_MESSAGES");
} }
sortchildren() { sortchildren() {
@ -726,6 +755,36 @@ class Channel {
}); });
} }
} }
async addRoleToPerms(role) {
await fetch(this.info.api.toString() + "/channels/" + this.id + "/permissions/" + role.id, {
method: "PUT",
headers: this.headers,
body: JSON.stringify({
allow: "0",
deny: "0",
id: role.id,
type: 0
})
});
const perm = new Permissions("0", "0");
this.permission_overwrites[role.id] = perm;
this.permission_overwritesar.push([role.id, perm]);
}
async updateRolePermissions(id, perms) {
const permision = this.permission_overwrites[id];
permision.allow = perms.allow;
permision.deny = perms.deny;
await fetch(this.info.api.toString() + "/channels/" + this.id + "/permissions/" + id, {
method: "PUT",
headers: this.headers,
body: JSON.stringify({
allow: permision.allow.toString(),
deny: permision.deny.toString(),
id: id,
type: 0
})
});
}
} }
Channel.setupcontextmenu(); Channel.setupcontextmenu();
export { Channel }; export { Channel };

View file

@ -3,6 +3,7 @@ import { Contextmenu } from "./contextmenu.js";
import { Role } from "./role.js"; import { Role } from "./role.js";
import { Fullscreen } from "./fullscreen.js"; import { Fullscreen } from "./fullscreen.js";
import { Member } from "./member.js"; import { Member } from "./member.js";
import { Settings, RoleList } from "./settings.js";
class Guild { class Guild {
owner; owner;
headers; headers;
@ -42,6 +43,9 @@ class Guild {
Guild.contextmenu.addbutton("Create invite", function () { Guild.contextmenu.addbutton("Create invite", function () {
console.log(this); console.log(this);
}, null, _ => true, _ => false); }, null, _ => true, _ => false);
Guild.contextmenu.addbutton("Settings[temp]", function () {
this.generateSettings();
});
/* -----things left for later----- /* -----things left for later-----
guild.contextmenu.addbutton("Leave Guild",function(){ guild.contextmenu.addbutton("Leave Guild",function(){
console.log(this) console.log(this)
@ -53,6 +57,16 @@ class Guild {
},null,_=>{return thisuser.isAdmin()}) },null,_=>{return thisuser.isAdmin()})
*/ */
} }
generateSettings() {
const settings = new Settings("Settings for " + this.properties.name);
const s1 = settings.addButton("roles");
const permlist = [];
for (const thing of this.roles) {
permlist.push([thing.id, thing.permissions]);
}
s1.options.push(new RoleList(permlist, this, this.updateRolePermissions.bind(this)));
settings.show();
}
constructor(JSON, owner, member) { constructor(JSON, owner, member) {
if (JSON === -1) { if (JSON === -1) {
return; return;
@ -481,6 +495,40 @@ class Guild {
body: JSON.stringify({ name: name, type: type }) body: JSON.stringify({ name: name, type: type })
}); });
} }
async createRole(name) {
const fetched = await fetch(this.info.api.toString() + "/guilds/" + this.id + "roles", {
method: "POST",
headers: this.headers,
body: JSON.stringify({
name: name,
color: 0,
permissions: "0"
})
});
const json = await fetched.json();
const role = new Role(json, this);
this.roleids[role.id] = role;
this.roles.push(role);
return role;
}
async updateRolePermissions(id, perms) {
const role = this.roleids[id];
role.permissions.allow = perms.allow;
role.permissions.deny = perms.deny;
await fetch(this.info.api.toString() + "/guilds/" + this.id + "/roles/" + this.id, {
method: "PATCH",
headers: this.headers,
body: JSON.stringify({
color: role.color,
hoist: role.hoist,
icon: role.icon,
mentionable: role.mentionable,
name: role.name,
permissions: role.permissions.allow.toString(),
unicode_emoji: role.unicode_emoji,
})
});
}
} }
Guild.setupcontextmenu(); Guild.setupcontextmenu();
export { Guild }; export { Guild };

View file

@ -10,17 +10,6 @@ async function waitforload() {
await res; await res;
} }
await waitforload(); await waitforload();
function setDynamicHeight() {
var servertdHeight = document.getElementById('servertd').offsetHeight + document.getElementById('typediv').offsetHeight + document.getElementById('pasteimage').offsetHeight;
document.documentElement.style.setProperty('--servertd-height', servertdHeight + 'px');
}
const resizeObserver = new ResizeObserver(() => {
setDynamicHeight();
});
resizeObserver.observe(document.getElementById('servertd'));
resizeObserver.observe(document.getElementById('replybox'));
resizeObserver.observe(document.getElementById('pasteimage'));
setDynamicHeight();
const users = getBulkUsers(); const users = getBulkUsers();
if (!users.currentuser) { if (!users.currentuser) {
window.location.href = '/login.html'; window.location.href = '/login.html';
@ -101,7 +90,7 @@ thisuser.initwebsocket().then(_ => {
} }
Contextmenu.currentmenu = table; Contextmenu.currentmenu = table;
console.log(table); console.log(table);
userdock.append(table); userdock.before(table);
event.stopImmediatePropagation(); event.stopImmediatePropagation();
}); });
} }

View file

@ -1,11 +1,24 @@
import { User } from "./user.js"; import { User } from "./user.js";
import { Guild } from "./guild.js"; import { Guild } from "./guild.js";
import { Contextmenu } from "./contextmenu.js";
class Member { class Member {
static already = {}; static already = {};
owner; owner;
user; user;
roles; roles;
error; error;
static contextmenu = new Contextmenu("User Menu");
static setUpContextMenu() {
this.contextmenu.addbutton("Copy user id", function () {
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user", function () {
fetch(this.info.api.toString() + "/v9/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.id] }),
headers: this.headers
});
});
}
constructor(memberjson, owner, error = false) { constructor(memberjson, owner, error = false) {
this.error = error; this.error = error;
this.owner = owner; this.owner = owner;
@ -54,8 +67,13 @@ class Member {
console.error(guild); console.error(guild);
} }
let user; let user;
let id = "";
if (unkown instanceof User) { if (unkown instanceof User) {
user = unkown; user = unkown;
id = user.id;
}
else if (typeof unkown === typeof "") {
id = unkown;
} }
else { else {
return new Member(unkown, guild); return new Member(unkown, guild);
@ -66,26 +84,26 @@ class Member {
if (!Member.already[guild.id]) { if (!Member.already[guild.id]) {
Member.already[guild.id] = {}; Member.already[guild.id] = {};
} }
else if (Member.already[guild.id][user.id]) { else if (Member.already[guild.id][id]) {
const memb = Member.already[guild.id][user.id]; const memb = Member.already[guild.id][id];
if (memb instanceof Promise) { if (memb instanceof Promise) {
return await memb; return await memb;
} }
return memb; return memb;
} }
const promoise = fetch(guild.info.api.toString() + "/v9/users/" + user.id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.id, { headers: guild.headers }).then(_ => _.json()).then(json => { const promoise = fetch(guild.info.api.toString() + "/v9/users/" + id + "/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id=" + guild.id, { headers: guild.headers }).then(_ => _.json()).then(json => {
const memb = new Member(json, guild); const memb = new Member(json, guild);
Member.already[guild.id][user.id] = memb; Member.already[guild.id][id] = memb;
console.log("resolved"); console.log("resolved");
return memb; return memb;
}); });
Member.already[guild.id][user.id] = promoise; Member.already[guild.id][id] = promoise;
try { try {
return await promoise; return await promoise;
} }
catch (_) { catch (_) {
const memb = new Member(user, guild, true); const memb = new Member(user, guild, true);
Member.already[guild.id][user.id] = memb; Member.already[guild.id][id] = memb;
return memb; return memb;
} }
} }
@ -115,5 +133,28 @@ class Member {
} }
return this.guild.properties.owner_id === this.user.id; return this.guild.properties.owner_id === this.user.id;
} }
bind(html) {
if (html.tagName === "SPAN") {
if (!this) {
return;
} }
;
console.log(this.error);
if (this.error) {
const error = document.createElement("span");
error.textContent = "!";
error.classList.add("membererror");
html.after(error);
return;
}
html.style.color = this.getColor();
}
this.profileclick(html);
Member.contextmenu.bind(html);
}
profileclick(html) {
//to be implemented
}
}
Member.setUpContextMenu();
export { Member }; export { Member };

View file

@ -41,15 +41,6 @@ class Message {
Message.contextmenu.addbutton("Copy message id", function () { Message.contextmenu.addbutton("Copy message id", function () {
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Message.contextmenu.addbutton("Copy user id", function () {
navigator.clipboard.writeText(this.author.id);
});
Message.contextmenu.addbutton("Message user", function () {
fetch(this.info.api.toString() + "/v9/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.author.id] }),
headers: this.headers
});
});
Message.contextmenu.addbutton("Edit", function () { Message.contextmenu.addbutton("Edit", function () {
this.channel.editing = this; this.channel.editing = this;
document.getElementById("typebox").value = this.content; document.getElementById("typebox").value = this.content;
@ -174,25 +165,26 @@ class Message {
replyline.appendChild(username); replyline.appendChild(username);
const reply = document.createElement("div"); const reply = document.createElement("div");
username.classList.add("username"); username.classList.add("username");
this.author.bind(username, this.guild);
/*
Member.resolve(this.author,this.guild).then(_=>{ Member.resolve(this.author,this.guild).then(_=>{
if (!_) { if(!_) {return};
return;
}
;
console.log(_.error); console.log(_.error);
if(_.error){ if(_.error){
username.textContent+="Error"; username.textContent+="Error";
alert("Should've gotten here"); alert("Should've gotten here")
const error=document.createElement("span"); const error=document.createElement("span");
error.textContent="!"; error.textContent="!";
error.classList.add("membererror"); error.classList.add("membererror");
username.after(error); username.after(error);
return; return;
} }
username.style.color=_.getColor(); username.style.color=_.getColor();
}).catch(_=>{ }).catch(_=>{
console.log(_); console.log(_)
}); });
*/
reply.classList.add("replytext"); reply.classList.add("replytext");
replyline.appendChild(reply); replyline.appendChild(reply);
const line2 = document.createElement("hr"); const line2 = document.createElement("hr");
@ -204,9 +196,9 @@ class Message {
const author = message.author; const author = message.author;
reply.appendChild(markdown(message.content, { stdsize: true })); reply.appendChild(markdown(message.content, { stdsize: true }));
minipfp.src = author.getpfpsrc(); minipfp.src = author.getpfpsrc();
author.profileclick(minipfp); author.bind(minipfp);
username.textContent = author.username; username.textContent = author.username;
author.profileclick(username); author.bind(username);
}); });
div.appendChild(replyline); div.appendChild(replyline);
} }
@ -227,7 +219,7 @@ class Message {
const combine = (premessage?.author?.id != this.author.id) || (current) || this.message_reference; const combine = (premessage?.author?.id != this.author.id) || (current) || this.message_reference;
if (combine) { if (combine) {
const pfp = this.author.buildpfp(); const pfp = this.author.buildpfp();
this.author.profileclick(pfp); this.author.bind(pfp);
pfpRow.appendChild(pfp); pfpRow.appendChild(pfp);
} }
else { else {
@ -242,21 +234,7 @@ class Message {
if (combine) { if (combine) {
const username = document.createElement("span"); const username = document.createElement("span");
username.classList.add("username"); username.classList.add("username");
this.author.profileclick(username); this.author.bind(username, this.guild);
Member.resolve(this.author, this.guild).then(_ => {
if (!_) {
return;
}
;
if (_.error) {
const error = document.createElement("span");
error.textContent = "!";
error.classList.add("membererror");
username.after(error);
return;
}
username.style.color = _.getColor();
});
username.textContent = this.author.username; username.textContent = this.author.username;
const userwrap = document.createElement("tr"); const userwrap = document.createElement("tr");
userwrap.appendChild(username); userwrap.appendChild(username);

View file

@ -2,7 +2,9 @@ export { Permissions };
class Permissions { class Permissions {
allow; allow;
deny; deny;
hasDeny;
constructor(allow, deny = "") { constructor(allow, deny = "") {
this.hasDeny = !!deny;
this.allow = BigInt(allow); this.allow = BigInt(allow);
this.deny = BigInt(deny); this.deny = BigInt(deny);
} }
@ -84,7 +86,7 @@ class Permissions {
}, },
{ {
name: "MANAGE_MESSAGES", name: "MANAGE_MESSAGES",
readableName: "Manager messages", readableName: "Manage messages",
description: "Allows the user to delete messages that aren't their own" description: "Allows the user to delete messages that aren't their own"
}, },
{ {
@ -242,5 +244,23 @@ class Permissions {
return 0; return 0;
} }
} }
setPermision(name, setto) {
const bit = Permissions.map[name];
if (setto === 0) {
this.deny = this.setPermisionbit(bit, false, this.deny);
this.allow = this.setPermisionbit(bit, false, this.allow);
}
else if (setto === 1) {
this.deny = this.setPermisionbit(bit, false, this.deny);
this.allow = this.setPermisionbit(bit, true, this.allow);
}
else if (setto === -1) {
this.deny = this.setPermisionbit(bit, true, this.deny);
this.allow = this.setPermisionbit(bit, false, this.allow);
}
else {
console.error("invalid number entered:" + setto);
}
}
} }
Permissions.makeMap(); Permissions.makeMap();

View file

@ -5,7 +5,16 @@ class Role {
owner; owner;
color; color;
id; id;
name;
info;
hoist;
icon;
mentionable;
unicode_emoji;
headers;
constructor(JSON, owner) { constructor(JSON, owner) {
this.headers = owner.headers;
this.info = owner.info;
for (const thing of Object.keys(JSON)) { for (const thing of Object.keys(JSON)) {
this[thing] = JSON[thing]; this[thing] = JSON[thing];
} }

254
.dist/settings.js Normal file
View file

@ -0,0 +1,254 @@
import { Permissions } from "./permissions.js";
class Buttons {
name;
buttons;
bigtable;
warndiv;
constructor(name) {
this.buttons = [];
this.name = name;
}
add(name, thing = undefined) {
if (!thing) {
thing = new Options(name, this);
}
this.buttons.push([name, thing]);
return thing;
}
generateHTML() {
const bigtable = document.createElement("div");
bigtable.classList.add("Buttons");
bigtable.classList.add("flexltr");
this.bigtable = bigtable;
const htmlarea = document.createElement("div");
const buttonTable = document.createElement("div");
buttonTable.classList.add("flexttb", "settingbuttons");
for (const thing of this.buttons) {
const button = document.createElement("button");
button.classList.add("SettingsButton");
button.textContent = thing[0];
button.onclick = _ => {
this.generateHTMLArea(thing[1], htmlarea);
if (this.warndiv) {
this.warndiv.remove();
}
};
buttonTable.append(button);
}
this.generateHTMLArea(this.buttons[0][1], htmlarea);
bigtable.append(buttonTable);
bigtable.append(htmlarea);
return bigtable;
}
handleString(str) {
const div = document.createElement("div");
div.textContent = str;
return div;
}
generateHTMLArea(genation, htmlarea) {
let html;
if (genation instanceof Options) {
html = genation.generateHTML();
}
else {
html = this.handleString(genation);
}
htmlarea.innerHTML = "";
htmlarea.append(html);
}
changed(html) {
this.warndiv = html;
this.bigtable.append(html);
}
save() { }
}
class PermisionToggle {
rolejson;
permissions;
owner;
constructor(roleJSON, permissions, owner) {
this.rolejson = roleJSON;
this.permissions = permissions;
this.owner = owner;
}
generateHTML() {
const div = document.createElement("div");
div.classList.add("setting");
const name = document.createElement("span");
name.textContent = this.rolejson.readableName;
name.classList.add("settingsname");
div.append(name);
div.append(this.generateCheckbox());
const p = document.createElement("p");
p.innerText = this.rolejson.description;
div.appendChild(p);
return div;
}
generateCheckbox() {
const div = document.createElement("div");
div.classList.add("tritoggle");
const state = this.permissions.getPermision(this.rolejson.name);
const on = document.createElement("input");
on.type = "radio";
on.name = this.rolejson.name;
div.append(on);
if (state === 1) {
on.checked = true;
}
;
on.onclick = _ => {
this.permissions.setPermision(this.rolejson.name, 1);
this.owner.changed();
};
const no = document.createElement("input");
no.type = "radio";
no.name = this.rolejson.name;
div.append(no);
if (state === 0) {
no.checked = true;
}
;
no.onclick = _ => {
this.permissions.setPermision(this.rolejson.name, 0);
this.owner.changed();
};
if (this.permissions.hasDeny) {
const off = document.createElement("input");
off.type = "radio";
off.name = this.rolejson.name;
div.append(off);
if (state === -1) {
off.checked = true;
}
;
off.onclick = _ => {
this.permissions.setPermision(this.rolejson.name, -1);
this.owner.changed();
};
}
return div;
}
}
class RoleList extends Buttons {
permissions;
permission;
guild;
channel;
options;
onchange;
curid;
constructor(permissions, guild, onchange, channel = false) {
super("Roles");
this.guild = guild;
this.permissions = permissions;
this.channel = channel;
this.onchange = onchange;
const options = new Options("", this);
if (channel) {
this.permission = new Permissions("0", "0");
}
else {
this.permission = new Permissions("0");
}
for (const thing of Permissions.info) {
options.addPermisionToggle(thing, this.permission); //
}
for (const i of permissions) {
this.buttons.push([guild.getRole(i[0]).name, i[0]]); //
}
this.options = options;
}
handleString(str) {
this.curid = str;
const perm = this.permissions.find(_ => _[0] === str)[1];
this.permission.deny = perm.deny;
this.permission.allow = perm.allow;
this.options.name = this.guild.getRole(str).name;
this.options.haschanged = false;
return this.options.generateHTML();
}
save() {
this.onchange(this.curid, this.permission);
}
}
class Options {
name;
haschanged = false;
options;
owner;
constructor(name, owner) {
this.name = name;
this.options = [];
this.owner = owner;
}
addPermisionToggle(roleJSON, permissions) {
this.options.push(new PermisionToggle(roleJSON, permissions, this));
}
generateHTML() {
const div = document.createElement("div");
div.classList.add("titlediv");
const title = document.createElement("h2");
title.textContent = this.name;
div.append(title);
title.classList.add("settingstitle");
const table = document.createElement("div");
table.classList.add("flexttb", "flexspace");
for (const thing of this.options) {
table.append(thing.generateHTML());
}
div.append(table);
return div;
}
changed() {
if (!this.haschanged) {
const div = document.createElement("div");
div.classList.add("flexltr", "savediv");
const span = document.createElement("span");
div.append(span);
span.textContent = "Careful, you have unsaved changes";
const button = document.createElement("button");
button.textContent = "Save changes";
div.append(button);
this.haschanged = true;
this.owner.changed(div);
button.onclick = _ => {
this.owner.save();
div.remove();
};
}
}
}
class Settings extends Buttons {
static Buttons = Buttons;
static Options = Options;
html;
constructor(name) {
super(name);
}
addButton(name) {
const options = new Options(name, this);
this.add(name, options);
return options;
}
show() {
const background = document.createElement("div");
background.classList.add("background");
const title = document.createElement("h2");
title.textContent = this.name;
title.classList.add("settingstitle");
background.append(title);
background.append(this.generateHTML());
const exit = document.createElement("span");
exit.textContent = "✖";
exit.classList.add("exitsettings");
background.append(exit);
exit.onclick = _ => { this.hide(); };
document.body.append(background);
this.html = background;
}
hide() {
this.html.remove();
this.html = null;
}
}
export { Settings, RoleList };

View file

@ -13,6 +13,18 @@ class User {
discriminator; discriminator;
pronouns; pronouns;
bot; bot;
static contextmenu = new Contextmenu("User Menu");
static setUpContextMenu() {
this.contextmenu.addbutton("Copy user id", function () {
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user", function () {
fetch(this.info.api.toString() + "/v9/users/@me/channels", { method: "POST",
body: JSON.stringify({ "recipients": [this.id] }),
headers: this.headers
});
});
}
static checkuser(userjson, owner) { static checkuser(userjson, owner) {
if (User.userids[userjson.id]) { if (User.userids[userjson.id]) {
return User.userids[userjson.id]; return User.userids[userjson.id];
@ -60,6 +72,21 @@ class User {
this.changepfp(json.avatar); this.changepfp(json.avatar);
} }
} }
bind(html, guild = null) {
if (guild && guild.id !== "@me") {
Member.resolve(this, guild).then(_ => {
_.bind(html);
}).catch(_ => {
console.log(_);
});
}
this.profileclick(html);
User.contextmenu.bind(html, this);
}
static async resolve(id, localuser) {
const json = await fetch(localuser.info.api.toString() + "/v9/users/" + id + "/profile", { headers: localuser.headers }).then(_ => _.json());
return new User(json, localuser);
}
changepfp(update) { changepfp(update) {
this.avatar = update; this.avatar = update;
this.hypotheticalpfp = false; this.hypotheticalpfp = false;
@ -137,4 +164,5 @@ class User {
}; };
} }
} }
User.setUpContextMenu();
export { User }; export { User };

View file

@ -7,7 +7,9 @@ import {markdown} from "./markdown.js";
import {Guild} from "./guild.js"; import {Guild} from "./guild.js";
import { Localuser } from "./localuser.js"; import { Localuser } from "./localuser.js";
import { Permissions } from "./permissions.js"; import { Permissions } from "./permissions.js";
import { Settings, RoleList } from "./settings.js";
import { Role } from "./role.js";
Settings;
declare global { declare global {
interface NotificationOptions { interface NotificationOptions {
image?: string image?: string
@ -27,6 +29,7 @@ class Channel{
guild_id:string; guild_id:string;
messageids:{[key : string]:Message}; messageids:{[key : string]:Message};
permission_overwrites:{[key:string]:Permissions}; permission_overwrites:{[key:string]:Permissions};
permission_overwritesar:[string,Permissions][]
topic:string; topic:string;
nsfw:boolean; nsfw:boolean;
position:number; position:number;
@ -41,25 +44,44 @@ class Channel{
static contextmenu=new Contextmenu("channel menu"); static contextmenu=new Contextmenu("channel menu");
replyingto:Message; replyingto:Message;
static setupcontextmenu(){ static setupcontextmenu(){
Channel.contextmenu.addbutton("Copy channel id",function(){ this.contextmenu.addbutton("Copy channel id",function(){
console.log(this) console.log(this)
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Channel.contextmenu.addbutton("Mark as read",function(){ this.contextmenu.addbutton("Mark as read",function(){
console.log(this) console.log(this)
this.readbottom(); this.readbottom();
}); });
Channel.contextmenu.addbutton("Delete channel",function(){ this.contextmenu.addbutton("Settings[temp]",function(){
this.generateSettings();
});
this.contextmenu.addbutton("Delete channel",function(){
console.log(this) console.log(this)
this.deleteChannel(); this.deleteChannel();
},null,_=>{console.log(_);return _.isAdmin()}); },null,_=>{console.log(_);return _.isAdmin()});
Channel.contextmenu.addbutton("Edit channel",function(){ this.contextmenu.addbutton("Edit channel",function(){
this.editChannel(this); this.editChannel(this);
},null,_=>{return _.isAdmin()}); },null,_=>{return _.isAdmin()});
} }
generateSettings(){
this.sortPerms();
const settings=new Settings("Settings for "+this.name);
const s1=settings.addButton("roles");
s1.options.push(new RoleList(this.permission_overwritesar,this.guild,this.updateRolePermissions.bind(this),true))
settings.show();
}
sortPerms(){
this.permission_overwritesar.sort((a,b)=>{
const order=this.guild.roles.findIndex(_=>_.id===a[0])-this.guild.roles.findIndex(_=>_.id===b[0]);
return order;
})
}
constructor(JSON,owner:Guild){ constructor(JSON,owner:Guild){
if(JSON===-1){ if(JSON===-1){
@ -78,9 +100,14 @@ class Channel{
this.guild_id=JSON.guild_id; this.guild_id=JSON.guild_id;
this.messageids={}; this.messageids={};
this.permission_overwrites={}; this.permission_overwrites={};
this.permission_overwritesar=[];
for(const thing of JSON.permission_overwrites){ for(const thing of JSON.permission_overwrites){
console.log(thing);
if(thing.id==="1182819038095799904"||thing.id==="1182820803700625444"){continue;};
this.permission_overwrites[thing.id]=new Permissions(thing.allow,thing.deny); this.permission_overwrites[thing.id]=new Permissions(thing.allow,thing.deny);
this.permission_overwritesar.push([thing.id,this.permission_overwrites[thing.id]]);
} }
this.topic=JSON.topic; this.topic=JSON.topic;
this.nsfw=JSON.nsfw; this.nsfw=JSON.nsfw;
this.position=JSON.position; this.position=JSON.position;
@ -127,6 +154,9 @@ class Channel{
return false; return false;
} }
get canMessage():boolean{ get canMessage():boolean{
if((0===this.permission_overwritesar.length)&&this.hasPermission("MANAGE_CHANNELS")){
this.addRoleToPerms(this.guild.roles.find(_=>_.name==="@everyone"));
}
return this.hasPermission("SEND_MESSAGES"); return this.hasPermission("SEND_MESSAGES");
} }
sortchildren(){ sortchildren(){
@ -717,6 +747,36 @@ class Channel{
}); });
} }
} }
async addRoleToPerms(role:Role){
await fetch(this.info.api.toString()+"/channels/"+this.id+"/permissions/"+role.id,{
method:"PUT",
headers:this.headers,
body:JSON.stringify({
allow:"0",
deny:"0",
id:role.id,
type:0
})
})
const perm=new Permissions("0","0");
this.permission_overwrites[role.id]=perm;
this.permission_overwritesar.push([role.id,perm]);
}
async updateRolePermissions(id:string,perms:Permissions){
const permision=this.permission_overwrites[id];
permision.allow=perms.allow;
permision.deny=perms.deny;
await fetch(this.info.api.toString()+"/channels/"+this.id+"/permissions/"+id,{
method:"PUT",
headers:this.headers,
body:JSON.stringify({
allow:permision.allow.toString(),
deny:permision.deny.toString(),
id:id,
type:0
})
})
}
} }
Channel.setupcontextmenu(); Channel.setupcontextmenu();
export {Channel}; export {Channel};

View file

@ -4,7 +4,8 @@ import {Contextmenu} from "./contextmenu.js";
import {Role} from "./role.js"; import {Role} from "./role.js";
import {Fullscreen} from "./fullscreen.js"; import {Fullscreen} from "./fullscreen.js";
import {Member} from "./member.js"; import {Member} from "./member.js";
import {Settings,RoleList} from "./settings.js";
import {Permissions} from "./permissions.js";
class Guild{ class Guild{
owner:Localuser; owner:Localuser;
headers:Localuser["headers"]; headers:Localuser["headers"];
@ -49,6 +50,9 @@ class Guild{
Guild.contextmenu.addbutton("Create invite",function(){ Guild.contextmenu.addbutton("Create invite",function(){
console.log(this); console.log(this);
},null,_=>true,_=>false); },null,_=>true,_=>false);
Guild.contextmenu.addbutton("Settings[temp]",function(){
this.generateSettings();
});
/* -----things left for later----- /* -----things left for later-----
guild.contextmenu.addbutton("Leave Guild",function(){ guild.contextmenu.addbutton("Leave Guild",function(){
console.log(this) console.log(this)
@ -60,6 +64,17 @@ class Guild{
},null,_=>{return thisuser.isAdmin()}) },null,_=>{return thisuser.isAdmin()})
*/ */
} }
generateSettings(){
const settings=new Settings("Settings for "+this.properties.name);
const s1=settings.addButton("roles");
const permlist=[];
for(const thing of this.roles){
permlist.push([thing.id,thing.permissions]);
}
s1.options.push(new RoleList(permlist,this,this.updateRolePermissions.bind(this)));
settings.show();
}
constructor(JSON,owner:Localuser,member){ constructor(JSON,owner:Localuser,member){
if(JSON===-1){ if(JSON===-1){
@ -92,6 +107,7 @@ class Guild{
this.headchannels.push(thing); this.headchannels.push(thing);
} }
} }
} }
notisetting(settings){ notisetting(settings){
this.message_notifications=settings.message_notifications; this.message_notifications=settings.message_notifications;
@ -489,6 +505,41 @@ class Guild{
body:JSON.stringify({name: name, type: type}) body:JSON.stringify({name: name, type: type})
}) })
} }
async createRole(name:string){
const fetched=await fetch(this.info.api.toString()+"/guilds/"+this.id+"roles",{
method:"POST",
headers:this.headers,
body:JSON.stringify({
name:name,
color:0,
permissions:"0"
})
})
const json=await fetched.json();
const role=new Role(json,this);
this.roleids[role.id]=role;
this.roles.push(role);
return role;
}
async updateRolePermissions(id:string,perms:Permissions){
const role=this.roleids[id];
role.permissions.allow=perms.allow;
role.permissions.deny=perms.deny;
await fetch(this.info.api.toString()+"/guilds/"+this.id+"/roles/"+this.id,{
method:"PATCH",
headers:this.headers,
body:JSON.stringify({
color:role.color,
hoist:role.hoist,
icon:role.icon,
mentionable:role.mentionable,
name:role.name,
permissions:role.permissions.allow.toString(),
unicode_emoji:role.unicode_emoji,
})
})
}
} }
Guild.setupcontextmenu(); Guild.setupcontextmenu();
export { Guild }; export { Guild };

View file

@ -13,51 +13,40 @@
<script src="/index.js" type="module"></script> <script src="/index.js" type="module"></script>
<div id="loading" class="loading"><div id="centerdiv"><img src="/bitmap.svg" width="1in" height="1in"><h1>Jank Client is loading</h1><h2>This shouldn't take long</h2></div></div> <div id="loading" class="loading"><div id="centerdiv"><img src="/bitmap.svg" width="1in" height="1in"><h1>Jank Client is loading</h1><h2>This shouldn't take long</h2></div></div>
<table id="page" cellspacing="0" cellpadding="0"> <div class="flexltr" id="page">
<tr> <div id="neunence">
<td rowspan="2" id="neunence">
<div id="servers"></div> <div id="servers"></div>
</td> </div>
<td class="servertd" id="servertd"> <div class="flexttb channelflex">
<div class="servertd" id="servertd">
<h2 id="serverName">Server Name</h2> <h2 id="serverName">Server Name</h2>
</td> </div>
<td colspan="2" class="servertd servernamediv">
<span id="mobileback" hidden=true></span><span id="channelname">Channel</span>
</td>
</tr>
<tr>
<td id="channels-td">
<div id="channels"></div> <div id="channels"></div>
<table id="userdock"> <div class="flexltr" id="userdock">
<tr> <div class="flexltr" id="userinfo">
<td>
<table id="userinfo"><tr>
<td>
<img id="userpfp" class="pfp"> <img id="userpfp" class="pfp">
</td>
<td> <div class="userflex">
<div>
<p id="username">USERNAME</p> <p id="username">USERNAME</p>
<p id="status">SATUS</p> <p id="status">SATUS</p>
</div> </div>
</td>
</tr></table></td>
<td>
<h2 id="settings"></h2>
</td>
</tr>
</table>
</div> </div>
</td> <h2 id="settings"></h2>
<td id="channelw"> </div>
</div>
<div class="flexttb messageflex">
<div class="servertd servernamediv">
<span id="mobileback" hidden=true></span><span id="channelname">Channel</span>
</div>
<div id="channelw">
<div id="messagecontainer"> <div id="messagecontainer">
<div id="messages"> <div id="messages">
</div> </div>
</div> </div>
</div>
<div id="pasteimage"></div> <div id="pasteimage"></div>
<div id="typediv">
<div id="replybox" class="hideReplyBox"></div> <div id="replybox" class="hideReplyBox"></div>
<div id="typediv">
<textarea id="typebox"></textarea> <textarea id="typebox"></textarea>
<div id="typing" class="hidden"> <div id="typing" class="hidden">
<p id="typingtext">typing</p> <p id="typingtext">typing</p>
@ -68,10 +57,7 @@
</div> </div>
</div> </div>
</div> </div>
</td> </div>
<td id="extra"> </div>
</td>
</tr>
</table>
</body> </body>
</html> </html>

View file

@ -1,6 +1,7 @@
import { Localuser } from "./localuser.js"; import { Localuser } from "./localuser.js";
import {Contextmenu} from "./contextmenu.js"; import {Contextmenu} from "./contextmenu.js";
import {mobile, getBulkUsers,setTheme, Specialuser} from "./login.js"; import {mobile, getBulkUsers,setTheme, Specialuser} from "./login.js";
async function waitforload(){ async function waitforload(){
let res let res
new Promise(r=>{res=r}); new Promise(r=>{res=r});
@ -11,17 +12,7 @@ async function waitforload(){
} }
await waitforload(); await waitforload();
function setDynamicHeight() {
var servertdHeight = document.getElementById('servertd').offsetHeight+document.getElementById('typediv').offsetHeight+document.getElementById('pasteimage').offsetHeight;
document.documentElement.style.setProperty('--servertd-height', servertdHeight + 'px');
}
const resizeObserver = new ResizeObserver(() => {
setDynamicHeight();
});
resizeObserver.observe(document.getElementById('servertd'));
resizeObserver.observe(document.getElementById('replybox'));
resizeObserver.observe(document.getElementById('pasteimage'));
setDynamicHeight();
const users=getBulkUsers(); const users=getBulkUsers();
if(!users.currentuser){ if(!users.currentuser){
@ -109,7 +100,7 @@ thisuser.initwebsocket().then(_=>{
} }
Contextmenu.currentmenu=table; Contextmenu.currentmenu=table;
console.log(table); console.log(table);
userdock.append(table); userdock.before(table);
event.stopImmediatePropagation(); event.stopImmediatePropagation();
}) })
} }

View file

@ -1,12 +1,27 @@
import {User} from "./user.js"; import {User} from "./user.js";
import {Role} from "./role.js"; import {Role} from "./role.js";
import {Guild} from "./guild.js"; import {Guild} from "./guild.js";
import { Contextmenu } from "./contextmenu.js";
class Member{ class Member{
static already={}; static already={};
owner:Guild; owner:Guild;
user:User; user:User;
roles:Role[]; roles:Role[];
error:boolean; error:boolean;
static contextmenu:Contextmenu=new Contextmenu("User Menu");
static setUpContextMenu(){
this.contextmenu.addbutton("Copy user id",function(){
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user",function(){
fetch(this.info.api.toString()+"/v9/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
headers: this.headers
});
});
}
constructor(memberjson,owner:Guild,error=false){ constructor(memberjson,owner:Guild,error=false){
this.error=error; this.error=error;
this.owner=owner; this.owner=owner;
@ -45,38 +60,42 @@ class Member{
get info(){ get info(){
return this.owner.info; return this.owner.info;
} }
static async resolve(unkown:User|object,guild:Guild):Promise<Member>{ static async resolve(unkown:User|object|string,guild:Guild):Promise<Member>{
if(!(guild instanceof Guild)){ if(!(guild instanceof Guild)){
console.error(guild) console.error(guild)
} }
let user:User; let user:User;
let id="";
if(unkown instanceof User){ if(unkown instanceof User){
user=unkown as User; user=unkown as User;
id=user.id;
}else if(typeof unkown===typeof ""){
id=unkown as string;
}else{ }else{
return new Member(unkown,guild); return new Member(unkown,guild);
} }
if(guild.id==="@me"){return null} if(guild.id==="@me"){return null}
if(!Member.already[guild.id]){ if(!Member.already[guild.id]){
Member.already[guild.id]={}; Member.already[guild.id]={};
}else if(Member.already[guild.id][user.id]){ }else if(Member.already[guild.id][id]){
const memb=Member.already[guild.id][user.id] const memb=Member.already[guild.id][id]
if(memb instanceof Promise){ if(memb instanceof Promise){
return await memb; return await memb;
} }
return memb; return memb;
} }
const promoise= fetch(guild.info.api.toString()+"/v9/users/"+user.id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.id,{headers:guild.headers}).then(_=>_.json()).then(json=>{ const promoise= fetch(guild.info.api.toString()+"/v9/users/"+id+"/profile?with_mutual_guilds=true&with_mutual_friends_count=true&guild_id="+guild.id,{headers:guild.headers}).then(_=>_.json()).then(json=>{
const memb=new Member(json,guild); const memb=new Member(json,guild);
Member.already[guild.id][user.id]=memb; Member.already[guild.id][id]=memb;
console.log("resolved") console.log("resolved")
return memb return memb
}) })
Member.already[guild.id][user.id]=promoise; Member.already[guild.id][id]=promoise;
try{ try{
return await promoise return await promoise
}catch(_){ }catch(_){
const memb=new Member(user,guild,true); const memb=new Member(user,guild,true);
Member.already[guild.id][user.id]=memb; Member.already[guild.id][id]=memb;
return memb; return memb;
} }
} }
@ -105,7 +124,27 @@ class Member{
} }
} }
return this.guild.properties.owner_id===this.user.id; return this.guild.properties.owner_id===this.user.id;
}
bind(html:HTMLElement){
if(html.tagName==="SPAN"){
if(!this) {return};
console.log(this.error);
if(this.error){
const error=document.createElement("span");
error.textContent="!";
error.classList.add("membererror");
html.after(error);
return;
}
html.style.color=this.getColor();
}
this.profileclick(html);
Member.contextmenu.bind(html);
}
profileclick(html:HTMLElement){
//to be implemented
} }
} }
Member.setUpContextMenu();
export {Member}; export {Member};

View file

@ -46,16 +46,6 @@ class Message{
Message.contextmenu.addbutton("Copy message id",function(){ Message.contextmenu.addbutton("Copy message id",function(){
navigator.clipboard.writeText(this.id); navigator.clipboard.writeText(this.id);
}); });
Message.contextmenu.addbutton("Copy user id",function(){
navigator.clipboard.writeText(this.author.id);
});
Message.contextmenu.addbutton("Message user",function(){
fetch(this.info.api.toString()+"/v9/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.author.id]}),
headers: this.headers
});
})
Message.contextmenu.addbutton("Edit",function(){ Message.contextmenu.addbutton("Edit",function(){
this.channel.editing=this; this.channel.editing=this;
(document.getElementById("typebox") as HTMLInputElement).value=this.content; (document.getElementById("typebox") as HTMLInputElement).value=this.content;
@ -179,7 +169,9 @@ class Message{
replyline.appendChild(username); replyline.appendChild(username);
const reply=document.createElement("div"); const reply=document.createElement("div");
username.classList.add("username"); username.classList.add("username");
this.author.bind(username,this.guild);
/*
Member.resolve(this.author,this.guild).then(_=>{ Member.resolve(this.author,this.guild).then(_=>{
if(!_) {return}; if(!_) {return};
console.log(_.error); console.log(_.error);
@ -197,6 +189,7 @@ class Message{
}).catch(_=>{ }).catch(_=>{
console.log(_) console.log(_)
}); });
*/
reply.classList.add("replytext"); reply.classList.add("replytext");
replyline.appendChild(reply); replyline.appendChild(reply);
@ -209,9 +202,9 @@ class Message{
const author=message.author; const author=message.author;
reply.appendChild(markdown(message.content,{stdsize:true})); reply.appendChild(markdown(message.content,{stdsize:true}));
minipfp.src=author.getpfpsrc() minipfp.src=author.getpfpsrc()
author.profileclick(minipfp) author.bind(minipfp);
username.textContent=author.username; username.textContent=author.username;
author.profileclick(username) author.bind(username);
}); });
div.appendChild(replyline); div.appendChild(replyline);
} }
@ -234,7 +227,7 @@ class Message{
const combine=(premessage?.author?.id!=this.author.id)||(current)||this.message_reference const combine=(premessage?.author?.id!=this.author.id)||(current)||this.message_reference
if(combine){ if(combine){
const pfp=this.author.buildpfp(); const pfp=this.author.buildpfp();
this.author.profileclick(pfp); this.author.bind(pfp);
pfpRow.appendChild(pfp); pfpRow.appendChild(pfp);
}else{ }else{
div["pfpparent"]=pfpparent; div["pfpparent"]=pfpparent;
@ -249,19 +242,8 @@ class Message{
if(combine){ if(combine){
const username=document.createElement("span"); const username=document.createElement("span");
username.classList.add("username") username.classList.add("username")
this.author.profileclick(username); this.author.bind(username,this.guild);
Member.resolve(this.author,this.guild).then(_=>{
if(!_) {return};
if(_.error){
const error=document.createElement("span");
error.textContent="!";
error.classList.add("membererror");
username.after(error);
return;
}
username.style.color=_.getColor();
})
username.textContent=this.author.username; username.textContent=this.author.username;
const userwrap=document.createElement("tr") const userwrap=document.createElement("tr")
userwrap.appendChild(username) userwrap.appendChild(username)

View file

@ -2,7 +2,9 @@ export {Permissions};
class Permissions{ class Permissions{
allow:bigint; allow:bigint;
deny:bigint; deny:bigint;
readonly hasDeny:boolean;
constructor(allow:string,deny:string=""){ constructor(allow:string,deny:string=""){
this.hasDeny=!!deny;
this.allow=BigInt(allow); this.allow=BigInt(allow);
this.deny=BigInt(deny); this.deny=BigInt(deny);
} }
@ -86,7 +88,7 @@ class Permissions{
}, },
{ {
name:"MANAGE_MESSAGES", name:"MANAGE_MESSAGES",
readableName:"Manager messages", readableName:"Manage messages",
description:"Allows the user to delete messages that aren't their own" description:"Allows the user to delete messages that aren't their own"
}, },
{ {
@ -242,5 +244,22 @@ class Permissions{
return 0; return 0;
} }
} }
setPermision(name:string,setto:number):void{
const bit=Permissions.map[name] as number;
if(setto===0){
this.deny=this.setPermisionbit(bit,false,this.deny);
this.allow=this.setPermisionbit(bit,false,this.allow);
}else if(setto===1){
this.deny=this.setPermisionbit(bit,false,this.deny);
this.allow=this.setPermisionbit(bit,true,this.allow);
}else if(setto===-1){
this.deny=this.setPermisionbit(bit,true,this.deny);
this.allow=this.setPermisionbit(bit,false,this.allow);
}else{
console.error("invalid number entered:"+setto);
}
}
} }
Permissions.makeMap(); Permissions.makeMap();

View file

@ -7,7 +7,16 @@ class Role{
owner:Guild; owner:Guild;
color:number; color:number;
id:string; id:string;
name:string;
info:Guild["info"];
hoist:boolean;
icon:string;
mentionable:boolean;
unicode_emoji:string;
headers:Guild["headers"];
constructor(JSON, owner:Guild){ constructor(JSON, owner:Guild){
this.headers=owner.headers;
this.info=owner.info;
for(const thing of Object.keys(JSON)){ for(const thing of Object.keys(JSON)){
this[thing]=JSON[thing]; this[thing]=JSON[thing];
} }
@ -24,5 +33,4 @@ class Role{
if(this.color===0){return null}; if(this.color===0){return null};
return `#${this.color.toString(16)}`; return `#${this.color.toString(16)}`;
} }
} }

263
webpage/settings.ts Normal file
View file

@ -0,0 +1,263 @@
import { Permissions } from "./permissions.js";
import { Guild } from "./guild.js";
class Buttons{
readonly name:string;
readonly buttons:[string,Options|string][];
bigtable:HTMLDivElement;
warndiv:HTMLElement;
constructor(name:string){
this.buttons=[];
this.name=name;
}
add(name:string,thing:Options=undefined){
if(!thing){thing=new Options(name,this)}
this.buttons.push([name,thing]);
return thing;
}
generateHTML(){
const bigtable=document.createElement("div");
bigtable.classList.add("Buttons");
bigtable.classList.add("flexltr");
this.bigtable=bigtable;
const htmlarea=document.createElement("div");
const buttonTable=document.createElement("div");
buttonTable.classList.add("flexttb","settingbuttons");
for(const thing of this.buttons){
const button=document.createElement("button");
button.classList.add("SettingsButton");
button.textContent=thing[0];
button.onclick=_=>{
this.generateHTMLArea(thing[1],htmlarea);
if(this.warndiv){
this.warndiv.remove();
}
}
buttonTable.append(button);
}
this.generateHTMLArea(this.buttons[0][1],htmlarea);
bigtable.append(buttonTable);
bigtable.append(htmlarea);
return bigtable;
}
handleString(str:string):HTMLElement{
const div=document.createElement("div");
div.textContent=str;
return div;
}
private generateHTMLArea(genation:Options|string,htmlarea:HTMLElement){
let html:HTMLElement;
if(genation instanceof Options){
html=genation.generateHTML();
}else{
html=this.handleString(genation);
}
htmlarea.innerHTML="";
htmlarea.append(html);
}
changed(html:HTMLElement){
this.warndiv=html;
this.bigtable.append(html);
}
save(){}
}
class PermisionToggle{
readonly rolejson:{name:string,readableName:string,description:string};
permissions:Permissions;
owner:Options;
constructor(roleJSON:PermisionToggle["rolejson"],permissions:Permissions,owner:Options){
this.rolejson=roleJSON;
this.permissions=permissions;
this.owner=owner;
}
generateHTML():HTMLElement{
const div=document.createElement("div");
div.classList.add("setting");
const name=document.createElement("span");
name.textContent=this.rolejson.readableName;
name.classList.add("settingsname");
div.append(name);
div.append(this.generateCheckbox());
const p=document.createElement("p");
p.innerText=this.rolejson.description;
div.appendChild(p);
return div;
}
generateCheckbox():HTMLElement{
const div=document.createElement("div");
div.classList.add("tritoggle");
const state=this.permissions.getPermision(this.rolejson.name);
const on=document.createElement("input");
on.type="radio";
on.name=this.rolejson.name;
div.append(on);
if(state===1){on.checked=true;};
on.onclick=_=>{
this.permissions.setPermision(this.rolejson.name,1);
this.owner.changed();
}
const no=document.createElement("input");
no.type="radio";
no.name=this.rolejson.name;
div.append(no);
if(state===0){no.checked=true;};
no.onclick=_=>{
this.permissions.setPermision(this.rolejson.name,0);
this.owner.changed();
}
if(this.permissions.hasDeny){
const off=document.createElement("input");
off.type="radio";
off.name=this.rolejson.name;
div.append(off);
if(state===-1){off.checked=true;};
off.onclick=_=>{
this.permissions.setPermision(this.rolejson.name,-1);
this.owner.changed();
}
}
return div;
}
}
class RoleList extends Buttons{
readonly permissions:[string,Permissions][];
permission:Permissions;
readonly guild:Guild;
readonly channel:boolean;
readonly declare buttons:[string,string][];
readonly options:Options;
onchange:Function;
curid:string;
constructor(permissions:[string,Permissions][],guild:Guild,onchange:Function,channel=false){
super("Roles");
this.guild=guild;
this.permissions=permissions;
this.channel=channel;
this.onchange=onchange;
const options=new Options("",this);
if(channel){
this.permission=new Permissions("0","0");
}else{
this.permission=new Permissions("0");
}
for(const thing of Permissions.info){
options.addPermisionToggle(thing,this.permission);//
}
for(const i of permissions){
this.buttons.push([guild.getRole(i[0]).name,i[0]])//
}
this.options=options;
}
handleString(str:string):HTMLElement{
this.curid=str;
const perm=this.permissions.find(_=>_[0]===str)[1];
this.permission.deny=perm.deny;
this.permission.allow=perm.allow;
this.options.name=this.guild.getRole(str).name;
this.options.haschanged=false;
return this.options.generateHTML();
}
save(){
this.onchange(this.curid,this.permission);
}
}
class Options{
name:string;
haschanged=false;
readonly options:(PermisionToggle|Buttons|RoleList)[];
readonly owner:Buttons;
constructor(name:string,owner:Buttons){
this.name=name;
this.options=[];
this.owner=owner;
}
addPermisionToggle(roleJSON:PermisionToggle["rolejson"],permissions:Permissions){
this.options.push(new PermisionToggle(roleJSON,permissions,this));
}
generateHTML():HTMLElement{
const div=document.createElement("div");
div.classList.add("titlediv");
const title=document.createElement("h2");
title.textContent=this.name;
div.append(title);
title.classList.add("settingstitle")
const table=document.createElement("div");
table.classList.add("flexttb","flexspace");
for(const thing of this.options){
table.append(thing.generateHTML());
}
div.append(table);
return div;
}
changed(){
if(!this.haschanged){
const div=document.createElement("div");
div.classList.add("flexltr","savediv");
const span=document.createElement("span");
div.append(span);
span.textContent="Careful, you have unsaved changes";
const button=document.createElement("button");
button.textContent="Save changes";
div.append(button);
this.haschanged=true;
this.owner.changed(div);
button.onclick=_=>{
this.owner.save();
div.remove();
}
}
}
}
class Settings extends Buttons{
static readonly Buttons=Buttons;
static readonly Options=Options;
html:HTMLElement;
constructor(name:string){
super(name);
}
addButton(name:string):Options{
const options=new Options(name,this);
this.add(name,options);
return options;
}
show(){
const background=document.createElement("div");
background.classList.add("background");
const title=document.createElement("h2");
title.textContent=this.name;
title.classList.add("settingstitle")
background.append(title);
background.append(this.generateHTML());
const exit=document.createElement("span");
exit.textContent="✖";
exit.classList.add("exitsettings");
background.append(exit);
exit.onclick=_=>{this.hide();};
document.body.append(background);
this.html=background;
}
hide(){
this.html.remove();
this.html=null;
}
}
export {Settings,RoleList}

View file

@ -5,6 +5,7 @@ body {
color: var(--primary-text); color: var(--primary-text);
overflow-y: hidden; overflow-y: hidden;
overflow-x: hidden; overflow-x: hidden;
height:100%;
margin: 0; margin: 0;
padding: 0; padding: 0;
} }
@ -52,10 +53,13 @@ th {
.background { .background {
position: absolute; position: absolute;
width: 100%; width: 100%;
height: 100%; height: 100vh;
top: 0; top: 0;
background-color: #000000bf; background-color: #000000bf;
z-index: 11; z-index: 11;
max-height: 100%;
display: flex;
flex-direction: column;
} }
.messagediv:hover { .messagediv:hover {
@ -177,7 +181,7 @@ h2 {
#neunence { #neunence {
vertical-align: top; vertical-align: top;
overflow: auto; overflow: auto;
height: 9dvh; flex-shrink: 0;
} }
#servers { #servers {
@ -235,14 +239,16 @@ img {
#messagecontainer { #messagecontainer {
overflow: auto; overflow: auto;
height: 100%; display: flex;
width: 100%;
display: inline-block;
max-width: 100%; max-width: 100%;
flex-shrink: 1;
width: 100%;
/* flex-grow: 1; */
} }
#messages { #messages {
max-width: 100%; max-width: 100%;
/* height: 100%; */
} }
p { p {
@ -253,14 +259,17 @@ p {
} }
#channels { #channels {
--top-height: 28px;
background-color: var(--channels-bg); background-color: var(--channels-bg);
position: absolute;
top: var(--top-height); top: var(--top-height);
height: calc(100dvh - 53px - var(--top-height));
width: 2.5in;
overflow: auto; overflow: auto;
user-select: none; user-select: none;
flex-grow: 1;
/* display: flex; */
flex-shrink: 1;
min-height: 0px;
min-width: 0px;
width: 2.5in;
/* height: 100%; */
} }
#userdock { #userdock {
@ -308,10 +317,12 @@ div {
font-size: 16px; font-size: 16px;
padding: 3px; padding: 3px;
border-radius: .25cm; border-radius: .25cm;
width: 99%; width: 100%;
height: .5in; height: .5in;
z-index: -100; z-index: -100;
max-width: 99%; max-width: 99%;
display: flex;
max-height: fit-content;
} }
p { p {
@ -369,6 +380,7 @@ p {
#typediv { #typediv {
position: relative; position: relative;
/* display: flex; */
} }
.loading-indicator { .loading-indicator {
@ -451,10 +463,14 @@ p {
} }
#channelw { #channelw {
width: 100%; width: 100%;
display: inline-block; display: flex;
grid-template-rows: auto 1fr; grid-template-rows: auto 1fr;
height: calc(100dvh - .1in - var(--servertd-height));
max-width: 100%; max-width: 100%;
flex-direction: column;
flex-shrink: 1;
min-height: 0;
height: 100%;
/* width: 100%; */
} }
.timestamp { .timestamp {
@ -586,7 +602,11 @@ textarea {
border-color: var(--server-border); border-color: var(--server-border);
border-width: .1cm; border-width: .1cm;
border-style: solid; border-style: solid;
height: .2in; height: .4in;
display: flex;
flex-direction: column;
justify-content: space-evenly;
flex-shrink: 0;
} }
.channeleffects { .channeleffects {
@ -628,13 +648,24 @@ textarea {
#userdock { #userdock {
background-color: var(--user-dock-bg); background-color: var(--user-dock-bg);
width: 2.5in; width: 2.5in;
position: absolute; position: relative;
bottom: 0; flex-shrink: 0;
/* position: absolute; */ height: .6in;
display: flex;
align-content: stretch;
/* flex-direction: row; */
justify-content: space-between;
align-items: center;
padding-top: .02in;
} }
#channels-td { #channels-td {
padding-right: 240px; padding-right: 240px;
flex-grow: 1;
display: flex;
flex-direction: column;
height: 100%;
flex-shrink: 1;
} }
#settings { #settings {
@ -646,6 +677,7 @@ textarea {
font-size: .25in; font-size: .25in;
width: .3in; width: .3in;
height: .3in; height: .3in;
overflow: visible;
} }
#settings:hover { #settings:hover {
@ -659,11 +691,13 @@ textarea {
background-color: var(--user-info-bg); background-color: var(--user-info-bg);
border-radius: .1in; border-radius: .1in;
cursor: pointer; cursor: pointer;
flex-shrink: 0;
padding: .03in;
} }
.servernamediv { .servernamediv {
width: 100%; /* width: 100%; */
max-width: 100%; /* max-width: 100%; */
} }
button { button {
@ -908,13 +942,14 @@ span {
.accountSwitcher{ .accountSwitcher{
background:var(--profile-bg); background:var(--profile-bg);
cursor:pointer; cursor:pointer;
position:absolute;
top:0px;
transform: translate(0, -100%);
/* width:100%; */
box-shadow: .00in -.5in 1in #0000006b; box-shadow: .00in -.5in 1in #0000006b;
border-radius: .05in .05in .0in.0in; border-radius: .05in .05in .0in.0in;
/* max-width: 100%; */ flex-grow: 0;
align-self: center;
flex-shrink: 1;
min-width: 0px;
display: inline-block;
width: 100%;
} }
.accountSwitcher tr{ .accountSwitcher tr{
transition: background .3s; transition: background .3s;
@ -932,6 +967,7 @@ span {
word-break: normal; word-break: normal;
white-space: nowrap; white-space: nowrap;
font-size: .125in; font-size: .125in;
min-width: 0px;
} }
.title{ .title{
font-size:.25in; font-size:.25in;
@ -941,6 +977,7 @@ span {
#channelname{ #channelname{
font-size:.2in; font-size:.2in;
font-weight:bold; font-weight:bold;
display: flex;
} }
#mobileback{ #mobileback{
/* display:inline-block; */ /* display:inline-block; */
@ -1051,9 +1088,10 @@ span {
right:0px; right:0px;
} }
.containedFile{ .containedFile{
position:relative !important; position: relative;
width: fit-content; width: fit-content;
box-shadow:.02in .02in .05in black; box-shadow:.02in .02in .05in black;
display: inline-block;
} }
#replybox{ #replybox{
display:flex; display:flex;
@ -1083,3 +1121,124 @@ span {
height: 0in; height: 0in;
padding: 0in; padding: 0in;
} }
.Buttons{
background:var(--primary-bg);
position: relative;
}
.flexltr{
display:flex;
flex-wrap: nowrap;
flex-direction: row;
max-height: 100%;
overflow: auto;
}
.flexttb{
display: flex;
flex-direction: column;
max-height: 100vh;
overflow: auto;
/* margin-bottom: 1in; */
/* padding-bottom: .1in; */
}
.settingbuttons{
padding-top:.075in;
width: 4in;
border-right: solid var(--message-bg-hover);
gap:.04in;
}
.setting{
background:var(--user-info-bg);
padding:.075in;
border-radius:.075in;
box-shadow:0in 0in .1in var(--message-bg-hover);
}
.settingsname{
font-size:.25in;
font-weight:bold;
margin-right:.1in;
}
.flexspace{
gap:.1in;
}
.titlediv{
height:100%;
display: flex;
flex-direction: column;
}
.settingstitle{
font-weight:900;
font-size:.25in;
border-bottom:solid var(--message-bg-hover);
background: var(--primary-bg);
padding: .02in .1in;
}
.exitsettings{
position:absolute;
top:.05in;
right:.05in;
background:var(--channel-hover);
width:.3in;
height:.3in;
display:flex;
border-radius:1in;
justify-content: space-evenly;
align-items: center;
font-size:.2in;
cursor:pointer;
border:solid .04in black;
}
.tritoggle{
display:inline-block;
input{
height:.15in;
width:.15in;
margin:.015in;
}
:first-child{
accent-color:green;
}
:last-child{
accent-color:red;
}
}
.channelflex{
flex-shrink:0;
flex-grow:0;
width:2.5in;
display: flex;
justify-content: space-evenly;
align-content: stretch;
flex-direction: column;
overflow: hidden;
}
.messageflex{
display:flex;
justify-content: space-between;
width: 100%;
}
.userflex{
display:flex;
flex-direction: column;
justify-content: center;
}
.savediv{
position:absolute;
background:var(--message-bg-hover);
border:solid black;
border-radius:.075in;
padding:.05in .2in;
display:flex;
align-items: center;
right:50%;
transform:translate(50%,0);
bottom:.1in;
font-weight:bold;
font-size:.2in;
width:5in;
height:.5in;
button{
background:green;
}
}

View file

@ -4,6 +4,7 @@ import {markdown} from "./markdown.js";
import {Contextmenu} from "./contextmenu.js"; import {Contextmenu} from "./contextmenu.js";
import {Localuser} from "./localuser.js"; import {Localuser} from "./localuser.js";
import {Guild} from "./guild.js"; import {Guild} from "./guild.js";
class User{ class User{
static userids={}; static userids={};
owner:Localuser; owner:Localuser;
@ -15,6 +16,19 @@ class User{
discriminator:string; discriminator:string;
pronouns:string; pronouns:string;
bot:boolean; bot:boolean;
static contextmenu:Contextmenu=new Contextmenu("User Menu");
static setUpContextMenu(){
this.contextmenu.addbutton("Copy user id",function(){
navigator.clipboard.writeText(this.id);
});
this.contextmenu.addbutton("Message user",function(){
fetch(this.info.api.toString()+"/v9/users/@me/channels",
{method:"POST",
body:JSON.stringify({"recipients":[this.id]}),
headers: this.headers
});
})
}
static checkuser(userjson,owner:Localuser){ static checkuser(userjson,owner:Localuser){
if(User.userids[userjson.id]){ if(User.userids[userjson.id]){
return User.userids[userjson.id]; return User.userids[userjson.id];
@ -58,6 +72,23 @@ class User{
this.changepfp(json.avatar); this.changepfp(json.avatar);
} }
} }
bind(html:HTMLElement,guild:Guild=null){
if(guild&&guild.id!=="@me"){
Member.resolve(this,guild).then(_=>{
_.bind(html);
}).catch(_=>{
console.log(_)
});
}
this.profileclick(html);
User.contextmenu.bind(html,this);
}
static async resolve(id:string,localuser:Localuser){
const json=await fetch(localuser.info.api.toString()+"/v9/users/"+id+"/profile",
{headers:localuser.headers}
).then(_=>_.json());
return new User(json,localuser);
}
changepfp(update:string){ changepfp(update:string){
this.avatar=update; this.avatar=update;
this.hypotheticalpfp=false; this.hypotheticalpfp=false;
@ -140,4 +171,5 @@ class User{
} }
} }
} }
User.setUpContextMenu();
export {User}; export {User};