admin finder Menu

This commit is contained in:
MathMan05
2025-08-30 16:39:04 -05:00
parent 667c1d2d64
commit 648df3fc12
6 changed files with 260 additions and 17 deletions
+1 -1
View File
@@ -147,6 +147,7 @@ class Contextmenu<x, y> {
static setup() {
Contextmenu.declareMenu();
document.addEventListener("click", (event) => {
console.log(event.target, Contextmenu.currentmenu);
while (Contextmenu.currentmenu && !Contextmenu.currentmenu.contains(event.target as Node)) {
Contextmenu.declareMenu();
}
@@ -270,7 +271,6 @@ class Contextmenu<x, y> {
obj.style.left = Math.floor(docwidth - box.width) + "px";
}
if (box.bottom > docheight) {
debugger;
obj.style.top = Math.floor(docheight - box.height) + "px";
}
}
+181
View File
@@ -263,6 +263,13 @@ class Guild extends SnowFlake {
},
},
);
//TODO make icon for this
Guild.contextmenu.addButton(
() => I18n.guild.admins(),
function (this: Guild) {
this.findAdmin();
},
);
Guild.contextmenu.addSeperator();
Guild.contextmenu.addButton(
@@ -273,6 +280,180 @@ class Guild extends SnowFlake {
);
//TODO mute guild button
}
async findAdmin() {
const menu = new Dialog(I18n.guild.admins());
menu.options.addText("Loading");
menu.show();
const roles = new Set(
Object.entries(
(await (
await fetch(this.info.api + `/guilds/${this.id}/roles/member-counts/`, {
headers: this.headers,
})
).json()) as {[key: string]: number},
)
.map(([id, count]) => {
return [this.roleids.get(id), count] as [Role, number];
})
//Just in case, this should never fire
.filter((_) => _[0] !== undefined)
//Filter out those who have too many users
.filter((_) => _[1] > 1000)
.map((_) => _[0]),
);
const everyone = this.roleids.get(this.id);
if (everyone) roles.add(everyone);
menu.options.removeAll();
let owner = true;
let perms = [
"ADMINISTRATOR",
"BAN_MEMBERS",
"KICK_MEMBERS",
"MANAGE_GUILD",
"MANAGE_CHANNELS",
"MODERATE_MEMBERS",
"MANAGE_ROLES",
"MANAGE_MESSAGES",
"MANAGE_NICKNAMES",
"MANAGE_WEBHOOKS",
"MANAGE_EVENTS",
"MANAGE_THREADS",
].filter((_) => {
for (const role of roles) {
if (role.permissions.hasPermission(_, false)) {
return false;
}
}
return true;
});
menu.options.addButtonInput("", I18n.guild.adminMenu.changePerms(), () => {
const d = new Dialog("", {noSubmit: false});
const opt = d.options;
opt
.addCheckboxInput(
I18n.guild.adminMenu.owner(),
(b) => {
owner = b;
d.hide();
queueMicrotask(() => loadResults());
},
{
initState: owner,
},
)
.watchForChange(() => opt.changed());
for (const perm of Permissions.info().filter((_) => {
for (const role of roles) {
if (role.permissions.hasPermission(_.name, false)) {
return false;
}
}
return true;
})) {
opt.addHR();
opt
.addCheckboxInput(
perm.readableName,
(b) => {
if (b) {
perms.push(perm.name);
} else {
perms = perms.filter((_) => _ !== perm.name);
}
},
{
initState: perms.includes(perm.name),
},
)
.watchForChange(() => opt.changed());
opt.addText(perm.description);
}
d.show().style.width = "80%";
});
const retDiv = document.createElement("div");
menu.options.addHTMLArea(retDiv);
const loadResults = async () => {
retDiv.textContent = I18n.guild.adminMenu.finding();
const results = new Set(
(
await Promise.all(
this.roles
.filter((_) => {
for (const perm of perms) {
if (_.permissions.hasPermission(perm, false)) {
return true;
}
}
return false;
})
.map(async (_) => {
return (
await fetch(`${this.info.api}/guilds/${this.id}/roles/${_.id}/member-ids`, {
headers: this.headers,
})
).json() as Promise<string[]>;
}),
)
).flat(),
);
if (owner) {
results.add(this.properties.owner_id);
}
const members = (
await Promise.all(
[...results].map(async (_) => {
const json = await this.localuser.resolvemember(_, this.id);
return json ? Member.new(json, this) : undefined;
}),
)
).filter((_) => _ !== undefined);
members.sort((a, b) => {
return a.name < b.name ? 1 : -1;
});
retDiv.innerHTML = "";
retDiv.append(
...members.map((memb) => {
const div = document.createElement("div");
div.classList.add("flexltr", "adminList");
const name = document.createElement("b");
name.textContent = memb.name;
const nameBox = document.createElement("div");
nameBox.classList.add("flexttb");
const roles = document.createElement("div");
roles.classList.add("flexltr");
roles.append(
...perms
.filter((_) => memb.hasPermission(_, false))
.map((perm) => {
const span = document.createElement("span");
//@ts-ignore
span.textContent = I18n.permissions.readableNames[perm]();
return span;
}),
);
if (owner && memb.id === this.properties.owner_id) {
const span = document.createElement("span");
span.textContent = I18n.guild.adminMenu.ownName();
roles.append(span);
}
nameBox.append(name, roles);
const pfp = memb.user.buildpfp(memb, div);
div.append(pfp, nameBox);
memb.user.bind(div, this, undefined);
return div;
}),
);
console.log(members);
};
loadResults();
}
generateSettings() {
const settings = new Settings(I18n.getTranslation("guild.settingsFor", this.properties.name));
const textChannels = this.channels.filter((e) => {
+12 -7
View File
@@ -824,15 +824,22 @@ class HtmlArea implements OptionsElement<void> {
*/
class Float {
options: Options;
html: WeakRef<HTMLElement> = new WeakRef(document.createElement("div"));
/**
* This is a simple wrapper class for Options to make it happy so it can be used outside of Settings.
*/
constructor(name: string, options = {ltr: false, noSubmit: true}) {
this.options = new Options(name, this, options);
}
changed = () => {};
changed(d: HTMLElement) {
const html = this.html.deref();
if (!html) return;
html.append(d);
}
generateHTML() {
return this.options.generateHTML();
const html = this.options.generateHTML();
this.html = new WeakRef(html);
return html;
}
}
class Dialog {
@@ -852,16 +859,14 @@ class Dialog {
center.classList.add("centeritem", "nonimagecenter");
center.classList.remove("titlediv");
background.append(center);
center.onclick = (e) => {
e.stopImmediatePropagation();
};
document.body.append(background);
this.background = new WeakRef(background);
background.onclick = (_) => {
if (hideOnClick) {
if (hideOnClick && _.target === background) {
background.remove();
}
};
return center;
}
hide() {
const background = this.background.deref();
@@ -1098,7 +1103,7 @@ class Options implements OptionsElement<void> {
}
addEmojiInput(
label: string,
onSubmit: (str: Emoji | undefined) => void,
onSubmit: (str: Emoji | null | undefined) => void,
localuser?: Localuser,
{initEmoji = undefined, clear = false} = {} as {initEmoji?: Emoji; clear?: boolean},
) {
+35 -3
View File
@@ -74,6 +74,35 @@ body {
margin-left: 4px;
}
}
.adminList {
width: 95%;
background: var(--secondary-bg);
padding: 6px;
margin-bottom: 6px;
border-radius: 10px;
cursor: pointer;
img {
width: 48px;
height: 48px;
}
.flexttb {
margin-left: 10px;
display: flex;
justify-content: center;
.flexltr {
span {
margin-right: 4px;
text-wrap: nowrap;
background: var(--primary-bg);
padding: 2px;
border-radius: 3px;
margin-bottom: 4px;
}
flex-wrap: wrap;
}
}
}
.flexltr {
min-height: 0;
display: flex;
@@ -2923,17 +2952,20 @@ fieldset input[type="radio"] {
.tritoggle input:last-child {
accent-color: var(--red);
}
.nonimagecenter > .flexltr.savediv {
padding: 16px !important;
}
.savediv {
position: fixed;
bottom: 24px;
right: 50%;
transform: translateX(50%);
padding: 16px;
background: var(--secondary-bg);
padding: 16px !important;
background: var(--secondary-bg) !important;
font-size: 1.2em;
font-weight: bold;
color: var(--secondary-text);
border-radius: 8px;
border-radius: 8px !important;
align-items: center;
box-shadow:
0 0 24px var(--shadow),
+24 -6
View File
@@ -496,7 +496,7 @@ class User extends SnowFlake {
nameBox.append(notFound);
}
});
this.bind(div, guild);
this.bind(div, guild, undefined);
return div;
}
buildstatuspfp(guild: Guild | void | Member | null): HTMLDivElement {
@@ -541,7 +541,12 @@ class User extends SnowFlake {
}
}
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
bind(
html: HTMLElement,
guild: Guild | null = null,
error = true,
button: "right" | "left" = "right",
): void {
if (guild && guild.id !== "@me") {
Member.resolveMember(this, guild)
.then((member) => {
@@ -557,14 +562,14 @@ class User extends SnowFlake {
if (member) {
member.bind(html);
} else {
User.contextmenu.bindContextmenu(html, this, undefined);
User.contextmenu.bindContextmenu(html, this, undefined, undefined, undefined, button);
}
})
.catch((err) => {
console.log(err);
});
} else {
User.contextmenu.bindContextmenu(html, this, undefined);
User.contextmenu.bindContextmenu(html, this, undefined, undefined, undefined, button);
}
if (guild) {
this.profileclick(html, guild);
@@ -913,6 +918,7 @@ class User extends SnowFlake {
x: number,
y: number,
guild: Guild | null | Member = null,
zIndex = -1,
): Promise<HTMLDivElement> {
const membres = (async () => {
if (!guild) return;
@@ -925,7 +931,9 @@ class User extends SnowFlake {
return member;
})();
const div = document.createElement("div");
if (zIndex !== -1) {
div.style.zIndex = zIndex + "";
}
if (this.accent_color) {
div.style.setProperty(
"--accent_color",
@@ -1100,8 +1108,18 @@ class User extends SnowFlake {
}
}
profileclick(obj: HTMLElement, guild?: Guild): void {
const getIndex = (elm: HTMLElement) => {
const index = getComputedStyle(elm).zIndex;
if (index === "auto") {
if (elm.parentElement) {
return getIndex(elm.parentElement);
}
}
return +index;
};
obj.onclick = (e: MouseEvent) => {
this.buildprofile(e.clientX, e.clientY, guild);
const index = 1 + getIndex(obj);
this.buildprofile(e.clientX, e.clientY, guild, index);
e.stopPropagation();
};
}
+7
View File
@@ -288,6 +288,13 @@
"edit": "Edit",
"guild": {
"template": "Template:",
"admins":"Find Admins",
"adminMenu":{
"finding":"Finding Admins",
"permission":"Permissions:",
"changePerms":"Change Permissions to find",
"owner":"Find the owner","ownName":"Owner"
},
"viewTemplate": "View Template",
"createFromTemplate": "Guild From Template",
"tempUseCount": "Template has been used $1 {{PLURAL:$1|time|times}}",