This commit is contained in:
MathMan05 2024-10-29 20:30:24 -05:00
parent e06d304064
commit c8e3125c5d
10 changed files with 759 additions and 250 deletions

View file

@ -61,7 +61,7 @@ class Channel extends SnowFlake{
this.readbottom();
});
this.contextmenu.addbutton("Settings[temp]", function(this: Channel){
this.contextmenu.addbutton("Settings", function(this: Channel){
this.generateSettings();
});
@ -76,17 +76,6 @@ class Channel extends SnowFlake{
}
);
this.contextmenu.addbutton(
"Edit channel",
function(this: Channel){
this.editChannel();
},
null,
function(){
return this.isAdmin();
}
);
this.contextmenu.addbutton(
"Make invite",
function(this: Channel){
@ -205,15 +194,33 @@ class Channel extends SnowFlake{
generateSettings(){
this.sortPerms();
const settings = new Settings("Settings for " + this.name);
const s1 = settings.addButton("roles");
{
const gensettings=settings.addButton("Settings");
const form=gensettings.addForm("",()=>{},{
fetchURL:this.info.api + "/channels/" + this.id,
method: "PATCH",
headers: this.headers,
});
form.addTextInput("Name:","name",{initText:this.name});
form.addMDInput("Topic:","topic",{initText:this.topic});
form.addCheckboxInput("NSFW:","nsfw",{initState:this.nsfw});
if(this.type!==4){
const options=["voice", "text", "announcement"];
form.addSelect("Type:","type",options,{
defaultIndex:options.indexOf({0:"text", 2:"voice", 5:"announcement", 4:"category" }[this.type] as string)
})
}
form.addPreprocessor((obj:any)=>{
obj.type={text: 0, voice: 2, announcement: 5, category: 4 }[obj.type as string]
})
}
const s1 = settings.addButton("Permisions");
s1.options.push(
new RoleList(
this.permission_overwritesar,
this.guild,
this.updateRolePermissions.bind(this),
true
this
)
);
settings.show();
@ -739,68 +746,6 @@ class Channel extends SnowFlake{
}),
});
}
editChannel(){
let name = this.name;
let topic = this.topic;
let nsfw = this.nsfw;
const thisid = this.id;
const thistype = this.type;
const full = new Dialog([
"hdiv",
[
"vdiv",
[
"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",
headers: this.headers,
body: JSON.stringify({
name,
type: thistype,
topic,
bitrate: 64000,
user_limit: 0,
nsfw,
flags: 0,
rate_limit_per_user: 0,
}),
});
console.log(full);
full.hide();
},
],
],
]);
full.show();
console.log(full);
}
deleteChannel(){
fetch(this.info.api + "/channels/" + this.id, {
method: "DELETE",
@ -1231,12 +1176,11 @@ class Channel extends SnowFlake{
this.children = [];
this.guild_id = json.guild_id;
const oldover=this.permission_overwrites;
this.permission_overwrites = new Map();
this.permission_overwritesar=[];
for(const thing of json.permission_overwrites){
if(
thing.id === "1182819038095799904" ||
thing.id === "1182820803700625444"
){
if(thing.id === "1182819038095799904" || thing.id === "1182820803700625444"){
continue;
}
this.permission_overwrites.set(
@ -1251,9 +1195,26 @@ class Channel extends SnowFlake{
}
}
}
const nchange=[...new Set<string>().union(oldover).difference(this.permission_overwrites)];
const pchange=[...new Set<string>().union(this.permission_overwrites).difference(oldover)];
for(const thing of nchange){
const role=this.guild.roleids.get(thing);
if(role){
this.croleUpdate(role,new Permissions("0"),false)
}
}
for(const thing of pchange){
const role=this.guild.roleids.get(thing);
const perms=this.permission_overwrites.get(thing);
if(role&&perms){
this.croleUpdate(role,perms,true);
}
}
console.log(pchange,nchange);
this.topic = json.topic;
this.nsfw = json.nsfw;
}
croleUpdate:(role:Role,perm:Permissions,added:boolean)=>unknown=()=>{};
typingstart(){
if(this.typing > Date.now()){
return;
@ -1366,16 +1327,14 @@ class Channel extends SnowFlake{
return;
}
if(
this.localuser.lookingguild?.prevchannel === this &&
document.hasFocus()
this.localuser.lookingguild?.prevchannel === this && document.hasFocus()
){
return;
}
if(this.notification === "all"){
this.notify(messagez);
}else if(
this.notification === "mentions" &&
messagez.mentionsuser(this.localuser.user)
this.notification === "mentions" && messagez.mentionsuser(this.localuser.user)
){
this.notify(messagez);
}
@ -1445,20 +1404,22 @@ class Channel extends SnowFlake{
if(permission){
permission.allow = perms.allow;
permission.deny = perms.deny;
await fetch(
this.info.api + "/channels/" + this.id + "/permissions/" + id,
{
method: "PUT",
headers: this.headers,
body: JSON.stringify({
allow: permission.allow.toString(),
deny: permission.deny.toString(),
id,
type: 0,
}),
}
);
}else{
//this.permission_overwrites.set(id,perms);
}
await fetch(
this.info.api + "/channels/" + this.id + "/permissions/" + id,
{
method: "PUT",
headers: this.headers,
body: JSON.stringify({
allow: perms.allow.toString(),
deny: perms.deny.toString(),
id,
type: 0,
}),
}
);
}
}
Channel.setupcontextmenu();

View file

@ -13,6 +13,7 @@ import{
emojijson,
memberjson,
invitejson,
rolesjson,
}from"./jsontypes.js";
import{ User }from"./user.js";
@ -114,16 +115,67 @@ class Guild extends SnowFlake{
}
form.addTextInput("Region:", "region", { initText: region });
}
const s1 = settings.addButton("roles");
const s1 = settings.addButton("Roles");
const permlist: [Role, Permissions][] = [];
for(const thing of this.roles){
permlist.push([thing, thing.permissions]);
}
s1.options.push(
new RoleList(permlist, this, this.updateRolePermissions.bind(this))
new RoleList(permlist, this, this.updateRolePermissions.bind(this),false)
);
settings.show();
}
roleUpdate:(role:Role,added:-1|0|1)=>unknown=()=>{};
sortRoles(){
this.roles.sort((a,b)=>(b.position-a.position));
}
async recalcRoles(){
let position=this.roles.length;
const map=this.roles.map(_=>{
position--;
return {id:_.id,position};
})
await fetch(this.info.api+"/guilds/"+this.id+"/roles",{
method:"PATCH",
body:JSON.stringify(map),
headers:this.headers
})
}
newRole(rolej:rolesjson){
const role=new Role(rolej,this);
this.roles.push(role);
this.roleids.set(role.id, role);
this.sortRoles();
this.roleUpdate(role,1);
}
updateRole(rolej:rolesjson){
const role=this.roleids.get(rolej.id) as Role;
role.newJson(rolej);
this.roleUpdate(role,0);
}
memberupdate(json:memberjson){
let member:undefined|Member=undefined;
for(const thing of this.members){
if(thing.id===json.id){
member=thing;
break;
}
}
if(!member) return;
member.update(json);
if(member===this.member){
console.log(member);
this.loadGuild();
}
}
deleteRole(id:string){
const role = this.roleids.get(id);
if(!role) return;
this.roleids.delete(id);
this.roles.splice(this.roles.indexOf(role),1);
this.roleUpdate(role,-1);
}
constructor(
json: guildjson | -1,
owner: Localuser,
@ -153,6 +205,7 @@ class Guild extends SnowFlake{
this.roles.push(roleh);
this.roleids.set(roleh.id, roleh);
}
this.sortRoles();
if(member instanceof User){
Member.resolveMember(member, this).then(_=>{
if(_){

View file

@ -478,7 +478,28 @@ roleCreate | {
guild_id: string;
emoji: emojijson;
};
s: 3;
s: number;
}|{
op: 0,
t: "GUILD_ROLE_UPDATE",
d: {
guild_id: string,
role: rolesjson
},
"s": number
}|{
op: 0,
t: "GUILD_ROLE_DELETE",
d: {
guild_id: string,
role_id: string
},
s:number
}|{
op: 0,
t: "GUILD_MEMBER_UPDATE",
d: memberjson,
"s": 3
}|memberlistupdatejson|voiceupdate|voiceserverupdate;

View file

@ -354,149 +354,174 @@ class Localuser{
if(temp.s)this.lastSequence = temp.s;
if(temp.op == 0){
switch(temp.t){
case"MESSAGE_CREATE":
if(this.initialized){
this.messageCreate(temp);
}
break;
case"MESSAGE_DELETE": {
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.id);
if(!message)break;
message.deleteEvent();
break;
}
case"READY":
await this.gottenReady(temp as readyjson);
break;
case"MESSAGE_UPDATE": {
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.id);
if(!message)break;
message.giveData(temp.d);
break;
}
case"TYPING_START":
if(this.initialized){
this.typingStart(temp);
}
break;
case"USER_UPDATE":
if(this.initialized){
const users = this.userMap.get(temp.d.id);
if(users){
users.userupdate(temp.d);
case"MESSAGE_CREATE":
if(this.initialized){
this.messageCreate(temp);
}
}
break;
case"CHANNEL_UPDATE":
if(this.initialized){
this.updateChannel(temp.d);
}
break;
case"CHANNEL_CREATE":
if(this.initialized){
this.createChannel(temp.d);
}
break;
case"CHANNEL_DELETE":
if(this.initialized){
this.delChannel(temp.d);
}
break;
case"GUILD_DELETE": {
const guildy = this.guildids.get(temp.d.id);
if(guildy){
this.guildids.delete(temp.d.id);
this.guilds.splice(this.guilds.indexOf(guildy), 1);
guildy.html.remove();
}
break;
}
case"GUILD_CREATE": {
const guildy = new Guild(temp.d, this, this.user);
this.guilds.push(guildy);
this.guildids.set(guildy.id, guildy);
(document.getElementById("servers") as HTMLDivElement).insertBefore(
guildy.generateGuildIcon(),
document.getElementById("bottomseparator")
);
break;
}
case"MESSAGE_REACTION_ADD":
{
break;
case"MESSAGE_DELETE": {
temp.d.guild_id ??= "@me";
const guild = this.guildids.get(temp.d.guild_id);
if(!guild)break;
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
const message = channel.messages.get(temp.d.id);
if(!message)break;
let thing: Member | { id: string };
if(temp.d.member){
thing = (await Member.new(temp.d.member, guild)) as Member;
}else{
thing = { id: temp.d.user_id };
message.deleteEvent();
break;
}
case"READY":
await this.gottenReady(temp as readyjson);
break;
case"MESSAGE_UPDATE": {
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.id);
if(!message)break;
message.giveData(temp.d);
break;
}
case"TYPING_START":
if(this.initialized){
this.typingStart(temp);
}
message.reactionAdd(temp.d.emoji, thing);
break;
case"USER_UPDATE":
if(this.initialized){
const users = this.userMap.get(temp.d.id);
if(users){
users.userupdate(temp.d);
}
}
break;
case"CHANNEL_UPDATE":
if(this.initialized){
this.updateChannel(temp.d);
}
break;
case"CHANNEL_CREATE":
if(this.initialized){
this.createChannel(temp.d);
}
break;
case"CHANNEL_DELETE":
if(this.initialized){
this.delChannel(temp.d);
}
break;
case"GUILD_DELETE": {
const guildy = this.guildids.get(temp.d.id);
if(guildy){
this.guildids.delete(temp.d.id);
this.guilds.splice(this.guilds.indexOf(guildy), 1);
guildy.html.remove();
}
break;
}
break;
case"MESSAGE_REACTION_REMOVE":
case"GUILD_CREATE": {
const guildy = new Guild(temp.d, this, this.user);
this.guilds.push(guildy);
this.guildids.set(guildy.id, guildy);
(document.getElementById("servers") as HTMLDivElement).insertBefore(
guildy.generateGuildIcon(),
document.getElementById("bottomseparator")
);
break;
}
case"MESSAGE_REACTION_ADD":
{
temp.d.guild_id ??= "@me";
const guild = this.guildids.get(temp.d.guild_id);
if(!guild)break;
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
let thing: Member | { id: string };
if(temp.d.member){
thing = (await Member.new(temp.d.member, guild)) as Member;
}else{
thing = { id: temp.d.user_id };
}
message.reactionAdd(temp.d.emoji, thing);
}
break;
case"MESSAGE_REACTION_REMOVE":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemove(temp.d.emoji, temp.d.user_id);
}
break;
case"MESSAGE_REACTION_REMOVE_ALL":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemoveAll();
}
break;
case"MESSAGE_REACTION_REMOVE_EMOJI":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemoveEmoji(temp.d.emoji);
}
break;
case"GUILD_MEMBERS_CHUNK":
this.gotChunk(temp.d);
break;
case"GUILD_MEMBER_LIST_UPDATE":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemove(temp.d.emoji, temp.d.user_id);
}
break;
case"MESSAGE_REACTION_REMOVE_ALL":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemoveAll();
}
break;
case"MESSAGE_REACTION_REMOVE_EMOJI":
{
temp.d.guild_id ??= "@me";
const channel = this.channelids.get(temp.d.channel_id);
if(!channel)break;
const message = channel.messages.get(temp.d.message_id);
if(!message)break;
message.reactionRemoveEmoji(temp.d.emoji);
}
break;
case"GUILD_MEMBERS_CHUNK":
this.gotChunk(temp.d);
break;
case"GUILD_MEMBER_LIST_UPDATE":
{
this.memberListUpdate(temp)
break;
}
case "VOICE_STATE_UPDATE":
if(this.voiceFactory){
this.voiceFactory.voiceStateUpdate(temp)
this.memberListUpdate(temp)
break;
}
case "VOICE_STATE_UPDATE":
if(this.voiceFactory){
this.voiceFactory.voiceStateUpdate(temp)
}
break;
case "VOICE_SERVER_UPDATE":
if(this.voiceFactory){
this.voiceFactory.voiceServerUpdate(temp)
break;
case "VOICE_SERVER_UPDATE":
if(this.voiceFactory){
this.voiceFactory.voiceServerUpdate(temp)
}
break;
case "GUILD_ROLE_CREATE":{
const guild=this.guildids.get(temp.d.guild_id);
if(!guild) break;
guild.newRole(temp.d.role);
break;
}
case "GUILD_ROLE_UPDATE":{
const guild=this.guildids.get(temp.d.guild_id);
if(!guild) break;
guild.updateRole(temp.d.role);
break;
}
case "GUILD_ROLE_DELETE":{
const guild=this.guildids.get(temp.d.guild_id);
if(!guild) break;
guild.deleteRole(temp.d.role_id);
break;
}
case "GUILD_MEMBER_UPDATE":{
const guild=this.guildids.get(temp.d.guild_id);
if(!guild) break;
guild.memberupdate(temp.d)
break
}
break;
}
}else if(temp.op === 10){
if(!this.ws)return;
console.log("heartbeat down");

View file

@ -49,6 +49,32 @@ class Member extends SnowFlake{
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
});
}
update(memberjson: memberjson){
this.roles=[];
for(const key of Object.keys(memberjson)){
if(key === "guild" || key === "owner" || key === "user"){
continue;
}
if(key === "roles"){
for(const strrole of memberjson.roles){
const role = this.guild.roleids.get(strrole);
if(!role)continue;
this.roles.push(role);
}
continue;
}
if(key === "presence"){
this.getPresence(memberjson.presence);
continue;
}
(this as any)[key] = (memberjson as any)[key];
}
this.roles.sort((a, b)=>{
return this.guild.roles.indexOf(a) - this.guild.roles.indexOf(b);
});
}
get guild(){
return this.owner;
}
@ -241,6 +267,24 @@ class Member extends SnowFlake{
]);
menu.show();
}
addRole(role:Role){
const roles=this.roles.map(_=>_.id)
roles.push(role.id);
fetch(this.info.api+"/guilds/"+this.guild.id+"/members/"+this.id,{
method:"PATCH",
headers:this.guild.headers,
body:JSON.stringify({roles})
})
}
removeRole(role:Role){
let roles=this.roles.map(_=>_.id)
roles=roles.filter(_=>_!==role.id);
fetch(this.info.api+"/guilds/"+this.guild.id+"/members/"+this.id,{
method:"PATCH",
headers:this.guild.headers,
body:JSON.stringify({roles})
})
}
banAPI(reason: string){
const headers = structuredClone(this.guild.headers);
(headers as any)["x-audit-log-reason"] = reason;

View file

@ -3,6 +3,7 @@ import{ Localuser }from"./localuser.js";
import{ Guild }from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ rolesjson }from"./jsontypes.js";
import{ Search }from"./search.js";
class Role extends SnowFlake{
permissions: Permissions;
owner: Guild;
@ -13,6 +14,7 @@ class Role extends SnowFlake{
icon!: string;
mentionable!: boolean;
unicode_emoji!: string;
position!:number;
headers: Guild["headers"];
constructor(json: rolesjson, owner: Guild){
super(json.id);
@ -27,6 +29,15 @@ class Role extends SnowFlake{
this.permissions = new Permissions(json.permissions);
this.owner = owner;
}
newJson(json: rolesjson){
for(const thing of Object.keys(json)){
if(thing === "id"||thing==="permissions"){
continue;
}
(this as any)[thing] = (json as any)[thing];
}
this.permissions.allow=BigInt(json.permissions);
}
get guild(): Guild{
return this.owner;
}
@ -39,6 +50,14 @@ class Role extends SnowFlake{
}
return`#${this.color.toString(16)}`;
}
canManage(){
if(this.guild.member.hasPermission("MANAGE_ROLES")){
let max=-Infinity;
this.guild.member.roles.forEach(r=>max=Math.max(max,r.position))
return this.position<=max||this.guild.properties.owner_id===this.guild.member.id;
}
return false;
}
}
export{ Role };
import{ Options }from"./settings.js";
@ -121,22 +140,25 @@ class PermissionToggle implements OptionsElement<number>{
submit(){}
}
import{ OptionsElement, Buttons }from"./settings.js";
import { Contextmenu } from "./contextmenu.js";
import { Channel } from "./channel.js";
class RoleList extends Buttons{
readonly permissions: [Role, Permissions][];
permissions: [Role, Permissions][];
permission: Permissions;
readonly guild: Guild;
readonly channel: boolean;
declare readonly buttons: [string, string][];
readonly channel: false|Channel;
declare buttons: [string, string][];
readonly options: Options;
onchange: Function;
curid!: string;
constructor(
permissions: [Role, Permissions][],
guild: Guild,
onchange: Function,
channel = false
){
super("Roles");
curid?: string;
get info(){
return this.guild.info;
}
get headers(){
return this.guild.headers;
}
constructor(permissions:[Role, Permissions][], guild:Guild, onchange:Function, channel:false|Channel){
super("");
this.guild = guild;
this.permissions = permissions;
this.channel = channel;
@ -147,16 +169,237 @@ class RoleList extends Buttons{
}else{
this.permission = new Permissions("0");
}
this.makeguildmenus(options);
for(const thing of Permissions.info){
options.options.push(
new PermissionToggle(thing, this.permission, options)
);
}
for(const i of permissions){
console.log(i);
this.buttons.push([i[0].name, i[0].id]);
}
this.options = options;
guild.roleUpdate=this.groleUpdate.bind(this);
if(channel){
channel.croleUpdate=this.croleUpdate.bind(this);
}
}
private groleUpdate(role:Role,added:1|0|-1){
if(!this.channel){
if(added===1){
this.permissions.push([role,role.permissions]);
}
}
if(added===-1){
this.permissions=this.permissions.filter(r=>r[0]!==role);
}
this.redoButtons();
}
private croleUpdate(role:Role,perm:Permissions,added:boolean){
if(added){
this.permissions.push([role,perm])
}else{
this.permissions=this.permissions.filter(r=>r[0]!==role);
}
this.redoButtons();
}
makeguildmenus(option:Options){
option.addButtonInput("","Display settings",()=>{
const role=this.guild.roleids.get(this.curid as string);
if(!role) return;
const form=option.addSubForm("Display settings",()=>{},{
fetchURL:this.info.api+"/guilds/"+this.guild.id+"/roles/"+this.curid,
method:"PATCH",
headers:this.headers
});
form.addTextInput("Role Name:","name",{
initText:role.name
});
form.addCheckboxInput("Hoisted:","hoist",{
initState:role.hoist
});
form.addCheckboxInput("Allow anyone to ping this role:","mentionable",{
initState:role.mentionable
});
const color="#"+role.color.toString(16).padStart(6,"0");
form.addColorInput("Color","color",{
initColor:color
});
form.addPreprocessor((obj:any)=>{
obj.color=Number("0x"+obj.color.substring(1));
console.log(obj.color);
})
})
}
static channelrolemenu=this.ChannelRoleMenu();
static guildrolemenu=this.GuildRoleMenu();
private static ChannelRoleMenu(){
const menu=new Contextmenu<RoleList,Role>("role settings");
menu.addbutton("Remove role",function(role){
if(!this.channel) return;
console.log(role);
fetch(this.info.api+"/channels/"+this.channel.id+"/permissions/"+role.id,{
method:"DELETE",
headers:this.headers
})
},null);
return menu;
}
private static GuildRoleMenu(){
const menu=new Contextmenu<RoleList,Role>("role settings");
menu.addbutton("Delete Role",function(role){
if(!confirm("Are you sure you want to delete "+role.name+"?")) return;
console.log(role);
fetch(this.info.api+"/guilds/"+this.guild.id+"/roles/"+role.id,{
method:"DELETE",
headers:this.headers
})
},null);
return menu;
}
redoButtons(){
this.buttons=[];
this.permissions.sort(([a],[b])=>b.position-a.position);
for(const i of this.permissions){
this.buttons.push([i[0].name, i[0].id]);
}
console.log("in here :P")
if(!this.buttonList)return;
console.log("in here :P");
const elms=Array.from(this.buttonList.children);
const div=elms[0] as HTMLDivElement;
const div2=elms[1] as HTMLDivElement;
console.log(div);
div.innerHTML="";
div.append(this.buttonListGen(div2));//not actually sure why the html is needed
}
buttonMap=new WeakMap<HTMLButtonElement,Role>();
dragged?:HTMLButtonElement;
buttonDragEvents(button:HTMLButtonElement,role:Role){
this.buttonMap.set(button,role);
button.addEventListener("dragstart", e=>{
this.dragged = button;
e.stopImmediatePropagation();
});
button.addEventListener("dragend", ()=>{
this.dragged = undefined;
});
button.addEventListener("dragenter", event=>{
console.log("enter");
event.preventDefault();
return true;
});
button.addEventListener("dragover", event=>{
event.preventDefault();
return true;
});
button.addEventListener("drop", _=>{
const role2=this.buttonMap.get(this.dragged as HTMLButtonElement);
if(!role2) return;
const index2=this.guild.roles.indexOf(role2);
this.guild.roles.splice(index2,1);
const index=this.guild.roles.indexOf(role);
this.guild.roles.splice(index+1,0,role2);
this.guild.recalcRoles();
console.log(role);
});
}
buttonListGen(html:HTMLElement){
const buttonTable=document.createElement("div");
buttonTable.classList.add("flexttb");
const roleRow=document.createElement("div");
roleRow.classList.add("flexltr");
roleRow.append("Roles");
const add=document.createElement("span");
add.classList.add("svg-plus","svgicon","addrole");
add.onclick=async (e)=>{
const box=add.getBoundingClientRect();
e.stopPropagation();
if(this.channel){
const roles:[Role,string[]][]=[];
for(const role of this.guild.roles){
if(this.permissions.find(r=>r[0]==role)){
continue;
}
roles.push([role,[role.name]]);
}
const search=new Search(roles);
const found=await search.find(box.left,box.top);
if(!found) return;
console.log(found);
this.onchange(found.id,new Permissions("0","0"));
}else{
const bar=document.createElement("input");
bar.classList.add("fixedsearch");
bar.style.left=(box.left^0)+"px";
bar.style.top=(box.top^0)+"px";
document.body.append(bar);
if(Contextmenu.currentmenu != ""){
Contextmenu.currentmenu.remove();
}
Contextmenu.currentmenu=bar;
Contextmenu.keepOnScreen(bar);
bar.onchange=()=>{
bar.remove();
console.log(bar.value)
if(bar.value==="") return;
fetch(this.info.api+`/guilds/${this.guild.id}/roles`,{
method:"POST",
headers:this.headers,
body:JSON.stringify({
color:0,
name:bar.value,
permissions:""
})
})
}
}
}
roleRow.append(add);
buttonTable.append(roleRow);
for(const thing of this.buttons){
const button = document.createElement("button");
button.classList.add("SettingsButton");
button.textContent = thing[0];
const role=this.guild.roleids.get(thing[1]);
if(role){
if(!this.channel){
if(role.canManage()){
this.buttonDragEvents(button,role);
button.draggable=true;
RoleList.guildrolemenu.bindContextmenu(button,this,role)
}
}else{
if(role.canManage()){
RoleList.channelrolemenu.bindContextmenu(button,this,role)
}
}
}
button.onclick = _=>{
this.generateHTMLArea(thing[1], html);
if(this.warndiv){
this.warndiv.remove();
}
};
buttonTable.append(button);
}
return buttonTable;
}
generateButtons(html:HTMLElement):HTMLDivElement{
const div = document.createElement("div");
div.classList.add("settingbuttons");
div.append(this.buttonListGen(html));
return div;
}
handleString(str: string): HTMLElement{
this.curid = str;

72
src/webpage/search.ts Normal file
View file

@ -0,0 +1,72 @@
import { Contextmenu } from "./contextmenu.js";
class Search<E>{
options:Map<string,E>;
readonly keys:string[];
constructor(options:[E,string[]][]){
const map=options.flatMap(e=>{
const val=e[1].map(f=>[f,e[0]]);
return val as [string,E][];
})
this.options=new Map(map);
this.keys=[...this.options.keys()];
}
generateList(str:string,max:number,res:(e:E)=>void){
str=str.toLowerCase();
const options=this.keys.filter(e=>{
return e.toLowerCase().includes(str)
});
const div=document.createElement("div");
div.classList.add("OptionList","flexttb");
for(const option of options.slice(0, max)){
const hoption=document.createElement("span");
hoption.textContent=option;
hoption.onclick=()=>{
if(!this.options.has(option)) return;
res(this.options.get(option) as E)
}
div.append(hoption);
}
return div;
}
async find(x:number,y:number,max=4):Promise<E|undefined>{
return new Promise<E|undefined>((res)=>{
const container=document.createElement("div");
container.classList.add("fixedsearch");
console.log((x^0)+"",(y^0)+"");
container.style.left=(x^0)+"px";
container.style.top=(y^0)+"px";
const remove=container.remove;
container.remove=()=>{
remove.call(container);
res(undefined);
}
function resolve(e:E){
res(e);
container.remove();
}
const bar=document.createElement("input");
const options=document.createElement("div");
const keydown=()=>{
const html=this.generateList(bar.value,max,resolve);
options.innerHTML="";
options.append(html);
}
bar.oninput=keydown;
keydown();
bar.type="text";
container.append(bar);
container.append(options);
document.body.append(container);
if(Contextmenu.currentmenu != ""){
Contextmenu.currentmenu.remove();
}
Contextmenu.currentmenu=container;
Contextmenu.keepOnScreen(container);
})
}
}
export {Search};

View file

@ -30,6 +30,15 @@ class Buttons implements OptionsElement<unknown>{
this.buttonList = buttonList;
const htmlarea = document.createElement("div");
htmlarea.classList.add("flexgrow");
const buttonTable = this.generateButtons(htmlarea);
if(this.buttons[0]){
this.generateHTMLArea(this.buttons[0][1], htmlarea);
}
buttonList.append(buttonTable);
buttonList.append(htmlarea);
return buttonList;
}
generateButtons(optionsArea:HTMLElement){
const buttonTable = document.createElement("div");
buttonTable.classList.add("settingbuttons");
for(const thing of this.buttons){
@ -37,24 +46,21 @@ class Buttons implements OptionsElement<unknown>{
button.classList.add("SettingsButton");
button.textContent = thing[0];
button.onclick = _=>{
this.generateHTMLArea(thing[1], htmlarea);
this.generateHTMLArea(thing[1], optionsArea);
if(this.warndiv){
this.warndiv.remove();
}
};
buttonTable.append(button);
}
this.generateHTMLArea(this.buttons[0][1], htmlarea);
buttonList.append(buttonTable);
buttonList.append(htmlarea);
return buttonList;
return buttonTable;
}
handleString(str: string): HTMLElement{
const div = document.createElement("span");
div.textContent = str;
return div;
}
private generateHTMLArea(
generateHTMLArea(
buttonInfo: Options | string,
htmlarea: HTMLElement
){
@ -1066,6 +1072,10 @@ class Form implements OptionsElement<object>{
this.owner.changed();
}
}
preprocessor:(obj:Object)=>void=()=>{};
addPreprocessor(func:(obj:Object)=>void){
this.preprocessor=func;
}
async submit(){
if(this.options.subOptions){
this.options.subOptions.submit();
@ -1130,6 +1140,7 @@ class Form implements OptionsElement<object>{
}
console.log("middle2");
await Promise.allSettled(promises);
this.preprocessor(build);
if(this.fetchURL !== ""){
fetch(this.fetchURL, {
method: this.method,

View file

@ -1933,4 +1933,29 @@ fieldset input[type="radio"] {
height: 100px;
width: 100%;
}
}
}
.addrole{
width:.1in;
height: .1in;
margin-left: .1in;
margin-top: .04in;
cursor: pointer;
}
.fixedsearch{
position: absolute;
background: var(--primary-bg);
min-height: .2in;
padding:.05in;
border:solid .03in var(--black);
border-radius:.05in;
span{
margin-top:.1in;
width:100%;
padding:.03in;
border:solid .03in var(--black);
box-sizing:border-box;
border-radius:.05in;
cursor:pointer;
}
}

View file

@ -5,6 +5,8 @@ import{ Localuser }from"./localuser.js";
import{ Guild }from"./guild.js";
import{ SnowFlake }from"./snowflake.js";
import{ presencejson, userjson }from"./jsontypes.js";
import { Role } from "./role.js";
import { Search } from "./search.js";
class User extends SnowFlake{
owner: Localuser;
@ -174,6 +176,58 @@ class User extends SnowFlake{
return us.hasPermission("BAN_MEMBERS") || false;
}
);
this.contextmenu.addbutton(
"Add roles",
async function(this: User, member: Member | undefined,e){
if(member){
e.stopPropagation();
const roles:[Role,string[]][]=[];
for(const role of member.guild.roles){
if(!role.canManage()||member.roles.indexOf(role)!==-1){
continue;
}
roles.push([role,[role.name]]);
}
const search=new Search(roles);
const result=await search.find(e.x,e.y);
if(!result) return;
member.addRole(result);
}
},
null,
member=>{
if(!member)return false;
const us = member.guild.member;
console.log(us.hasPermission("MANAGE_ROLES"))
return us.hasPermission("MANAGE_ROLES") || false;
}
);
this.contextmenu.addbutton(
"Remove roles",
async function(this: User, member: Member | undefined,e){
if(member){
e.stopPropagation();
const roles:[Role,string[]][]=[];
for(const role of member.roles){
if(!role.canManage()){
continue;
}
roles.push([role,[role.name]]);
}
const search=new Search(roles);
const result=await search.find(e.x,e.y);
if(!result) return;
member.removeRole(result);
}
},
null,
member=>{
if(!member)return false;
const us = member.guild.member;
console.log(us.hasPermission("MANAGE_ROLES"))
return us.hasPermission("MANAGE_ROLES") || false;
}
);
}
static checkuser(user: User | userjson, owner: Localuser): User{