Corban-Lee e8ba77d56a
Some checks failed
Build and Push Docker Image / build (push) Failing after 6m55s
close server view button
2024-10-29 16:53:07 +00:00

423 lines
11 KiB
JavaScript

// region Loaded Servers
var _loadedServers = []
var selectedServer = null;
function getLoadedServer(options) {
let servers = _loadedServers.filter(item => {
for (let key in options) {
if (item[key] !== options[key]) {
return false
}
}
return true;
});
return servers || [];
}
function getServerFromSnowflake(id) {
server = getLoadedServer({id: id});
if (!server.length) {
return null;
}
return server[0];
}
function addToLoadedServers(serverData, autoSelect=false) {
_loadedServers.push(serverData);
createSelectButton(serverData);
if (autoSelect) {
selectServer(serverData["id"]);
}
}
function removeFromLoadedServers(id) {
_loadedServers = _loadedServers.filter(item => item.id !== id);
removeSelectButton(id)
if (selectedServer.id === id) {
selectedServer(null);
}
}
// region Loaded Channels
var _loadedChannels = {};
async function loadedChannels(serverId) {
if (!(serverId in _loadedChannels)) {
await fetchChannels(serverId);
}
return _loadedChannels[serverId]
}
$(document).on("selectedServerChange", async function() {
// Try load channels to determine if bot has permissions
loadedChannels(selectedServer.id);
});
const fetchChannels = async serverId => {
$(".sidebar .sidebar-item").prop("disabled", true);
try {
channels = await ajaxRequest(`/generate-channels?guild=${serverId}`, "GET");
_loadedChannels[serverId] = channels;
unspotItem(serverId); // remove the spot because no errors occured
}
catch (error) {
logError(error);
unspotItem(serverId);
switch (error?.status) {
case 429:
rateLimitedLoadingChannels(error.responseJSON.retry_after);
break;
case 403:
notAuthorisedLoadingChannels(serverId);
break;
default:
alert("unknown error loading channels");
break;
}
}
finally {
$(".sidebar .sidebar-item").prop("disabled", false);
}
}
const rateLimitedLoadingChannels = retryAfterSeconds => {
createModal({
title: "Failed to Fetch Server Channels",
texts: [
{ content: "Discord is rate-limiting your request." },
{ content: `This happens when making requests too quickly. Retry after ${retryAfterSeconds} seconds to continue without issue.` }
],
buttons: [
{
className: "btn-warning px-4",
iconClass: "bi-arrow-return-right",
closeModal: true
}
]
});
}
const notAuthorisedLoadingChannels = serverId => {
// Mark the sidebar item as non-operational
spotItem(serverId, "danger");
const inviteBotToServer = () => {
window.open(
`https://discord.com/oauth2/authorize
?client_id=${discordClientId}
&permissions=2147534848
&scope=bot+applications.commands
&guild_id=${serverId}
&disable_guild_select=true`,
"_blank"
);
}
// Inform the user auth problem
createModal({
title: "Failed to Fetch Server Channels",
texts: [
{ content: "The Discord Bot is unable to access this server's channels, certain features will not work." },
{ content: "Ensure the Bot is a member, and has the neccessary permissions to operate." }
],
buttons: [
{
text: "Invite the Bot",
className: "btn-primary me-3",
iconClass: "bi-envelope-plus",
closeModal: true,
onClick: inviteBotToServer
},
{
className: "btn-secondary",
iconClass: "bi-arrow-return-right",
closeModal: true
}
]
});
}
// region UI Buttons
function createSelectButton(serverData) {
// server details
const id = serverData["id"];
const name = serverData["name"];
const iconHash = serverData["icon_hash"];
const isBotOperational = serverData["is_bot_operational"];
let template = $($("#serverItemTemplate").html());
const imageUrl = `https://cdn.discordapp.com/icons/${id}/${iconHash}.webp?size=80`;
const altText = name.split(' ').map(word => word.charAt(0)).join(''); // initials of server name, used if iconUrl is 404
template.find(".js-image").attr("src", imageUrl).attr("alt", altText);
template.find(".js-name").text(name);
template.find(".js-id").text(id);
template.find(".sidebar-item").attr("data-id", id);
// Show inoperational status, also, `isBotOperatioanl` can be null,
// so we can't rely on it's truthy value.
if (isBotOperational === false) {
template.find(".sidebar-item").addClass("spot spot-danger");
}
// Bind the button for selecting this server
template.find(".sidebar-item").off("click").on("click", function() {
const myID = $(this).data("id");
// only select if not already selected, otherwise hide sidebar on smaller screens (responsive)
selectedServer?.id !== myID ? selectServer(myID) : setSidebarVisibility(false);
});
$("#serverList").prepend(template);
}
function removeSelectButton(id) {
$(`#serverList .server-item[data-id=${id}]`).remove();
}
$("#backToSelectServer").on("click", function() {
$("#noSelectedServer").show();
$("#selectedServerContainer").hide();
$("#serverList .server-item > .server-item-selector.active").removeClass("active");
selectedServer = null;
});
// #region Server Selection
function selectServer(id) {
let server = getServerFromSnowflake(id);
// Change appearance of selected vs none-selected items
$("#serverList .sidebar-item").removeClass("active");
$(`#serverList .sidebar-item[data-id="${id}"]`).addClass("active");
// Global variable
selectedServer = server;
// Close sidebar on smaller screens
setSidebarVisibility(false);
// Show no server selected if that's the case
if (!server) {
$("#noSelectedServer").show();
$("#selectedServerContainer").hide();
return;
}
// Update UI
$("#noSelectedServer").hide();
$("#selectedServerContainer").show().css("display", "flex");
// Announce change to any listeners
$(document).trigger("selectedServerChange");
}
// #region Resolve Strings
function resolveServerStrings() {
// Server icon
$(".resolve-to-server-icon").attr(
"src",
`https://cdn.discordapp.com/icons/${selectedServer.id}/${selectedServer.icon_hash}.webp?size=80`
).attr("alt", selectedServer.name.split(' ').map(word => word.charAt(0)).join(''));
// Server names
$(".resolve-to-server-name").text(selectedServer.name);
// Server Guild Ids
$(".resolve-to-server-id").text(selectedServer.id);
// Bot Invite links
$(".resolve-to-invite-link").attr("href", `https://discord.com/oauth2/authorize
?client_id=${discordClientId}
&permissions=2147534848
&scope=bot+applications.commands
&guild_id=${selectedServer.id}
&disable_guild_select=true`);
}
// region Change Listener
$(document).on("selectedServerChange", async function() {
resolveServerStrings();
$("#serverJoinAlert").hide();
});
// region Load Servers
async function loadServers(generate=true) {
// Remove any previously loaded servers
$(".sidebar .sidebar-item").closest("li").remove();
// Show placeholder items & hide rate limit warning
$(".sidebar .sidebar-loading").show();
$(".sidebar .server-rate-limit").hide();
try {
let data = await ajaxRequest(
generate ? "/generate-servers/" : "/api/servers/",
"GET"
);
data = generate ? data : data.results; // api responds differently
data.forEach(server => addToLoadedServers(server, false));
}
catch (error) {
switch (error?.status) {
case 401:
window.location.href = "/login"; // discord token has expired
break;
case 429:
$(".sidebar .server-rate-limit").show();
break;
default:
logError(error);
break;
}
}
finally {
$(".sidebar .sidebar-loading").hide();
}
}
// Retry load servers button
$(".sidebar .sidebar-retry-btn").on("click", loadServers);
// region Spot Icons
const unspotItem = id => {
$(`.sidebar .sidebar-item[data-id="${id}"]`).removeClass(
"spot spot-primary spot-secondary spot-success spot-info spot-warning spot-danger"
);
}
const spotItem = (id, spotStyle) => {
$(`.sidebar .sidebar-item[data-id="${id}"]`).addClass(`spot spot-${spotStyle}`);
}
// region View Other Users
$(".js-serverUsersBtn").on("click", () => {
createModal({
title: "Other Users",
texts: [
{content: "This feature has not yet been implemented."}
],
buttons: [
{
className: "btn-secondary px-4",
iconClass: "bi-arrow-return-right",
closeModal: true
}
]
});
});
// region View Edit History
$(".js-serverHistoryBtn").on("click", () => {
createModal({
title: "Edit History",
texts: [
{content: "This feature has not yet been implemented."}
],
buttons: [
{
className: "btn-secondary px-4",
iconClass: "bi-arrow-return-right",
closeModal: true
}
]
});
});
// region Delete Server Data
const eraseServerData = async server => {
const response = await ajaxRequest(`/api/servers/${server.id}/`, "DELETE");
// Only server owners can delete all data at once
if (response?.status === 403) {
createModal({
title: `Failed to Delete Data for ${server.name}`,
texts: [{
content: `Only the owner of <b>${server.name}</b> can erase all of it's data.`,
html: true
}],
buttons: [{
className: "btn-danger px-4",
iconClass: "bi-arrow-return-right",
closeModal: true
}]
});
return;
}
// Return to the 'select a server' screen
selectServer(null);
// Refresh the sidebar server list
await loadServers(false);
}
$(".js-closeServerBtn").on("click", () => {
selectServer(null);
});
$(".js-eraseServerBtn").on("click", () => {
const server = selectedServer; // Store incase it changes
const itemsToLose = arrayToHtmlList([
"Subscriptions",
"Filters",
"Message Styles",
"Tracked Content"
]).addClass("mb-3").prop("outerHTML");
createModal({
title: `Delete Data for ${server.name}?`,
texts: [
{content: "You will lose all data related to this server, including:"},
{content: itemsToLose, html: true},
{content: "Please reconsider this decision."}
],
buttons: [
{
className: "btn-danger me-3",
iconClass: "bi-trash3",
closeModal: true,
onClick: () => eraseServerData(server)
},
{
className: "btn-secondary px-4",
iconClass: "bi-arrow-return-right",
closeModal: true
}
]
})
});