1127 lines
31 KiB
TypeScript
1127 lines
31 KiB
TypeScript
import {Channel} from "./channel.js";
|
|
import {Localuser} from "./localuser.js";
|
|
import {Contextmenu} from "./contextmenu.js";
|
|
import {Role, RoleList} from "./role.js";
|
|
import {Member} from "./member.js";
|
|
import {Dialog, Options, Settings} from "./settings.js";
|
|
import {Permissions} from "./permissions.js";
|
|
import {SnowFlake} from "./snowflake.js";
|
|
import {
|
|
channeljson,
|
|
guildjson,
|
|
memberjson,
|
|
invitejson,
|
|
rolesjson,
|
|
emojipjson,
|
|
extendedProperties,
|
|
} from "./jsontypes.js";
|
|
import {User} from "./user.js";
|
|
import {I18n} from "./i18n.js";
|
|
import {Emoji} from "./emoji.js";
|
|
import {webhookMenu} from "./webhooks.js";
|
|
import {createImg} from "./utils/utils.js";
|
|
|
|
class Guild extends SnowFlake {
|
|
owner!: Localuser;
|
|
headers!: Localuser["headers"];
|
|
channels!: Channel[];
|
|
properties!: guildjson["properties"];
|
|
member_count!: number;
|
|
roles!: Role[];
|
|
roleids!: Map<string, Role>;
|
|
prevchannel: Channel | undefined;
|
|
banner!: string;
|
|
message_notifications!: number;
|
|
headchannels!: Channel[];
|
|
position!: number;
|
|
parent_id!: string;
|
|
member!: Member;
|
|
html!: HTMLElement;
|
|
emojis!: emojipjson[];
|
|
large!: boolean;
|
|
members = new Set<Member>();
|
|
static contextmenu = new Contextmenu<Guild, undefined>("guild menu");
|
|
static setupcontextmenu() {
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.makeInvite"),
|
|
function (this: Guild) {
|
|
const d = new Dialog("");
|
|
this.makeInviteMenu(d.options);
|
|
d.show();
|
|
},
|
|
{
|
|
enabled: function () {
|
|
return this.member.hasPermission("CREATE_INSTANT_INVITE");
|
|
},
|
|
color: "blue",
|
|
},
|
|
);
|
|
Guild.contextmenu.addSeperator();
|
|
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.markRead"),
|
|
function (this: Guild) {
|
|
this.markAsRead();
|
|
},
|
|
);
|
|
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.notifications"),
|
|
function (this: Guild) {
|
|
this.setnotifcation();
|
|
},
|
|
);
|
|
Guild.contextmenu.addSeperator();
|
|
this.contextmenu.addButton(
|
|
() => I18n.getTranslation("user.editServerProfile"),
|
|
function () {
|
|
this.member.showEditProfile();
|
|
},
|
|
);
|
|
Guild.contextmenu.addSeperator();
|
|
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.leave"),
|
|
function (this: Guild) {
|
|
this.confirmleave();
|
|
},
|
|
{
|
|
visable: function (_) {
|
|
return this.properties.owner_id !== this.member.user.id;
|
|
},
|
|
color: "red",
|
|
},
|
|
);
|
|
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.delete"),
|
|
function (this: Guild) {
|
|
this.confirmDelete();
|
|
},
|
|
{
|
|
visable: function (_) {
|
|
return this.properties.owner_id === this.member.user.id;
|
|
},
|
|
color: "red",
|
|
icon: {
|
|
css: "svg-delete",
|
|
},
|
|
},
|
|
);
|
|
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.settings"),
|
|
function (this: Guild) {
|
|
this.generateSettings();
|
|
},
|
|
{
|
|
visable: function () {
|
|
return this.member.hasPermission("MANAGE_GUILD");
|
|
},
|
|
icon: {
|
|
css: "svg-settings",
|
|
},
|
|
},
|
|
);
|
|
|
|
Guild.contextmenu.addSeperator();
|
|
Guild.contextmenu.addButton(
|
|
() => I18n.getTranslation("guild.copyId"),
|
|
function (this: Guild) {
|
|
navigator.clipboard.writeText(this.id);
|
|
},
|
|
);
|
|
//TODO mute guild button
|
|
}
|
|
generateSettings() {
|
|
const settings = new Settings(I18n.getTranslation("guild.settingsFor", this.properties.name));
|
|
const textChannels = this.channels.filter((e) => {
|
|
//TODO there are almost certainly more types. is Voice valid?
|
|
return new Set([0, 5]).has(e.type);
|
|
});
|
|
{
|
|
const overview = settings.addButton(I18n.getTranslation("guild.overview"));
|
|
const form = overview.addForm("", (_) => {}, {
|
|
headers: this.headers,
|
|
traditionalSubmit: true,
|
|
fetchURL: this.info.api + "/guilds/" + this.id,
|
|
method: "PATCH",
|
|
});
|
|
form.addTextInput(I18n.getTranslation("guild.name:"), "name", {
|
|
initText: this.properties.name,
|
|
});
|
|
form.addMDInput(I18n.getTranslation("guild.description:"), "description", {
|
|
initText: this.properties.description,
|
|
});
|
|
|
|
form.addFileInput(I18n.getTranslation("guild.banner:"), "banner", {clear: true});
|
|
form.addFileInput(I18n.getTranslation("guild.icon:"), "icon", {clear: true});
|
|
|
|
form.addHR();
|
|
|
|
const sysmap = [null, ...textChannels.map((e) => e.id)];
|
|
form.addSelect(
|
|
I18n.getTranslation("guild.systemSelect:"),
|
|
"system_channel_id",
|
|
["No system messages", ...textChannels.map((e) => e.name)],
|
|
{defaultIndex: sysmap.indexOf(this.properties.system_channel_id)},
|
|
sysmap,
|
|
);
|
|
console.log(textChannels, this.channels);
|
|
const options: ["DISCOVERABLE", "COMMUNITY", "INVITES_DISABLED"] = [
|
|
"DISCOVERABLE",
|
|
"COMMUNITY",
|
|
"INVITES_DISABLED",
|
|
];
|
|
const defaultIndex = options.findIndex((_) => this.properties.features.includes(_));
|
|
form.addSelect(
|
|
I18n.guild.howJoin(),
|
|
"features",
|
|
options.map((_) => I18n.guild[_]()),
|
|
{
|
|
defaultIndex: defaultIndex == -1 ? 1 : defaultIndex,
|
|
},
|
|
options,
|
|
);
|
|
|
|
form.addCheckboxInput(I18n.getTranslation("guild.sendrandomwelcome?"), "s1", {
|
|
initState: !(this.properties.system_channel_flags & 1),
|
|
});
|
|
form.addCheckboxInput(I18n.getTranslation("guild.stickWelcomeReact?"), "s4", {
|
|
initState: !(this.properties.system_channel_flags & 8),
|
|
});
|
|
form.addCheckboxInput(I18n.getTranslation("guild.boostMessage?"), "s2", {
|
|
initState: !(this.properties.system_channel_flags & 2),
|
|
});
|
|
form.addCheckboxInput(I18n.getTranslation("guild.helpTips?"), "s3", {
|
|
initState: !(this.properties.system_channel_flags & 4),
|
|
});
|
|
form.addPreprocessor((e: any) => {
|
|
let bits = 0;
|
|
bits += (1 - e.s1) * 1;
|
|
delete e.s1;
|
|
bits += (1 - e.s2) * 2;
|
|
delete e.s2;
|
|
bits += (1 - e.s3) * 4;
|
|
delete e.s3;
|
|
bits += (1 - e.s4) * 8;
|
|
delete e.s4;
|
|
e.system_channel_flags = bits;
|
|
let temp = this.properties.features;
|
|
console.log([...temp]);
|
|
//@ts-ignore
|
|
temp = temp.filter((_) => !options.includes(_));
|
|
console.log(temp, options);
|
|
temp.push(e.features);
|
|
e.features = temp;
|
|
});
|
|
|
|
form.addHR();
|
|
form.addSelect(
|
|
I18n.getTranslation("guild.defaultNoti"),
|
|
"default_message_notifications",
|
|
[I18n.getTranslation("guild.onlyMentions"), I18n.getTranslation("guild.all")],
|
|
{
|
|
defaultIndex: [1, 0].indexOf(this.properties.default_message_notifications),
|
|
radio: true,
|
|
},
|
|
[1, 0],
|
|
);
|
|
form.addHR();
|
|
let region = this.properties.region;
|
|
if (!region) {
|
|
region = "";
|
|
}
|
|
form.addTextInput(I18n.getTranslation("guild.region:"), "region", {initText: region});
|
|
}
|
|
this.makeInviteMenu(
|
|
settings.addButton(I18n.getTranslation("invite.inviteMaker")),
|
|
textChannels,
|
|
);
|
|
const s1 = settings.addButton(I18n.getTranslation("guild.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), false));
|
|
{
|
|
const emoji = settings.addButton(I18n.getTranslation("emoji.title"));
|
|
emoji.addButtonInput("", I18n.getTranslation("emoji.upload"), () => {
|
|
const popup = new Dialog(I18n.getTranslation("emoji.upload"));
|
|
const form = popup.options.addForm(
|
|
"",
|
|
() => {
|
|
popup.hide();
|
|
},
|
|
{
|
|
fetchURL: `${this.info.api}/guilds/${this.id}/emojis`,
|
|
method: "POST",
|
|
headers: this.headers,
|
|
},
|
|
);
|
|
form.addFileInput(I18n.getTranslation("emoji.image:"), "image", {required: true});
|
|
form.addTextInput(I18n.getTranslation("emoji.name:"), "name", {required: true});
|
|
popup.show();
|
|
});
|
|
const containdiv = document.createElement("div");
|
|
const genDiv = () => {
|
|
containdiv.innerHTML = "";
|
|
for (const emoji of this.emojis) {
|
|
const div = document.createElement("div");
|
|
div.classList.add("flexltr", "emojiOption");
|
|
const emojic = new Emoji(emoji, this);
|
|
|
|
const text = document.createElement("input");
|
|
text.type = "text";
|
|
text.value = emoji.name;
|
|
text.addEventListener("change", () => {
|
|
fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`, {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify({name: text.value}),
|
|
}).then((e) => {
|
|
if (!e.ok) text.value = emoji.name;
|
|
}); //if not ok, undo
|
|
});
|
|
|
|
const del = document.createElement("span");
|
|
del.classList.add("svgicon", "svg-x", "deleteEmoji");
|
|
del.onclick = () => {
|
|
const diaolog = new Dialog("");
|
|
diaolog.options.addTitle(I18n.getTranslation("emoji.confirmDel"));
|
|
const options = diaolog.options.addOptions("", {ltr: true});
|
|
options.addButtonInput("", I18n.getTranslation("yes"), () => {
|
|
fetch(`${this.info.api}/guilds/${this.id}/emojis/${emoji.id}`, {
|
|
method: "DELETE",
|
|
headers: this.headers,
|
|
});
|
|
diaolog.hide();
|
|
});
|
|
options.addButtonInput("", I18n.getTranslation("no"), () => {
|
|
diaolog.hide();
|
|
});
|
|
diaolog.show();
|
|
};
|
|
|
|
div.append(emojic.getHTML(true), ":", text, ":", del);
|
|
|
|
containdiv.append(div);
|
|
}
|
|
};
|
|
this.onEmojiUpdate = () => {
|
|
if (!document.body.contains(containdiv)) {
|
|
this.onEmojiUpdate = () => {};
|
|
return;
|
|
}
|
|
genDiv();
|
|
};
|
|
genDiv();
|
|
emoji.addHTMLArea(containdiv);
|
|
}
|
|
(async () => {
|
|
const widgetMenu = settings.addButton(I18n.widget());
|
|
const cur = (await (
|
|
await fetch(this.info.api + "/guilds/" + this.id + "/widget", {
|
|
headers: this.headers,
|
|
})
|
|
).json()) as {
|
|
enabled: boolean;
|
|
channel_id?: null | string;
|
|
};
|
|
const form = widgetMenu.addForm("", () => {}, {
|
|
traditionalSubmit: true,
|
|
fetchURL: this.info.api + "/guilds/" + this.id + "/widget",
|
|
headers: this.headers,
|
|
method: "PATCH",
|
|
});
|
|
form.addCheckboxInput(I18n.widgetEnabled(), "enabled", {initState: cur.enabled});
|
|
const channels = this.channels.filter((_) => _.type !== 4);
|
|
form.addSelect(
|
|
I18n.channel.name(),
|
|
"channel_id",
|
|
channels.map((_) => _.name),
|
|
{
|
|
defaultIndex: channels.findIndex((_) => _.id == cur.channel_id),
|
|
},
|
|
channels.map((_) => _.id),
|
|
);
|
|
})();
|
|
const webhooks = settings.addButton(I18n.webhooks.base());
|
|
webhookMenu(this, this.info.api + `/guilds/${this.id}/webhooks`, webhooks);
|
|
settings.show();
|
|
}
|
|
makeInviteMenu(options: Options, valid: void | Channel[]) {
|
|
if (!valid) {
|
|
valid = this.channels.filter((e) => {
|
|
//TODO there are almost certainly more types. is Voice valid?
|
|
return new Set([0, 5]).has(e.type);
|
|
});
|
|
}
|
|
let channel = valid[0];
|
|
const div = document.createElement("div");
|
|
div.classList.add("invitediv");
|
|
const text = document.createElement("span");
|
|
text.classList.add("ellipsis");
|
|
div.append(text);
|
|
let uses = 0;
|
|
let expires = 1800;
|
|
const copycontainer = document.createElement("div");
|
|
copycontainer.classList.add("copycontainer");
|
|
const copy = document.createElement("span");
|
|
copy.classList.add("copybutton", "svgicon", "svg-copy");
|
|
copycontainer.append(copy);
|
|
copycontainer.onclick = (_) => {
|
|
if (text.textContent) {
|
|
navigator.clipboard.writeText(text.textContent);
|
|
}
|
|
};
|
|
div.append(copycontainer);
|
|
const update = () => {
|
|
fetch(`${this.info.api}/channels/${channel.id}/invites`, {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
flags: 0,
|
|
target_type: null,
|
|
target_user_id: null,
|
|
max_age: expires + "",
|
|
max_uses: uses,
|
|
temporary: uses !== 0,
|
|
}),
|
|
})
|
|
.then((_) => _.json())
|
|
.then((json) => {
|
|
const params = new URLSearchParams("");
|
|
params.set("instance", this.info.wellknown);
|
|
const encoded = params.toString();
|
|
text.textContent = `${location.origin}/invite/${json.code}?${encoded}`;
|
|
});
|
|
};
|
|
|
|
options.addTitle(I18n.getTranslation("inviteOptions.title"));
|
|
const text2 = options.addText("");
|
|
options
|
|
.addSelect(
|
|
I18n.getTranslation("invite.channel:"),
|
|
() => {},
|
|
valid.map((e) => e.name),
|
|
)
|
|
.watchForChange((e) => {
|
|
channel = valid[e];
|
|
text2.setText(I18n.getTranslation("invite.subtext", channel.name, this.properties.name));
|
|
});
|
|
|
|
options.addSelect(
|
|
I18n.getTranslation("invite.expireAfter"),
|
|
() => {},
|
|
["30m", "1h", "6h", "12h", "1d", "7d", "30d", "never"].map((e) =>
|
|
I18n.getTranslation("inviteOptions." + e),
|
|
),
|
|
).onchange = (e) => {
|
|
expires = [1800, 3600, 21600, 43200, 86400, 604800, 2592000, 0][e];
|
|
};
|
|
|
|
const timeOptions = ["1", "5", "10", "25", "50", "100"].map((e) =>
|
|
I18n.getTranslation("inviteOptions.limit", e),
|
|
);
|
|
timeOptions.unshift(I18n.getTranslation("inviteOptions.noLimit"));
|
|
options.addSelect(I18n.getTranslation("invite.expireAfter"), () => {}, timeOptions).onchange = (
|
|
e,
|
|
) => {
|
|
uses = [0, 1, 5, 10, 25, 50, 100][e];
|
|
};
|
|
|
|
options.addButtonInput("", I18n.getTranslation("invite.createInvite"), () => {
|
|
update();
|
|
});
|
|
|
|
options.addHTMLArea(div);
|
|
}
|
|
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);
|
|
}
|
|
onEmojiUpdate = (_: emojipjson[]) => {};
|
|
update(json: extendedProperties) {
|
|
this.large = json.large;
|
|
this.member_count = json.member_count;
|
|
this.emojis = json.emojis;
|
|
this.headers = this.owner.headers;
|
|
this.properties.features = json.features;
|
|
if (this.properties.icon !== json.icon) {
|
|
this.properties.icon = json.icon;
|
|
if (this.HTMLicon) {
|
|
const divy = this.generateGuildIcon();
|
|
this.HTMLicon.replaceWith(divy);
|
|
this.HTMLicon = divy;
|
|
}
|
|
}
|
|
this.roleids = new Map();
|
|
this.banner = json.banner;
|
|
}
|
|
constructor(json: guildjson | -1, owner: Localuser, member: memberjson | User | null) {
|
|
if (json === -1 || member === null) {
|
|
super("@me");
|
|
return;
|
|
}
|
|
if (json.stickers.length) {
|
|
console.log(json.stickers, ":3");
|
|
}
|
|
super(json.id);
|
|
this.owner = owner;
|
|
this.large = json.large;
|
|
this.member_count = json.member_count;
|
|
this.emojis = json.emojis;
|
|
this.headers = this.owner.headers;
|
|
this.channels = [];
|
|
if (json.properties) {
|
|
this.properties = json.properties;
|
|
}
|
|
this.roles = [];
|
|
this.roleids = new Map();
|
|
this.banner = json.properties.banner;
|
|
if (json.roles) {
|
|
for (const roley of json.roles) {
|
|
const roleh = new Role(roley, this);
|
|
this.roles.push(roleh);
|
|
this.roleids.set(roleh.id, roleh);
|
|
}
|
|
}
|
|
|
|
this.message_notifications = 0;
|
|
|
|
this.sortRoles();
|
|
if (member instanceof User) {
|
|
console.warn(member);
|
|
Member.resolveMember(member, this).then((_) => {
|
|
if (_) {
|
|
this.member = _;
|
|
} else {
|
|
console.error("Member was unable to resolve");
|
|
}
|
|
});
|
|
} else {
|
|
Member.new(member, this).then((_) => {
|
|
if (_) {
|
|
this.member = _;
|
|
}
|
|
});
|
|
}
|
|
|
|
this.perminfo ??= {channels: {}};
|
|
for (const thing of json.channels) {
|
|
const temp = new Channel(thing, this);
|
|
this.channels.push(temp);
|
|
this.localuser.channelids.set(temp.id, temp);
|
|
}
|
|
this.headchannels = [];
|
|
for (const thing of this.channels) {
|
|
const parent = thing.resolveparent(this);
|
|
if (!parent) {
|
|
this.headchannels.push(thing);
|
|
}
|
|
}
|
|
this.prevchannel = this.localuser.channelids.get(this.perminfo.prevchannel);
|
|
}
|
|
get perminfo() {
|
|
return this.localuser.perminfo.guilds[this.id];
|
|
}
|
|
set perminfo(e) {
|
|
this.localuser.perminfo.guilds[this.id] = e;
|
|
}
|
|
notisetting(settings: {
|
|
channel_overrides: {
|
|
message_notifications: number;
|
|
muted: boolean;
|
|
mute_config: {selected_time_window: number; end_time: number};
|
|
channel_id: string;
|
|
}[];
|
|
message_notifications: any;
|
|
flags?: number;
|
|
hide_muted_channels?: boolean;
|
|
mobile_push?: boolean;
|
|
mute_config?: null;
|
|
mute_scheduled_events?: boolean;
|
|
muted?: boolean;
|
|
notify_highlights?: number;
|
|
suppress_everyone?: boolean;
|
|
suppress_roles?: boolean;
|
|
version?: number;
|
|
guild_id?: string;
|
|
}) {
|
|
this.message_notifications = settings.message_notifications;
|
|
for (const override of settings.channel_overrides) {
|
|
const channel = this.localuser.channelids.get(override.channel_id);
|
|
if (!channel) continue;
|
|
channel.handleUserOverrides(override);
|
|
}
|
|
}
|
|
setnotifcation() {
|
|
const options = ["all", "onlyMentions", "none"].map((e) => I18n.getTranslation("guild." + e));
|
|
const notiselect = new Dialog("");
|
|
const form = notiselect.options.addForm(
|
|
"",
|
|
(_, sent: any) => {
|
|
notiselect.hide();
|
|
this.message_notifications = sent.message_notifications;
|
|
},
|
|
{
|
|
fetchURL: `${this.info.api}/users/@me/guilds/${this.id}/settings/`,
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
},
|
|
);
|
|
form.addSelect(
|
|
I18n.getTranslation("guild.selectnoti"),
|
|
"message_notifications",
|
|
options,
|
|
{
|
|
radio: true,
|
|
defaultIndex: this.message_notifications,
|
|
},
|
|
[0, 1, 2],
|
|
);
|
|
notiselect.show();
|
|
}
|
|
confirmleave() {
|
|
const full = new Dialog("");
|
|
full.options.addTitle(I18n.getTranslation("guild.confirmLeave"));
|
|
const options = full.options.addOptions("", {ltr: true});
|
|
options.addButtonInput("", I18n.getTranslation("guild.yesLeave"), () => {
|
|
this.leave().then((_) => {
|
|
full.hide();
|
|
});
|
|
});
|
|
options.addButtonInput("", I18n.getTranslation("guild.noLeave"), () => {
|
|
full.hide();
|
|
});
|
|
full.show();
|
|
}
|
|
async leave() {
|
|
return fetch(this.info.api + "/users/@me/guilds/" + this.id, {
|
|
method: "DELETE",
|
|
headers: this.headers,
|
|
});
|
|
}
|
|
printServers() {
|
|
let build = "";
|
|
for (const thing of this.headchannels) {
|
|
build += thing.name + ":" + thing.position + "\n";
|
|
for (const thingy of thing.children) {
|
|
build += " " + thingy.name + ":" + thingy.position + "\n";
|
|
}
|
|
}
|
|
console.log(build);
|
|
}
|
|
calculateReorder() {
|
|
let position = -1;
|
|
const build: {
|
|
id: string;
|
|
position: number | undefined;
|
|
parent_id: string | undefined;
|
|
}[] = [];
|
|
for (const thing of this.headchannels) {
|
|
const thisthing: {
|
|
id: string;
|
|
position: number | undefined;
|
|
parent_id: string | undefined;
|
|
} = {id: thing.id, position: undefined, parent_id: undefined};
|
|
if (thing.position <= position) {
|
|
thing.position = thisthing.position = position + 1;
|
|
}
|
|
position = thing.position;
|
|
console.log(position);
|
|
if (thing.move_id && thing.move_id !== thing.parent_id) {
|
|
thing.parent_id = thing.move_id;
|
|
thisthing.parent_id = thing.parent?.id;
|
|
thing.move_id = undefined;
|
|
}
|
|
if (thisthing.position || thisthing.parent_id) {
|
|
build.push(thisthing);
|
|
}
|
|
if (thing.children.length > 0) {
|
|
const things = thing.calculateReorder();
|
|
for (const thing of things) {
|
|
build.push(thing);
|
|
}
|
|
}
|
|
}
|
|
console.log(build);
|
|
this.printServers();
|
|
if (build.length === 0) {
|
|
return;
|
|
}
|
|
const serverbug = false;
|
|
if (serverbug) {
|
|
for (const thing of build) {
|
|
console.log(build, thing);
|
|
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify([thing]),
|
|
});
|
|
}
|
|
} else {
|
|
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
|
method: "PATCH",
|
|
headers: this.headers,
|
|
body: JSON.stringify(build),
|
|
});
|
|
}
|
|
}
|
|
get localuser() {
|
|
return this.owner;
|
|
}
|
|
get info() {
|
|
return this.owner.info;
|
|
}
|
|
sortchannels() {
|
|
this.headchannels.sort((a, b) => {
|
|
return a.position - b.position;
|
|
});
|
|
}
|
|
HTMLicon?: HTMLElement;
|
|
static generateGuildIcon(guild: Guild | (invitejson["guild"] & {info: {cdn: string}})) {
|
|
const divy = document.createElement("div");
|
|
divy.classList.add("servernoti");
|
|
|
|
const noti = document.createElement("div");
|
|
noti.classList.add("unread");
|
|
divy.append(noti);
|
|
if (guild instanceof Guild) {
|
|
guild.localuser.guildhtml.set(guild.id, divy);
|
|
guild.html = divy;
|
|
}
|
|
let icon: string | null;
|
|
if (guild instanceof Guild) {
|
|
icon = guild.properties.icon;
|
|
} else {
|
|
icon = guild.icon;
|
|
}
|
|
if (icon !== null) {
|
|
const img = createImg(guild.info.cdn + "/icons/" + guild.id + "/" + icon + ".png");
|
|
img.classList.add("pfp", "servericon");
|
|
divy.appendChild(img);
|
|
if (guild instanceof Guild) {
|
|
img.onclick = () => {
|
|
console.log(guild.loadGuild);
|
|
guild.loadGuild();
|
|
guild.loadChannel();
|
|
};
|
|
Guild.contextmenu.bindContextmenu(img, guild, undefined);
|
|
}
|
|
} else {
|
|
const div = document.createElement("div");
|
|
let name: string;
|
|
if (guild instanceof Guild) {
|
|
name = guild.properties.name;
|
|
} else {
|
|
name = guild.name;
|
|
}
|
|
const build = name
|
|
.replace(/'s /g, " ")
|
|
.replace(/\w+/g, (word) => word[0])
|
|
.replace(/\s/g, "");
|
|
div.textContent = build;
|
|
div.classList.add("blankserver", "servericon");
|
|
divy.appendChild(div);
|
|
if (guild instanceof Guild) {
|
|
div.onclick = () => {
|
|
guild.loadGuild();
|
|
guild.loadChannel();
|
|
};
|
|
Guild.contextmenu.bindContextmenu(div, guild, undefined);
|
|
}
|
|
}
|
|
return divy;
|
|
}
|
|
generateGuildIcon() {
|
|
return Guild.generateGuildIcon(this);
|
|
}
|
|
confirmDelete() {
|
|
let confirmname = "";
|
|
|
|
const full = new Dialog("");
|
|
full.options.addTitle(I18n.getTranslation("guild.confirmDelete", this.properties.name));
|
|
full.options.addTextInput(I18n.getTranslation("guild.serverName"), () => {}).onchange = (e) =>
|
|
(confirmname = e);
|
|
|
|
const options = full.options.addOptions("", {ltr: true});
|
|
options.addButtonInput("", I18n.getTranslation("guild.yesDelete"), () => {
|
|
if (confirmname !== this.properties.name) {
|
|
//TODO maybe some sort of form error? idk
|
|
alert("names don't match");
|
|
return;
|
|
}
|
|
this.delete().then((_) => {
|
|
full.hide();
|
|
});
|
|
});
|
|
|
|
options.addButtonInput("", I18n.getTranslation("guild.noDelete"), () => {
|
|
full.hide();
|
|
});
|
|
full.show();
|
|
}
|
|
async delete() {
|
|
return fetch(this.info.api + "/guilds/" + this.id + "/delete", {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
});
|
|
}
|
|
get mentions() {
|
|
let mentions = 0;
|
|
for (const thing of this.channels) {
|
|
mentions += thing.mentions;
|
|
}
|
|
return mentions;
|
|
}
|
|
unreads(html?: HTMLElement | undefined) {
|
|
if (html) {
|
|
this.html = html;
|
|
} else {
|
|
html = this.html;
|
|
}
|
|
if (!html) {
|
|
return;
|
|
}
|
|
let read = true;
|
|
let mentions = this.mentions;
|
|
for (const thing of this.channels) {
|
|
if (thing.hasunreads) {
|
|
read = false;
|
|
break;
|
|
}
|
|
}
|
|
const noti = html.children[0];
|
|
if (mentions !== 0) {
|
|
noti.classList.add("pinged");
|
|
noti.textContent = "" + mentions;
|
|
} else {
|
|
noti.textContent = "";
|
|
noti.classList.remove("pinged");
|
|
}
|
|
if (read) {
|
|
noti.classList.remove("notiunread");
|
|
} else {
|
|
noti.classList.add("notiunread");
|
|
}
|
|
}
|
|
getHTML() {
|
|
const sideContainDiv = document.getElementById("sideContainDiv");
|
|
if (sideContainDiv) sideContainDiv.classList.remove("searchDiv");
|
|
const searchBox = document.getElementById("searchBox");
|
|
if (searchBox) searchBox.textContent = "";
|
|
|
|
//this.printServers();
|
|
this.sortchannels();
|
|
this.printServers();
|
|
const build = document.createElement("div");
|
|
|
|
for (const thing of this.headchannels) {
|
|
build.appendChild(thing.createguildHTML(this.isAdmin()));
|
|
}
|
|
return build;
|
|
}
|
|
isAdmin() {
|
|
return this.member.isAdmin();
|
|
}
|
|
async markAsRead() {
|
|
const build: {
|
|
read_states: {
|
|
channel_id: string;
|
|
message_id: string | null | undefined;
|
|
read_state_type: number;
|
|
}[];
|
|
} = {read_states: []};
|
|
for (const thing of this.channels) {
|
|
if (thing.hasunreads) {
|
|
build.read_states.push({
|
|
channel_id: thing.id,
|
|
message_id: thing.lastmessageid,
|
|
read_state_type: 0,
|
|
});
|
|
thing.lastreadmessageid = thing.lastmessageid;
|
|
if (!thing.myhtml) continue;
|
|
thing.myhtml.classList.remove("cunread");
|
|
}
|
|
}
|
|
this.unreads();
|
|
fetch(this.info.api + "/read-states/ack-bulk", {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify(build),
|
|
});
|
|
}
|
|
hasRole(r: Role | string) {
|
|
console.log("this should run");
|
|
if (r instanceof Role) {
|
|
r = r.id;
|
|
}
|
|
return this.member.hasRole(r);
|
|
}
|
|
loadChannel(ID?: string | undefined | null, addstate = true) {
|
|
if (ID) {
|
|
const channel = this.localuser.channelids.get(ID);
|
|
if (channel) {
|
|
channel.getHTML(addstate);
|
|
return;
|
|
}
|
|
}
|
|
if (this.prevchannel && ID !== null) {
|
|
console.log(this.prevchannel);
|
|
this.prevchannel.getHTML(addstate);
|
|
return;
|
|
}
|
|
if (this.id !== "@me") {
|
|
for (const thing of this.channels) {
|
|
if (thing.type !== 4) {
|
|
thing.getHTML(addstate);
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
this.removePrevChannel();
|
|
this.noChannel(addstate);
|
|
}
|
|
removePrevChannel() {
|
|
if (this.localuser.channelfocus) {
|
|
this.localuser.channelfocus.infinite.delete();
|
|
}
|
|
if (this !== this.localuser.lookingguild) {
|
|
this.loadGuild();
|
|
}
|
|
if (this.localuser.channelfocus && this.localuser.channelfocus.myhtml) {
|
|
this.localuser.channelfocus.myhtml.classList.remove("viewChannel");
|
|
}
|
|
this.prevchannel = undefined;
|
|
this.localuser.channelfocus = undefined;
|
|
const replybox = document.getElementById("replybox") as HTMLElement;
|
|
const typebox = document.getElementById("typebox") as HTMLElement;
|
|
replybox.classList.add("hideReplyBox");
|
|
typebox.classList.remove("typeboxreplying");
|
|
(document.getElementById("typebox") as HTMLDivElement).contentEditable = "false";
|
|
(document.getElementById("upload") as HTMLElement).style.visibility = "hidden";
|
|
(document.getElementById("typediv") as HTMLElement).style.visibility = "hidden";
|
|
(document.getElementById("sideDiv") as HTMLElement).innerHTML = "";
|
|
}
|
|
noChannel(addstate: boolean) {
|
|
if (addstate) {
|
|
history.pushState([this.id, undefined], "", "/channels/" + this.id);
|
|
}
|
|
this.localuser.pageTitle(I18n.getTranslation("guild.emptytitle"));
|
|
const channelTopic = document.getElementById("channelTopic") as HTMLSpanElement;
|
|
channelTopic.setAttribute("hidden", "");
|
|
|
|
const loading = document.getElementById("loadingdiv") as HTMLDivElement;
|
|
loading.classList.remove("loading");
|
|
this.localuser.getSidePannel();
|
|
|
|
const messages = document.getElementById("channelw") as HTMLDivElement;
|
|
for (const thing of Array.from(messages.getElementsByClassName("messagecontainer"))) {
|
|
thing.remove();
|
|
}
|
|
const h1 = document.createElement("h1");
|
|
h1.classList.add("messagecontainer");
|
|
h1.textContent = I18n.getTranslation("guild.emptytext");
|
|
messages.append(h1);
|
|
}
|
|
loadGuild() {
|
|
this.localuser.loadGuild(this.id);
|
|
}
|
|
updateChannel(json: channeljson) {
|
|
const channel = this.localuser.channelids.get(json.id);
|
|
if (channel) {
|
|
channel.updateChannel(json);
|
|
this.headchannels = [];
|
|
for (const thing of this.channels) {
|
|
thing.children = [];
|
|
}
|
|
this.headchannels = [];
|
|
for (const thing of this.channels) {
|
|
const parent = thing.resolveparent(this);
|
|
if (!parent) {
|
|
this.headchannels.push(thing);
|
|
}
|
|
}
|
|
this.printServers();
|
|
}
|
|
}
|
|
createChannelpac(json: channeljson) {
|
|
const thischannel = new Channel(json, this);
|
|
this.localuser.channelids.set(json.id, thischannel);
|
|
this.channels.push(thischannel);
|
|
thischannel.resolveparent(this);
|
|
if (!thischannel.parent) {
|
|
this.headchannels.push(thischannel);
|
|
}
|
|
this.calculateReorder();
|
|
this.printServers();
|
|
return thischannel;
|
|
}
|
|
goToChannelDelay(id: string) {
|
|
const channel = this.channels.find((_) => _.id == id);
|
|
if (channel) {
|
|
this.loadChannel(channel.id);
|
|
} else {
|
|
this.localuser.gotoid = id;
|
|
}
|
|
}
|
|
createchannels(func = this.createChannel.bind(this)) {
|
|
const options = ["text", "announcement", "voice"].map((e) =>
|
|
I18n.getTranslation("channel." + e),
|
|
);
|
|
|
|
const channelselect = new Dialog("");
|
|
const form = channelselect.options.addForm("", (e: any) => {
|
|
func(e.name, e.type);
|
|
channelselect.hide();
|
|
});
|
|
|
|
form.addSelect(
|
|
I18n.getTranslation("channel.selectType"),
|
|
"type",
|
|
options,
|
|
{radio: true},
|
|
[0, 5, 2],
|
|
);
|
|
form.addTextInput(I18n.getTranslation("channel.selectName"), "name");
|
|
channelselect.show();
|
|
}
|
|
createcategory() {
|
|
const category = 4;
|
|
const channelselect = new Dialog("");
|
|
const options = channelselect.options;
|
|
const form = options.addForm("", (e: any) => {
|
|
this.createChannel(e.name, category);
|
|
channelselect.hide();
|
|
});
|
|
form.addTextInput(I18n.getTranslation("channel.selectCatName"), "name");
|
|
channelselect.show();
|
|
}
|
|
delChannel(json: channeljson) {
|
|
const channel = this.localuser.channelids.get(json.id);
|
|
this.localuser.channelids.delete(json.id);
|
|
if (!channel) return;
|
|
this.channels.splice(this.channels.indexOf(channel), 1);
|
|
const indexy = this.headchannels.indexOf(channel);
|
|
if (indexy !== -1) {
|
|
this.headchannels.splice(indexy, 1);
|
|
}
|
|
if (channel === this.prevchannel) {
|
|
this.prevchannel = undefined;
|
|
}
|
|
/*
|
|
const build=[];
|
|
for(const thing of this.channels){
|
|
console.log(thing.id);
|
|
if(thing!==channel){
|
|
build.push(thing)
|
|
}else{
|
|
console.log("fail");
|
|
if(thing.parent){
|
|
thing.parent.delChannel(json);
|
|
}
|
|
}
|
|
}
|
|
this.channels=build;
|
|
*/
|
|
this.printServers();
|
|
}
|
|
createChannel(name: string, type: number) {
|
|
fetch(this.info.api + "/guilds/" + this.id + "/channels", {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({name, type}),
|
|
})
|
|
.then((_) => _.json())
|
|
.then((_) => this.goToChannelDelay(_.id));
|
|
}
|
|
async createRole(name: string) {
|
|
const fetched = await fetch(this.info.api + "/guilds/" + this.id + "roles", {
|
|
method: "POST",
|
|
headers: this.headers,
|
|
body: JSON.stringify({
|
|
name,
|
|
color: 0,
|
|
permissions: "0",
|
|
}),
|
|
});
|
|
const json = await fetched.json();
|
|
const role = new Role(json, this);
|
|
this.roleids.set(role.id, role);
|
|
this.roles.push(role);
|
|
return role;
|
|
}
|
|
async updateRolePermissions(id: string, perms: Permissions) {
|
|
const role = this.roleids.get(id);
|
|
if (!role) {
|
|
return;
|
|
}
|
|
role.permissions.allow = perms.allow;
|
|
role.permissions.deny = perms.deny;
|
|
|
|
await fetch(this.info.api + "/guilds/" + this.id + "/roles/" + role.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();
|
|
export {Guild};
|