varrious updates

This commit is contained in:
MathMan05
2025-11-10 23:23:02 -06:00
parent 4ffda330c7
commit 53b94e8f47
10 changed files with 217 additions and 102 deletions
+46 -17
View File
@@ -44,7 +44,7 @@ class Channel extends SnowFlake {
children!: Channel[];
guild_id!: string;
permission_overwrites!: Map<string, Permissions>;
permission_overwritesar: [Role, Permissions][] = [];
permission_overwritesar: [Role | Promise<User>, Permissions][] = [];
topic!: string;
nsfw!: boolean;
position: number = 0;
@@ -356,14 +356,16 @@ class Channel extends SnowFlake {
}
}
const s1 = settings.addButton(I18n.channel.permissions(), {optName: ""});
s1.options.push(
new RoleList(
this.permission_overwritesar,
this.guild,
this.updateRolePermissions.bind(this),
this,
),
);
(async () => {
const list = await Promise.all(
this.permission_overwritesar.map(async (_) => {
return [await _[0], _[1]] as [Role | User, Permissions];
}),
);
s1.options.push(new RoleList(list, this.guild, this.updateRolePermissions.bind(this), this));
})();
const inviteMenu = settings.addButton(I18n.guild.invites());
makeInviteMenu(inviteMenu, this.owner, this.info.api + `/channels/${this.id}/invites`);
@@ -374,9 +376,13 @@ class Channel extends SnowFlake {
settings.show();
}
sortPerms() {
console.log(this.permission_overwritesar + "");
this.permission_overwritesar.sort((a, b) => {
if (a[0] instanceof Promise) return -1;
if (b[0] instanceof Promise) return 1;
return this.guild.roles.indexOf(a[0]) - this.guild.roles.indexOf(b[0]);
});
console.log(this.permission_overwritesar + "");
}
setUpInfiniteScroller() {
this.infinite = new InfiniteScroller(
@@ -469,6 +475,8 @@ class Channel extends SnowFlake {
const role = this.guild.roleids.get(thing.id);
if (role) {
this.permission_overwritesar.push([role, permission]);
} else {
this.permission_overwritesar.push([this.localuser.getUser(thing.id), permission]);
}
}
}
@@ -608,6 +616,15 @@ class Channel extends SnowFlake {
if (!member.user.bot || true) {
roles.add(everyone);
}
const premission = this.permission_overwrites.get(member.id);
if (premission) {
const perm = premission.getPermission(name);
if (perm) {
return perm === 1;
}
}
for (const thing of roles) {
const premission = this.permission_overwrites.get(thing.id);
if (premission) {
@@ -2184,15 +2201,14 @@ class Channel extends SnowFlake {
this.permission_overwrites = new Map();
this.permission_overwritesar = [];
for (const thing of json.permission_overwrites) {
if (thing.id === "1182819038095799904" || thing.id === "1182820803700625444") {
continue;
}
this.permission_overwrites.set(thing.id, new Permissions(thing.allow, thing.deny));
const permisions = this.permission_overwrites.get(thing.id);
if (permisions) {
const role = this.guild.roleids.get(thing.id);
if (role) {
this.permission_overwritesar.push([role, permisions]);
} else {
this.permission_overwritesar.push([this.localuser.getUser(thing.id), permisions]);
}
}
}
@@ -2202,6 +2218,11 @@ class Channel extends SnowFlake {
const role = this.guild.roleids.get(thing);
if (role) {
this.croleUpdate(role, new Permissions("0"), false);
} else {
const user = this.localuser.getUser(thing);
user.then((_) => {
if (_) this.croleUpdate(_, new Permissions("0"), false);
});
}
}
for (const thing of pchange) {
@@ -2209,13 +2230,18 @@ class Channel extends SnowFlake {
const perms = this.permission_overwrites.get(thing);
if (role && perms) {
this.croleUpdate(role, perms, true);
} else if (perms) {
const user = this.localuser.getUser(thing);
user.then((_) => {
if (_) this.croleUpdate(_, perms, true);
});
}
}
console.log(pchange, nchange);
this.topic = json.topic;
this.nsfw = json.nsfw;
}
croleUpdate: (role: Role, perm: Permissions, added: boolean) => unknown = () => {};
croleUpdate: (role: Role | User, perm: Permissions, added: boolean) => unknown = () => {};
typingstart() {
if (this.typing > Date.now()) {
return;
@@ -2633,7 +2659,7 @@ class Channel extends SnowFlake {
}
}
voiceMode: "VoiceOnly" | "ChatAndVoice" = "VoiceOnly";
async addRoleToPerms(role: Role) {
async addRoleToPerms(role: Role | User) {
await fetch(this.info.api + "/channels/" + this.id + "/permissions/" + role.id, {
method: "PUT",
headers: this.headers,
@@ -2641,12 +2667,15 @@ class Channel extends SnowFlake {
allow: "0",
deny: "0",
id: role.id,
type: 0,
type: role instanceof User ? 1 : 0,
}),
});
const perm = new Permissions("0", "0");
this.permission_overwrites.set(role.id, perm);
this.permission_overwritesar.push([role, perm]);
this.permission_overwritesar.push([
role instanceof User ? new Promise<User>((res) => res(role)) : role,
perm,
]);
}
async updateRolePermissions(id: string, perms: Permissions) {
const permission = this.permission_overwrites.get(id);
@@ -2663,7 +2692,7 @@ class Channel extends SnowFlake {
allow: perms.allow.toString(),
deny: perms.deny.toString(),
id,
type: 0,
type: this.localuser.userMap.get(id) ? 1 : 0,
}),
});
}
+5 -5
View File
@@ -15,7 +15,7 @@ interface menuPart<x, y> {
}
class ContextButton<x, y> implements menuPart<x, y> {
private text: string | ((this: x) => string);
private text: string | ((this: x, arg: y) => string);
private onClick: (this: x, arg: y, e: MouseEvent) => void;
private icon?: iconJson;
private visable?: (this: x, arg: y) => boolean;
@@ -50,7 +50,7 @@ class ContextButton<x, y> implements menuPart<x, y> {
const intext = document.createElement("button");
intext.classList.add("contextbutton");
intext.append(this.textContent(obj1));
intext.append(this.textContent(obj1, obj2));
intext.disabled = !!this.enabled && !this.enabled.call(obj1, obj2);
@@ -95,9 +95,9 @@ class ContextButton<x, y> implements menuPart<x, y> {
menu.append(intext);
}
textContent(x: x) {
textContent(x: x, y: y) {
if (this.text instanceof Function) {
return this.text.call(x);
return this.text.call(x, y);
}
return this.text;
}
@@ -179,7 +179,7 @@ class Contextmenu<x, y> {
for (const button of this.buttons) {
button.makeContextHTML(addinfo, other, div);
}
if (div.children[div.children.length - 1].tagName === "HR") {
if (div.children[div.children.length - 1]?.tagName === "HR") {
div.children[div.children.length - 1].remove();
}
//NOTE I don't know if this'll ever actually happen in reality
+55
View File
@@ -20,6 +20,7 @@ import {
GuildOverrides,
commandJson,
applicationJson,
presencejson,
} from "./jsontypes.js";
import {User} from "./user.js";
import {I18n} from "./i18n.js";
@@ -489,6 +490,60 @@ class Guild extends SnowFlake {
};
loadResults();
}
async searchMembers(limit: number, query: string): Promise<Member[]> {
if (this.id !== "@me") {
return new Promise<Member[]>((res) => {
const nonce = Math.floor(Math.random() * 10 ** 8) + "";
this.localuser.ws!.send(
JSON.stringify({
op: 8,
d: {
guild_id: [this.id],
query,
limit,
presences: true,
nonce,
},
}),
);
this.localuser.searchMap.set(
nonce,
async (e: {
chunk_index: number;
chunk_count: number;
nonce: string;
not_found?: string[];
members?: memberjson[];
presences: presencejson[];
}) => {
console.log(e);
if (e.members && e.members[0]) {
if (e.members[0].user) {
res(
(await Promise.all(e.members.map(async (_) => await Member.new(_, this)))).filter(
(_) => _ !== undefined,
),
);
} else {
const prom1: Promise<User>[] = [];
for (const thing of e.members) {
prom1.push(this.localuser.getUser(thing.id));
}
await Promise.all(prom1);
res(
(await Promise.all(e.members.map(async (_) => await Member.new(_, this)))).filter(
(_) => _ !== undefined,
),
);
}
}
return [];
},
);
});
}
return [];
}
generateSettings() {
const settings = new Settings(I18n.guild.settingsFor(this.properties.name));
const textChannels = this.channels.filter((e) => {
+3 -2
View File
@@ -148,7 +148,8 @@ async function handleEnter(event: KeyboardEvent): Promise<void> {
const channel = thisUser.channelfocus;
if (!channel) return;
if (markdown.rawString === "" && event.key === "ArrowUp") {
const content = MarkDown.gatherBoxText(typebox);
if (content === "" && event.key === "ArrowUp") {
channel.editLast();
return;
}
@@ -168,7 +169,7 @@ async function handleEnter(event: KeyboardEvent): Promise<void> {
thisUser.channelfocus.replyingto = null;
}
channel.sendMessage(markdown.rawString, {
channel.sendMessage(content, {
attachments: images.filter((_) => document.contains(imagesHtml.get(_) || null)),
embeds: [], // Add an empty array for the embeds property
replyingto: replyingTo,
+11 -38
View File
@@ -114,10 +114,17 @@ class Localuser {
onswap?.(thisUser);
}
static userMenu = this.generateUserMenu();
userResMap = new Map<string, Promise<User>>();
async getUser(id: string) {
let user = this.userMap.get(id);
if (user) return user;
return User.resolve(id, this);
const cache = this.userResMap.get(id);
if (cache) return cache;
const prom = User.resolve(id, this);
this.userResMap.set(id, prom);
await prom;
this.userResMap.delete(id);
return prom;
}
static generateUserMenu() {
const menu = new Contextmenu<Localuser, void>("");
@@ -3579,45 +3586,11 @@ class Localuser {
MDFindMention(name: string, original: string, box: HTMLDivElement, typebox: MarkDown) {
if (this.ws && this.lookingguild) {
this.MDFineMentionGen(name, original, box, typebox);
const nonce = Math.floor(Math.random() * 10 ** 8) + "";
if (this.lookingguild.member_count <= this.lookingguild.members.size) return;
if (this.lookingguild.id !== "@me") {
this.ws.send(
JSON.stringify({
op: 8,
d: {
guild_id: [this.lookingguild.id],
query: name,
limit: 8,
presences: true,
nonce,
},
}),
);
this.searchMap.set(nonce, async (e) => {
console.log(e);
if (e.members && e.members[0]) {
if (e.members[0].user) {
for (const thing of e.members) {
await Member.new(thing, this.lookingguild as Guild);
}
} else {
const prom1: Promise<User>[] = [];
for (const thing of e.members) {
prom1.push(this.getUser(thing.id));
}
Promise.all(prom1);
for (const thing of e.members) {
if (!this.userMap.has(thing.id)) {
console.warn("Dumb server bug for this member", thing);
continue;
}
await Member.new(thing, this.lookingguild as Guild);
}
}
if (!typebox.rawString.startsWith(original)) return;
this.MDFineMentionGen(name, original, box, typebox);
}
this.lookingguild.searchMembers(8, name).then(async () => {
if (!typebox.rawString.startsWith(original)) return;
this.MDFineMentionGen(name, original, box, typebox);
});
}
}
+36 -21
View File
@@ -129,6 +129,7 @@ import {Options} from "./settings.js";
import {createImg} from "./utils/utils.js";
import {Hover} from "./hover.js";
import {Emoji} from "./emoji.js";
import {User} from "./user.js";
class PermissionToggle implements OptionsElement<number> {
readonly rolejson: {
name: string;
@@ -206,13 +207,13 @@ class PermissionToggle implements OptionsElement<number> {
}
class RoleList extends Buttons {
permissions: [Role, Permissions][];
permissions: [Role | User, Permissions][];
permission: Permissions;
readonly guild: Guild;
readonly channel: false | Channel;
declare buttons: [string, string][];
readonly options: Options;
onchange: Function;
onchange: (id: string, perms: Permissions) => void;
curid?: string;
get info() {
return this.guild.info;
@@ -221,9 +222,9 @@ class RoleList extends Buttons {
return this.guild.headers;
}
constructor(
permissions: [Role, Permissions][],
permissions: [Role | User, Permissions][],
guild: Guild,
onchange: Function,
onchange: (id: string, perms: Permissions) => void,
channel: false | Channel,
) {
super("");
@@ -270,7 +271,7 @@ class RoleList extends Buttons {
}
this.redoButtons();
}
private croleUpdate(role: Role, perm: Permissions, added: boolean) {
private croleUpdate(role: Role | User, perm: Permissions, added: boolean) {
if (added) {
this.permissions.push([role, perm]);
} else {
@@ -400,17 +401,20 @@ class RoleList extends Buttons {
secondary_color: colorInputs[1] ? Number("0x" + colors[1].substring(1)) : undefined,
tertiary_color: colorInputs[2] ? Number("0x" + colors[2].substring(1)) : undefined,
};
console.log(obj.color);
});
});
}
static channelrolemenu = this.ChannelRoleMenu();
static guildrolemenu = this.GuildRoleMenu();
private static ChannelRoleMenu() {
const menu = new Contextmenu<RoleList, Role>("role settings");
const menu = new Contextmenu<RoleList, Role | User>("role settings");
menu.addButton(
() => I18n.role.remove(),
function (user) {
if (user instanceof User) {
return I18n.user.remove();
}
return I18n.role.remove();
},
function (role) {
if (!this.channel) return;
console.log(role);
@@ -420,21 +424,24 @@ class RoleList extends Buttons {
});
},
{
visable: (role) => role.id !== role.guild.id,
visable: function (role) {
//TODO, maybe this needs a check if the user is above/bellow the other user, hard to say
return role.id !== this.guild.id;
},
},
);
return menu;
}
deleteRole(role: Role) {
deleteRole(role: Role | User) {
const dio = new Dialog(I18n.role.confirmDelete(role.name));
const opt = dio.options.addOptions("", {ltr: true});
opt.addButtonInput("", I18n.yes(), async () => {
opt.removeAll();
opt.addText(I18n.role.deleting());
await fetch(role.info.api + "/guilds/" + role.guild.id + "/roles/" + role.id, {
await fetch(role.info.api + "/guilds/" + this.guild.id + "/roles/" + role.id, {
method: "DELETE",
headers: role.headers,
headers: this.guild.headers,
});
if (this.curid === role.id) {
const id = this.permissions.filter((_) => _[0].id !== role.id)[0][0].id;
@@ -463,13 +470,15 @@ class RoleList extends Buttons {
}
redoButtons() {
this.buttons = [];
this.permissions.sort(([a], [b]) => b.position - a.position);
this.permissions.sort(([a], [b]) => {
if (b instanceof User) return 1;
if (a instanceof User) return -1;
return 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;
@@ -548,7 +557,13 @@ class RoleList extends Buttons {
}
roles.push([role, [role.name]]);
}
const search = new Search(roles);
const search = new Search<Role | User>(roles, async (str) => {
const users = (await this.guild.searchMembers(3, str))
.map((_) => _.user)
.map((_) => [_.name, _] as [string, User]);
console.log(users);
return users;
});
const found = await search.find(box.left, box.top);
@@ -590,16 +605,16 @@ class RoleList extends Buttons {
this.buttonMap.set(thing[0], button);
button.classList.add("SettingsButton");
button.textContent = thing[0];
const role = this.guild.roleids.get(thing[1]);
const role = this.guild.roleids.get(thing[1]) || this.guild.localuser.userMap.get(thing[1]);
if (role) {
if (!this.channel) {
if (role.canManage()) {
if (role instanceof Role && role.canManage()) {
this.buttonDragEvents(button, role);
button.draggable = true;
RoleList.guildrolemenu.bindContextmenu(button, this, role);
}
} else {
if (role.canManage()) {
if (role instanceof User || role.canManage()) {
RoleList.channelrolemenu.bindContextmenu(button, this, role);
}
}
@@ -639,7 +654,7 @@ class RoleList extends Buttons {
return this.options.generateHTML();
}
save() {
if (this.options.subOptions) return;
if (this.options.subOptions || !this.curid) return;
this.onchange(this.curid, this.permission);
}
}
+42 -10
View File
@@ -3,29 +3,61 @@ import {Contextmenu} from "./contextmenu.js";
class Search<E> {
options: Map<string, E>;
readonly keys: string[];
constructor(options: [E, string[]][]) {
dynamic: (str: string) => Promise<[string, E][]> | [string, E][];
constructor(
options: [E, string[]][],
dynamic: (str: string) => Promise<[string, E][]> | [string, E][] = async () => [],
) {
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()];
this.dynamic = dynamic;
}
generateList(str: string, max: number, res: (e: E) => void) {
str = str.toLowerCase();
const options = this.keys.filter((e) => {
let options = this.keys.filter((e) => {
return e.toLowerCase().includes(str);
});
const dyn = this.dynamic(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);
const cmap = new Map<string, E>();
const genDiv = () => {
div.innerHTML = "";
for (const option of options.slice(0, max)) {
const hoption = document.createElement("span");
hoption.textContent = option;
hoption.onclick = () => {
if (cmap.has(option)) {
res(cmap.get(option) as E);
}
if (!this.options.has(option)) return;
res(this.options.get(option) as E);
};
div.append(hoption);
}
};
if (dyn instanceof Promise) {
genDiv();
dyn.then((arr) => {
options = [...options, ...arr.map(([_]) => _)];
for (const thing of arr) {
cmap.set(...thing);
}
genDiv();
});
} else {
const arr = dyn;
options = [...options, ...arr.map(([_]) => _)];
for (const thing of arr) {
cmap.set(...thing);
}
genDiv();
}
return div;
}
+12 -7
View File
@@ -11,7 +11,7 @@ import {I18n} from "./i18n.js";
import {Direct} from "./direct.js";
import {Hover} from "./hover.js";
import {Dialog, Float} from "./settings.js";
import {createImg, removeAni} from "./utils/utils.js";
import {createImg, removeAni, safeImg} from "./utils/utils.js";
import {Permissions} from "./permissions.js";
class User extends SnowFlake {
owner: Localuser;
@@ -32,7 +32,7 @@ class User extends SnowFlake {
hypotheticalbanner!: boolean;
premium_since!: string;
premium_type!: number;
theme_colors!: string;
theme_colors: [number, number] | null = null;
badge_ids!: string[];
members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> = new WeakMap();
status!: string;
@@ -558,6 +558,16 @@ class User extends SnowFlake {
}
userupdate(json: userjson): void {
if (json.avatar !== this.avatar) {
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach((element) => {
const img = element as safeImg;
if ("setSrcs" in element) {
img.setSrcs(this.getpfpsrc());
} else {
console.warn("element didn't have setSrcs property");
}
});
}
for (const key of Object.keys(json)) {
if (key === "bio") {
this.bio = new MarkDown(json[key], this.localuser);
@@ -647,11 +657,6 @@ class User extends SnowFlake {
changepfp(update: string | null): void {
this.avatar = update;
this.hypotheticalpfp = false;
//const src = this.getpfpsrc();
Array.from(document.getElementsByClassName("userid:" + this.id)).forEach((_element) => {
//(element as HTMLImageElement).src = src;
//FIXME
});
}
async block() {
+5 -1
View File
@@ -674,11 +674,15 @@ export async function removeAni(elm: HTMLElement, time = 500) {
]);
elm.remove();
}
export type safeImg = HTMLImageElement & {
setSrcs: (nsrc: string, nstaticsrc: string | void) => void;
isAnimated: () => Promise<boolean>;
};
export function createImg(
src: string | undefined,
staticsrc: string | void,
elm: HTMLElement | void,
) {
): safeImg {
const settings =
localStorage.getItem("gifSetting") || ("hover" as "hover") || "always" || "never";
const img = document.createElement("img");
+2 -1
View File
@@ -670,7 +670,8 @@
"editServerProfile": "Edit guild profile",
"instanceBan": "Instance ban",
"confirmInstBan": "Are you sure you want to instance ban $1?",
"unban": "Unban $1"
"unban": "Unban $1",
"remove":"Remove user"
},
"login": {
"checking": "Checking Instance",