906 lines
23 KiB
TypeScript
906 lines
23 KiB
TypeScript
import {Member} from "./member.js";
|
|
import {MarkDown} from "./markdown.js";
|
|
import {Contextmenu} from "./contextmenu.js";
|
|
import {Localuser} from "./localuser.js";
|
|
import {Guild} from "./guild.js";
|
|
import {SnowFlake} from "./snowflake.js";
|
|
import {presencejson, userjson, webhookInfo} from "./jsontypes.js";
|
|
import {Role} from "./role.js";
|
|
import {Search} from "./search.js";
|
|
import {I18n} from "./i18n.js";
|
|
import {Direct} from "./direct.js";
|
|
import {Hover} from "./hover.js";
|
|
import {Dialog} from "./settings.js";
|
|
import {createImg} from "./utils/utils.js";
|
|
|
|
class User extends SnowFlake {
|
|
owner: Localuser;
|
|
hypotheticalpfp!: boolean;
|
|
avatar!: string | null;
|
|
uid: string;
|
|
username!: string;
|
|
nickname: string | null = null;
|
|
relationshipType: 0 | 1 | 2 | 3 | 4 | 5 | 6 = 0;
|
|
bio!: MarkDown;
|
|
discriminator!: string;
|
|
pronouns?: string;
|
|
bot!: boolean;
|
|
public_flags!: number;
|
|
webhook?: webhookInfo;
|
|
accent_color!: number;
|
|
banner: string | undefined;
|
|
hypotheticalbanner!: boolean;
|
|
premium_since!: string;
|
|
premium_type!: number;
|
|
theme_colors!: string;
|
|
badge_ids!: string[];
|
|
members: WeakMap<Guild, Member | undefined | Promise<Member | undefined>> = new WeakMap();
|
|
status!: string;
|
|
resolving: false | Promise<any> = false;
|
|
|
|
constructor(userjson: userjson, owner: Localuser, dontclone: boolean = false) {
|
|
super(userjson.id);
|
|
this.owner = owner;
|
|
if (localStorage.getItem("logbad") && owner.user && owner.user.id !== userjson.id) {
|
|
this.checkfortmi(userjson);
|
|
}
|
|
if (!owner) {
|
|
console.error("missing localuser");
|
|
}
|
|
this.uid = userjson.id;
|
|
if (userjson.webhook) {
|
|
this.uid += ":::" + userjson.username;
|
|
console.log(this.uid);
|
|
}
|
|
userjson.uid = this.uid;
|
|
if (dontclone) {
|
|
this.userupdate(userjson);
|
|
this.hypotheticalpfp = false;
|
|
} else {
|
|
return User.checkuser(userjson, owner);
|
|
}
|
|
}
|
|
/**
|
|
* function is meant to check if userjson contains too much information IE non-public stuff
|
|
*
|
|
*
|
|
*/
|
|
checkfortmi(json: any) {
|
|
if (json.data) {
|
|
console.error("Server sent *way* too much info, this is really bad, it sent data");
|
|
}
|
|
const bad = new Set([
|
|
"fingerprints",
|
|
"extended_settings",
|
|
"mfa_enabled",
|
|
"nsfw_allowed",
|
|
"premium_usage_flags",
|
|
"totp_last_ticket",
|
|
"totp_secret",
|
|
"webauthn_enabled",
|
|
]);
|
|
if (!this.localuser.rights.getPermission("OPERATOR")) {
|
|
//Unless the user is an operator, we really shouldn't ever see this
|
|
bad.add("rights");
|
|
}
|
|
for (const thing of bad) {
|
|
if (json.hasOwnProperty(thing)) {
|
|
console.error(thing + " should not be exposed to the client");
|
|
}
|
|
}
|
|
}
|
|
tojson(): userjson {
|
|
return {
|
|
username: this.username,
|
|
id: this.id,
|
|
public_flags: this.public_flags,
|
|
discriminator: this.discriminator,
|
|
avatar: this.avatar,
|
|
accent_color: this.accent_color,
|
|
banner: this.banner,
|
|
bio: this.bio.rawString,
|
|
premium_since: this.premium_since,
|
|
premium_type: this.premium_type,
|
|
bot: this.bot,
|
|
theme_colors: this.theme_colors,
|
|
pronouns: this.pronouns,
|
|
badge_ids: this.badge_ids,
|
|
};
|
|
}
|
|
|
|
clone(): User {
|
|
const json = this.tojson();
|
|
json.id += "#clone";
|
|
return new User(json, this.owner);
|
|
}
|
|
|
|
public getPresence(presence: presencejson | undefined): void {
|
|
if (presence) {
|
|
this.setstatus(presence.status);
|
|
} else {
|
|
this.setstatus("offline");
|
|
}
|
|
}
|
|
get online() {
|
|
return this.status && this.status != "offline";
|
|
}
|
|
setstatus(status: string): void {
|
|
if (this.id === this.localuser.user.id) {
|
|
console.warn(status);
|
|
}
|
|
this.status = status;
|
|
}
|
|
|
|
getStatus(): string {
|
|
return this.status || "offline";
|
|
}
|
|
|
|
static contextmenu = new Contextmenu<User, Member | undefined>("User Menu");
|
|
async opendm() {
|
|
for (const dm of (this.localuser.guildids.get("@me") as Direct).channels) {
|
|
if (dm.user.id === this.id) {
|
|
this.localuser.goToChannel(dm.id);
|
|
return;
|
|
}
|
|
}
|
|
|
|
await fetch(this.info.api + "/users/@me/channels", {
|
|
method: "POST",
|
|
body: JSON.stringify({recipients: [this.id]}),
|
|
headers: this.localuser.headers,
|
|
})
|
|
.then((res) => res.json())
|
|
.then((json) => {
|
|
this.localuser.goToChannel(json.id);
|
|
});
|
|
return;
|
|
}
|
|
async changeRelationship(type: 0 | 1 | 2 | 3 | 4 | 5) {
|
|
if (type !== 0) {
|
|
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
|
method: "PUT",
|
|
headers: this.owner.headers,
|
|
body: JSON.stringify({
|
|
type,
|
|
}),
|
|
});
|
|
} else {
|
|
await fetch(`${this.info.api}/users/@me/relationships/${this.id}`, {
|
|
method: "DELETE",
|
|
headers: this.owner.headers,
|
|
});
|
|
}
|
|
this.relationshipType = type;
|
|
}
|
|
static setUpContextMenu(): void {
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.message"),
|
|
function (this: User) {
|
|
this.opendm();
|
|
},
|
|
{
|
|
icon: {
|
|
css: "svg-frmessage",
|
|
},
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addSeperator();
|
|
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.block"),
|
|
function (this: User) {
|
|
this.block();
|
|
},
|
|
{
|
|
visable: function () {
|
|
return this.relationshipType !== 2 && this.id !== this.localuser.user.id;
|
|
},
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.unblock"),
|
|
function (this: User) {
|
|
this.unblock();
|
|
},
|
|
{
|
|
visable: function () {
|
|
return this.relationshipType === 2 && this.id !== this.localuser.user.id;
|
|
},
|
|
},
|
|
);
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.friendReq"),
|
|
function (this: User) {
|
|
this.changeRelationship(1);
|
|
},
|
|
{
|
|
visable: function () {
|
|
return (
|
|
(this.relationshipType === 0 || this.relationshipType === 3) &&
|
|
this.id !== this.localuser.user.id &&
|
|
!this.bot
|
|
);
|
|
},
|
|
icon: {
|
|
css: "svg-addfriend",
|
|
},
|
|
},
|
|
);
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("friends.removeFriend"),
|
|
function (this: User) {
|
|
this.changeRelationship(0);
|
|
},
|
|
{
|
|
visable: function () {
|
|
return this.relationshipType === 1 && this.id !== this.localuser.user.id;
|
|
},
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addSeperator();
|
|
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.editServerProfile"),
|
|
function (this: User, member: Member | undefined) {
|
|
if (!member) return;
|
|
member.showEditProfile();
|
|
},
|
|
{
|
|
visable: function (member) {
|
|
return member?.id === this.localuser.user.id;
|
|
},
|
|
},
|
|
);
|
|
|
|
//TODO kick icon
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.kick"),
|
|
function (this: User, member: Member | undefined) {
|
|
member?.kick();
|
|
},
|
|
{
|
|
visable: function (member) {
|
|
if (!member) return false;
|
|
const us = member.guild.member;
|
|
if (member.id === us.id) {
|
|
return false;
|
|
}
|
|
if (member.id === member.guild.properties.owner_id) {
|
|
return false;
|
|
}
|
|
return us.hasPermission("KICK_MEMBERS") && this.id !== this.localuser.user.id;
|
|
},
|
|
color: "red",
|
|
},
|
|
);
|
|
|
|
//TODO ban icon
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.ban"),
|
|
function (this: User, member: Member | undefined) {
|
|
member?.ban();
|
|
},
|
|
{
|
|
visable: function (member) {
|
|
if (!member) return false;
|
|
const us = member.guild.member;
|
|
if (member.id === us.id) {
|
|
return false;
|
|
}
|
|
if (member.id === member.guild.properties.owner_id) {
|
|
return false;
|
|
}
|
|
return us.hasPermission("BAN_MEMBERS") && this.id !== this.localuser.user.id;
|
|
},
|
|
color: "red",
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addSeperator();
|
|
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.addRole"),
|
|
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);
|
|
}
|
|
},
|
|
{
|
|
visable: (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(
|
|
() => I18n.getTranslation("user.removeRole"),
|
|
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);
|
|
}
|
|
},
|
|
{
|
|
visable: (member) => {
|
|
if (!member) return false;
|
|
const us = member.guild.member;
|
|
console.log(us.hasPermission("MANAGE_ROLES"));
|
|
return us.hasPermission("MANAGE_ROLES") || false;
|
|
},
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addSeperator();
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.copyId"),
|
|
function (this: User) {
|
|
navigator.clipboard.writeText(this.id);
|
|
},
|
|
);
|
|
|
|
this.contextmenu.addSeperator();
|
|
|
|
this.contextmenu.addButton(
|
|
() => I18n.user.instanceBan(),
|
|
function (this: User) {
|
|
const menu = new Dialog("");
|
|
const options = menu.float.options;
|
|
options.addTitle(I18n.user.confirmInstBan(this.name));
|
|
const opt = options.addOptions("", {ltr: true});
|
|
opt.addButtonInput("", I18n.yes(), () => {
|
|
fetch(this.info.api + "/users/" + this.id + "/delete", {
|
|
headers: this.localuser.headers,
|
|
method: "POST",
|
|
});
|
|
menu.hide();
|
|
});
|
|
opt.addButtonInput("", I18n.no(), () => {
|
|
menu.hide();
|
|
});
|
|
menu.show();
|
|
},
|
|
{
|
|
visable: function () {
|
|
return this.localuser.rights.hasPermission("MANAGE_USERS");
|
|
},
|
|
color: "red",
|
|
},
|
|
);
|
|
console.warn("this ran");
|
|
}
|
|
|
|
static checkuser(user: User | userjson, owner: Localuser): User {
|
|
const tempUser = owner.userMap.get(user.uid || user.id);
|
|
if (tempUser) {
|
|
if (!(user instanceof User)) {
|
|
tempUser.userupdate(user);
|
|
}
|
|
return tempUser;
|
|
} else {
|
|
const tempuser = new User(user as userjson, owner, true);
|
|
owner.userMap.set(user.uid || user.id, tempuser);
|
|
return tempuser;
|
|
}
|
|
}
|
|
|
|
get info() {
|
|
return this.owner.info;
|
|
}
|
|
|
|
get localuser() {
|
|
return this.owner;
|
|
}
|
|
|
|
get name() {
|
|
return this.username;
|
|
}
|
|
|
|
async resolvemember(guild: Guild): Promise<Member | undefined> {
|
|
return await Member.resolveMember(this, guild);
|
|
}
|
|
|
|
async getUserProfile(): Promise<any> {
|
|
return await fetch(
|
|
`${this.info.api}/users/${this.id.replace(
|
|
"#clone",
|
|
"",
|
|
)}/profile?with_mutual_guilds=true&with_mutual_friends=true`,
|
|
{
|
|
headers: this.localuser.headers,
|
|
},
|
|
).then((res) => res.json());
|
|
}
|
|
|
|
async getBadge(id: string) {
|
|
if (this.localuser.badges.has(id)) {
|
|
return this.localuser.badges.get(id);
|
|
} else {
|
|
if (this.resolving) {
|
|
await this.resolving;
|
|
return this.localuser.badges.get(id);
|
|
}
|
|
|
|
const prom = await this.getUserProfile();
|
|
this.resolving = prom;
|
|
const badges = prom.badges;
|
|
this.resolving = false;
|
|
for (const badge of badges) {
|
|
this.localuser.badges.set(badge.id, badge);
|
|
}
|
|
return this.localuser.badges.get(id);
|
|
}
|
|
}
|
|
|
|
buildpfp(guild: Guild | void | Member | null, hoverElm: void | HTMLElement): HTMLImageElement {
|
|
const pfp = createImg(this.getpfpsrc(), undefined, hoverElm);
|
|
pfp.loading = "lazy";
|
|
pfp.classList.add("pfp");
|
|
pfp.classList.add("userid:" + this.id);
|
|
if (guild) {
|
|
(async () => {
|
|
if (guild instanceof Guild) {
|
|
const memb = await Member.resolveMember(this, guild);
|
|
if (!memb) return;
|
|
pfp.setSrcs(memb.getpfpsrc());
|
|
} else {
|
|
pfp.setSrcs(guild.getpfpsrc());
|
|
}
|
|
})();
|
|
}
|
|
return pfp;
|
|
}
|
|
createWidget(guild: Guild) {
|
|
const div = document.createElement("div");
|
|
div.classList.add("flexltr", "createdWebhook");
|
|
//TODO make sure this is something I can actually do here
|
|
const name = document.createElement("b");
|
|
name.textContent = this.name;
|
|
const nameBox = document.createElement("div");
|
|
nameBox.classList.add("flexttb");
|
|
nameBox.append(name);
|
|
const pfp = this.buildpfp(undefined, div);
|
|
div.append(pfp, nameBox);
|
|
Member.resolveMember(this, guild).then((_) => {
|
|
if (_) {
|
|
name.textContent = _.name;
|
|
pfp.src = _.getpfpsrc();
|
|
} else {
|
|
const notFound = document.createElement("span");
|
|
notFound.textContent = I18n.webhooks.notFound();
|
|
nameBox.append(notFound);
|
|
}
|
|
});
|
|
this.bind(div, guild);
|
|
return div;
|
|
}
|
|
buildstatuspfp(guild: Guild | void | Member | null): HTMLDivElement {
|
|
const div = document.createElement("div");
|
|
div.classList.add("pfpDiv");
|
|
const pfp = this.buildpfp(guild, div);
|
|
div.append(pfp);
|
|
const status = document.createElement("div");
|
|
status.classList.add("statusDiv");
|
|
switch (this.getStatus()) {
|
|
case "offline":
|
|
case "invisible":
|
|
status.classList.add("offlinestatus");
|
|
break;
|
|
case "online":
|
|
default:
|
|
status.classList.add("onlinestatus");
|
|
break;
|
|
}
|
|
div.append(status);
|
|
return div;
|
|
}
|
|
|
|
userupdate(json: userjson): void {
|
|
for (const key of Object.keys(json)) {
|
|
if (key === "bio") {
|
|
this.bio = new MarkDown(json[key], this.localuser);
|
|
continue;
|
|
}
|
|
if (key === "id") {
|
|
continue;
|
|
}
|
|
(this as any)[key] = (json as any)[key];
|
|
}
|
|
if ("rights" in this) {
|
|
if (
|
|
this === this.localuser.user &&
|
|
(typeof this.rights == "string" || typeof this.rights == "number")
|
|
) {
|
|
this.localuser.updateRights(this.rights);
|
|
}
|
|
}
|
|
}
|
|
|
|
bind(html: HTMLElement, guild: Guild | null = null, error = true): void {
|
|
if (guild && guild.id !== "@me") {
|
|
Member.resolveMember(this, guild)
|
|
.then((member) => {
|
|
User.contextmenu.bindContextmenu(html, this, member);
|
|
if (member === undefined && error) {
|
|
if (this.webhook) return;
|
|
const errorSpan = document.createElement("span");
|
|
errorSpan.textContent = "!";
|
|
errorSpan.classList.add("membererror");
|
|
html.after(errorSpan);
|
|
return;
|
|
}
|
|
if (member) {
|
|
member.bind(html);
|
|
} else {
|
|
User.contextmenu.bindContextmenu(html, this, undefined);
|
|
}
|
|
})
|
|
.catch((err) => {
|
|
console.log(err);
|
|
});
|
|
} else {
|
|
User.contextmenu.bindContextmenu(html, this, undefined);
|
|
}
|
|
if (guild) {
|
|
this.profileclick(html, guild);
|
|
} else {
|
|
this.profileclick(html);
|
|
}
|
|
}
|
|
|
|
static async resolve(id: string, localuser: Localuser): Promise<User> {
|
|
const json = await fetch(localuser.info.api.toString() + "/users/" + id + "/profile", {
|
|
headers: localuser.headers,
|
|
}).then((res) => res.json());
|
|
if (json.code === 404) {
|
|
return new User(
|
|
{
|
|
id: "0",
|
|
public_flags: 0,
|
|
username: I18n.friends.notfound(),
|
|
avatar: null,
|
|
discriminator: "0000",
|
|
bio: "",
|
|
bot: false,
|
|
premium_type: 0,
|
|
premium_since: "",
|
|
accent_color: 0,
|
|
theme_colors: "",
|
|
badge_ids: [],
|
|
},
|
|
localuser,
|
|
);
|
|
}
|
|
return new User(json.user, localuser);
|
|
}
|
|
|
|
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() {
|
|
await this.changeRelationship(2);
|
|
const channel = this.localuser.channelfocus;
|
|
if (channel) {
|
|
for (const message of channel.messages) {
|
|
message[1].generateMessage();
|
|
}
|
|
}
|
|
}
|
|
|
|
async unblock() {
|
|
await this.changeRelationship(0);
|
|
const channel = this.localuser.channelfocus;
|
|
if (channel) {
|
|
for (const message of channel.messages) {
|
|
message[1].generateMessage();
|
|
}
|
|
}
|
|
}
|
|
/**
|
|
* @param guild this is an optional thing that'll get the src of the member if it exists, otherwise ignores it, this is meant to be fast, not accurate
|
|
*/
|
|
getpfpsrc(guild: Guild | void): string {
|
|
if (this.hypotheticalpfp && this.avatar) {
|
|
return this.avatar;
|
|
}
|
|
if (guild) {
|
|
const member = this.members.get(guild);
|
|
if (member instanceof Member) {
|
|
return member.getpfpsrc();
|
|
}
|
|
}
|
|
if (this.avatar !== null) {
|
|
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${this.avatar}.png`;
|
|
} else {
|
|
const int = Number((BigInt(this.id.replace("#clone", "")) >> 22n) % 6n);
|
|
return `${this.info.cdn}/embed/avatars/${int}.png`;
|
|
}
|
|
}
|
|
async getBadges() {
|
|
let i = 0;
|
|
let flagbits = this.public_flags;
|
|
const ids = [
|
|
"staff",
|
|
"partner",
|
|
"certified_moderator",
|
|
"hypesquad",
|
|
"hypesquad_house_1",
|
|
"hypesquad_house_2",
|
|
"hypesquad_house_3",
|
|
"bug_hunter_level_1",
|
|
"bug_hunter_level_2",
|
|
"active_developer",
|
|
"verified_developer",
|
|
"early_supporter",
|
|
"premium",
|
|
"guild_booster_lvl1",
|
|
"guild_booster_lvl2",
|
|
"guild_booster_lvl3",
|
|
"guild_booster_lvl4",
|
|
"guild_booster_lvl5",
|
|
"guild_booster_lvl6",
|
|
"guild_booster_lvl7",
|
|
"guild_booster_lvl8",
|
|
"guild_booster_lvl9",
|
|
"bot_commands",
|
|
"automod",
|
|
"application_guild_subscription",
|
|
"legacy_username",
|
|
"quest_completed",
|
|
];
|
|
let badgeids: string[] = [];
|
|
while (flagbits !== 0) {
|
|
if (flagbits & 1) {
|
|
badgeids.push(ids[i]);
|
|
}
|
|
flagbits >>= 1;
|
|
i++;
|
|
}
|
|
if (this.badge_ids) {
|
|
badgeids = badgeids.concat(this.badge_ids);
|
|
}
|
|
|
|
let badges: {
|
|
id: string;
|
|
description: string;
|
|
icon: string;
|
|
link?: string;
|
|
translate?: boolean;
|
|
}[] = [];
|
|
|
|
const b = (await Promise.all(badgeids.map((_) => this.getBadge(_)))).filter(
|
|
(_) => _ !== undefined,
|
|
);
|
|
badges = b;
|
|
|
|
return badges;
|
|
}
|
|
async buildprofile(
|
|
x: number,
|
|
y: number,
|
|
guild: Guild | null | Member = null,
|
|
): Promise<HTMLDivElement> {
|
|
if (Contextmenu.currentmenu != "") {
|
|
Contextmenu.currentmenu.remove();
|
|
}
|
|
const membres = (async () => {
|
|
if (!guild) return;
|
|
let member: Member | undefined;
|
|
if (guild instanceof Guild) {
|
|
member = await Member.resolveMember(this, guild);
|
|
} else {
|
|
member = guild;
|
|
}
|
|
return member;
|
|
})();
|
|
const div = document.createElement("div");
|
|
|
|
if (this.accent_color) {
|
|
div.style.setProperty(
|
|
"--accent_color",
|
|
`#${this.accent_color.toString(16).padStart(6, "0")}`,
|
|
);
|
|
} else {
|
|
div.style.setProperty("--accent_color", "transparent");
|
|
}
|
|
const banner = this.getBanner(guild);
|
|
div.append(banner);
|
|
membres.then((member) => {
|
|
if (!member) return;
|
|
if (member.accent_color && member.accent_color !== 0) {
|
|
div.style.setProperty(
|
|
"--accent_color",
|
|
`#${member.accent_color.toString(16).padStart(6, "0")}`,
|
|
);
|
|
}
|
|
});
|
|
|
|
if (x !== -1) {
|
|
div.style.left = `${x}px`;
|
|
div.style.top = `${y}px`;
|
|
div.classList.add("profile", "flexttb");
|
|
} else {
|
|
this.setstatus("online");
|
|
div.classList.add("hypoprofile", "profile", "flexttb");
|
|
}
|
|
const badgediv = document.createElement("div");
|
|
badgediv.classList.add("badges");
|
|
(async () => {
|
|
const badges = await this.getBadges();
|
|
for (const badgejson of badges) {
|
|
const badge = document.createElement(badgejson.link ? "a" : "div");
|
|
badge.classList.add("badge");
|
|
let src: string;
|
|
if (URL.canParse(badgejson.icon)) {
|
|
src = badgejson.icon;
|
|
} else {
|
|
src = this.info.cdn + "/badge-icons/" + badgejson.icon + ".png";
|
|
}
|
|
const img = createImg(src, undefined, badgediv);
|
|
|
|
badge.append(img);
|
|
let hovertxt: string;
|
|
if (badgejson.translate) {
|
|
hovertxt = I18n.getTranslation("badge." + badgejson.description);
|
|
} else {
|
|
hovertxt = badgejson.description;
|
|
}
|
|
const hover = new Hover(hovertxt);
|
|
hover.addEvent(badge);
|
|
if (badgejson.link && badge instanceof HTMLAnchorElement) {
|
|
badge.href = badgejson.link;
|
|
}
|
|
badgediv.append(badge);
|
|
}
|
|
})();
|
|
const pfp = this.buildstatuspfp(guild);
|
|
div.appendChild(pfp);
|
|
const userbody = document.createElement("div");
|
|
userbody.classList.add("flexttb", "infosection");
|
|
div.appendChild(userbody);
|
|
const usernamehtml = document.createElement("h2");
|
|
usernamehtml.textContent = this.username;
|
|
userbody.appendChild(usernamehtml);
|
|
userbody.appendChild(badgediv);
|
|
const discrimatorhtml = document.createElement("h3");
|
|
discrimatorhtml.classList.add("tag");
|
|
discrimatorhtml.textContent = `${this.username}#${this.discriminator}`;
|
|
userbody.appendChild(discrimatorhtml);
|
|
|
|
const pronounshtml = document.createElement("p");
|
|
pronounshtml.textContent = this.pronouns || "";
|
|
pronounshtml.classList.add("pronouns");
|
|
userbody.appendChild(pronounshtml);
|
|
|
|
membres.then((member) => {
|
|
if (!member) return;
|
|
if (member.pronouns && member.pronouns !== "") {
|
|
pronounshtml.textContent = member.pronouns;
|
|
}
|
|
});
|
|
|
|
const rule = document.createElement("hr");
|
|
userbody.appendChild(rule);
|
|
const biohtml = this.bio.makeHTML();
|
|
userbody.appendChild(biohtml);
|
|
|
|
membres.then((member) => {
|
|
if (!member) return;
|
|
if (member.bio && member.bio !== "") {
|
|
//TODO make markdown take Guild
|
|
userbody.insertBefore(new MarkDown(member.bio, this.localuser).makeHTML(), biohtml);
|
|
biohtml.remove();
|
|
}
|
|
});
|
|
|
|
if (guild) {
|
|
membres.then((member) => {
|
|
if (!member) return;
|
|
usernamehtml.textContent = member.name;
|
|
const roles = document.createElement("div");
|
|
roles.classList.add("flexltr", "rolesbox");
|
|
for (const role of member.roles) {
|
|
if (role.id === member.guild.id) continue;
|
|
const roleDiv = document.createElement("div");
|
|
roleDiv.classList.add("rolediv");
|
|
const color = document.createElement("div");
|
|
roleDiv.append(color);
|
|
color.style.setProperty("--role-color", `#${role.color.toString(16).padStart(6, "0")}`);
|
|
color.classList.add("colorrolediv");
|
|
const span = document.createElement("span");
|
|
roleDiv.append(span);
|
|
span.textContent = role.name;
|
|
roles.append(roleDiv);
|
|
}
|
|
userbody.append(roles);
|
|
});
|
|
}
|
|
if (x !== -1) {
|
|
Contextmenu.currentmenu = div;
|
|
document.body.appendChild(div);
|
|
Contextmenu.keepOnScreen(div);
|
|
}
|
|
return div;
|
|
}
|
|
getBanner(guild: Guild | null | Member): HTMLImageElement {
|
|
const banner = createImg(undefined);
|
|
|
|
const bsrc = this.getBannerUrl();
|
|
if (bsrc) {
|
|
banner.setSrcs(bsrc);
|
|
banner.classList.add("banner");
|
|
}
|
|
|
|
if (guild) {
|
|
if (guild instanceof Member) {
|
|
const bsrc = guild.getBannerUrl();
|
|
if (bsrc) {
|
|
banner.setSrcs(bsrc);
|
|
banner.classList.add("banner");
|
|
}
|
|
} else {
|
|
Member.resolveMember(this, guild).then((memb) => {
|
|
if (!memb) return;
|
|
const bsrc = memb.getBannerUrl();
|
|
if (bsrc) {
|
|
banner.setSrcs(bsrc);
|
|
banner.classList.add("banner");
|
|
}
|
|
});
|
|
}
|
|
}
|
|
return banner;
|
|
}
|
|
getBannerUrl(): string | undefined {
|
|
if (this.banner) {
|
|
if (!this.hypotheticalbanner) {
|
|
return `${this.info.cdn}/avatars/${this.id.replace("#clone", "")}/${this.banner}.png`;
|
|
} else {
|
|
return this.banner;
|
|
}
|
|
} else {
|
|
return undefined;
|
|
}
|
|
}
|
|
profileclick(obj: HTMLElement, guild?: Guild): void {
|
|
obj.onclick = (e: MouseEvent) => {
|
|
this.buildprofile(e.clientX, e.clientY, guild);
|
|
e.stopPropagation();
|
|
};
|
|
}
|
|
}
|
|
|
|
User.setUpContextMenu();
|
|
export {User};
|