remove unused static files
This commit is contained in:
parent
7f151366b1
commit
e6d0cea361
@ -1,254 +0,0 @@
|
|||||||
var contentTable;
|
|
||||||
contentOptions = null;
|
|
||||||
channelResolveInterval = null;
|
|
||||||
|
|
||||||
async function initContentTable() {
|
|
||||||
contentOptions = await getTrackedContentOptions();
|
|
||||||
await initTable("#contentTabPane", "contentTable", loadContent, null, deleteSelectedContent, contentOptions);
|
|
||||||
|
|
||||||
contentTable = $("#contentTable").DataTable({
|
|
||||||
info: false,
|
|
||||||
paging: false,
|
|
||||||
ordering: false,
|
|
||||||
searching: false,
|
|
||||||
autoWidth: false,
|
|
||||||
order: [],
|
|
||||||
select: {
|
|
||||||
style: "multi+shift",
|
|
||||||
selector: 'th:first-child input[type="checkbox"]'
|
|
||||||
},
|
|
||||||
columnDefs: [
|
|
||||||
{ orderable: false, targets: "no-sort" },
|
|
||||||
{
|
|
||||||
targets: 0,
|
|
||||||
checkboxes: { selectRow: true }
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
// Select row checkbox column
|
|
||||||
title: '<input type="checkbox" class="form-check-input table-select-all" />',
|
|
||||||
data: null,
|
|
||||||
orderable: false,
|
|
||||||
className: "text-center col-switch-width",
|
|
||||||
render: function() {
|
|
||||||
return '<input type="checkbox" class="form-check-input table-select-row" />'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ data: "id", visible: false },
|
|
||||||
{
|
|
||||||
title: "GUID",
|
|
||||||
data: "guid",
|
|
||||||
className: "text-truncate mw-10rem",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Name",
|
|
||||||
data: "title",
|
|
||||||
className: "text-truncate",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const title = sanitise(data);
|
|
||||||
const url = sanitise(row.url);
|
|
||||||
return `<a href="${url}" class="btn btn-link text-start text-decoration-none" target="_blank">${title}</a>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Subscription",
|
|
||||||
data: "subscription.name",
|
|
||||||
className: "text-nowrap",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const subName = sanitise(data);
|
|
||||||
return `<button type="button" onclick="goToSubscription(${row.subscription.id})" class="btn btn-link text-start text-decoration-none">${subName}</button>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Blocked",
|
|
||||||
data: "blocked",
|
|
||||||
className: "text-center col-1",
|
|
||||||
render: function(data) {
|
|
||||||
return data ? `<i class="bi bi-check-lg text-success"></i>` : ""
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Channel",
|
|
||||||
data: "channel_id",
|
|
||||||
className: "text-start",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const channelId = sanitise(data);
|
|
||||||
const messageId = sanitise(row.message_id);
|
|
||||||
return `<div class="resolve-channel-name text-center" data-channel-id="${channelId}" data-msg-id="${messageId}">
|
|
||||||
<div class="spinner-border spinner-border-sm" role="status">
|
|
||||||
<span class="visually-hidden">Loading...</span>
|
|
||||||
</div>
|
|
||||||
</div>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Created",
|
|
||||||
data: "creation_datetime",
|
|
||||||
className: "text-nowrap",
|
|
||||||
render: function(data, type) {
|
|
||||||
let dateTime = new Date(data);
|
|
||||||
return $(`
|
|
||||||
<span data-bs-trigger="hover focus"
|
|
||||||
data-bs-html="true"
|
|
||||||
data-bs-custom-class="text-center"
|
|
||||||
data-bs-toggle="popover"
|
|
||||||
data-bs-content="${formatStringDate(dateTime, "%a, %D %B, %Y<br>%H:%M:%S")}">
|
|
||||||
${formatStringDate(dateTime, "%D, %b %Y")}
|
|
||||||
</span>
|
|
||||||
`).popover()[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
orderable: false,
|
|
||||||
className: "p-0",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const embedColour = sanitise(row.subscription.embed_colour);
|
|
||||||
return `<div class="h-100" style="background-color: #${embedColour}; width: .25rem;"> </div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
bindTableCheckboxes("#contentTable", contentTable, "#contentTabPane .table-del-btn");
|
|
||||||
|
|
||||||
contentTable.on("draw", function() {
|
|
||||||
restartResolveChannelNamesTask();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #region Resolve Channels
|
|
||||||
|
|
||||||
function restartResolveChannelNamesTask() {
|
|
||||||
clearInterval(channelResolveInterval);
|
|
||||||
startResolveChannelNamesTask();
|
|
||||||
}
|
|
||||||
|
|
||||||
function startResolveChannelNamesTask() {
|
|
||||||
const guildId = selectedServer.id;
|
|
||||||
channelResolveInterval = setInterval(function() {
|
|
||||||
if (resolveChannelNames(guildId))
|
|
||||||
clearInterval(channelResolveInterval);
|
|
||||||
}, 50)
|
|
||||||
}
|
|
||||||
|
|
||||||
function resolveChannelNames(guildId) {
|
|
||||||
if (!discordChannels.length) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
$(".resolve-channel-name").each(function() {
|
|
||||||
const channelId = $(this).data("channel-id");
|
|
||||||
const messageId = $(this).data("msg-id");
|
|
||||||
console.log(channelId + " " + messageId);
|
|
||||||
const channel = discordChannels.find(channel => channel.value === channelId);
|
|
||||||
|
|
||||||
if (channel) {
|
|
||||||
const href = `https://discord.com/channels/${guildId}/${channelId}/${messageId}/`;
|
|
||||||
$(this).replaceWith(
|
|
||||||
$("<a>").text(channel.text)
|
|
||||||
.attr("href", href)
|
|
||||||
.attr("target", "_blank")
|
|
||||||
.addClass("btn btn-link text-start text-decoration-none text-nowrap")
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
async function goToSubscription(subId) {
|
|
||||||
$("#subscriptionsTab").click();
|
|
||||||
await showEditSubModal(subId);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// #region Delete Content
|
|
||||||
|
|
||||||
async function deleteSelectedContent() {
|
|
||||||
const rows = contentTable.rows(".selected").data().toArray();
|
|
||||||
const names = rows.map(row => { return row.title });
|
|
||||||
const namesString = arrayToHtmlList(names, true).prop("outerHTML");
|
|
||||||
const isMany = names.length > 1;
|
|
||||||
|
|
||||||
await confirmationModal(
|
|
||||||
`Delete ${isMany ? "Many Tracked Contents" : "a Tracked Content"}`,
|
|
||||||
`Do you wish to permanently delete ${isMany ? "these" : "this"} <b>${names.length}</b> Tracked Content${isMany ? "s" : ""}?<br><br>${namesString}`,
|
|
||||||
"danger",
|
|
||||||
async () => {
|
|
||||||
rows.forEach(async row => { await deleteTrackedContent(row.id) });
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
`Deleted ${names.length} Content${isMany ? "s" : ""}`,
|
|
||||||
`${arrayToHtmlList(names, false).prop("outerHTML")}`,
|
|
||||||
12000
|
|
||||||
);
|
|
||||||
|
|
||||||
// Multi-deletion can take time, this timeout ensures the refresh is accurate
|
|
||||||
setTimeout(async () => {
|
|
||||||
await loadContent(selectedServer.id);
|
|
||||||
}, 600);
|
|
||||||
},
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
|
|
||||||
function clearExistingContentRows() {
|
|
||||||
$("#contentTable thead .table-select-all").prop("checked", false).prop("indeterminate", false);
|
|
||||||
contentTable.clear().draw(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#contentTabPane").on("click", ".table-refresh-btn", async function() {
|
|
||||||
await loadContent(selectedServer.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
// #region Load Content
|
|
||||||
|
|
||||||
async function loadContent(guildId) {
|
|
||||||
|
|
||||||
if (!guildId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
setTableFilter("contentTable", "subscription__guild_id", guildId);
|
|
||||||
ensureTablePagination("contentTable");
|
|
||||||
|
|
||||||
$("#contentTabPane .table-del-btn").prop("disabled", true);
|
|
||||||
clearExistingContentRows();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var content = await getTrackedContent(tableFilters["contentTable"], tableSorts["contentTable"]);
|
|
||||||
contentTable.rows.add(content.results).draw(false);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
showToast("danger", `Error loading Tracked Content: HTTP ${err.status}`, err, 15000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTableContainer(
|
|
||||||
"contentTabPane",
|
|
||||||
tableFilters["contentTable"]["page"],
|
|
||||||
tableFilters["contentTable"]["page_size"],
|
|
||||||
content.results.length,
|
|
||||||
content.count,
|
|
||||||
content.next,
|
|
||||||
content.previous
|
|
||||||
);
|
|
||||||
|
|
||||||
$("#contentTable thead .table-select-all").prop("disabled", content.results.length === 0);
|
|
||||||
console.debug(`loaded filters, ${content.results.length} found`)
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).on("selectedServerChange", async function() {
|
|
||||||
await loadContent(selectedServer.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
// #endregion
|
|
@ -1,310 +0,0 @@
|
|||||||
var filtersTable;
|
|
||||||
filterOptions = null;
|
|
||||||
|
|
||||||
// Create filters table
|
|
||||||
async function initFiltersTable() {
|
|
||||||
filterOptions = await getFilterOptions();
|
|
||||||
await initTable("#filtersTabPane", "filtersTable", loadFilters, showEditFilterModal, deleteSelectedFilters, filterOptions);
|
|
||||||
|
|
||||||
filtersTable = $("#filtersTable").DataTable({
|
|
||||||
info: false,
|
|
||||||
paging: false,
|
|
||||||
ordering: false,
|
|
||||||
searching: false,
|
|
||||||
autoWidth: false,
|
|
||||||
order: [],
|
|
||||||
select: {
|
|
||||||
style: "multi+shift",
|
|
||||||
selector: 'th:first-child input[type="checkbox"]'
|
|
||||||
},
|
|
||||||
columnDefs: [
|
|
||||||
{ orderable: false, targets: "no-sort" },
|
|
||||||
{
|
|
||||||
targets: 0,
|
|
||||||
checkboxes: { selectRow: true }
|
|
||||||
}
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
// Select row checkbox column
|
|
||||||
title: '<input type="checkbox" class="form-check-input table-select-all" />',
|
|
||||||
data: null,
|
|
||||||
orderable: false,
|
|
||||||
className: "text-center col-switch-width",
|
|
||||||
render: function() {
|
|
||||||
return '<input type="checkbox" class="form-check-input table-select-row" />'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ title: "ID", data: "id", visible: false },
|
|
||||||
{
|
|
||||||
title: "Name",
|
|
||||||
data: "name",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const name = sanitise(data);
|
|
||||||
return `<button type="button" onclick="showEditFilterModal(${row.id})" class="btn btn-link text-start text-decoration-none">${name}</button>`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Matching Algorithm",
|
|
||||||
data: "matching_algorithm",
|
|
||||||
render: function(data) {
|
|
||||||
switch (data) {
|
|
||||||
case 1: return "Any Word";
|
|
||||||
case 2: return "All Words";
|
|
||||||
case 3: return "Exact Match";
|
|
||||||
case 4: return "Regular Expression";
|
|
||||||
case 5: return "Fuzzy Match";
|
|
||||||
default:
|
|
||||||
console.error(`unknown matching algorithm '${data}'`);
|
|
||||||
return sanitise(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Match",
|
|
||||||
data: "match"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Case Sensitivity",
|
|
||||||
data: "is_insensitive",
|
|
||||||
render: function(data) {
|
|
||||||
return data ? "Insensitive" : "Sensitive"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Control List",
|
|
||||||
data: "is_whitelist",
|
|
||||||
render: function(data) {
|
|
||||||
return data ? "Whitelist" : "Blacklist";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
bindTableCheckboxes("#filtersTable", filtersTable, "#filtersTabPane .table-del-btn");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#addFilterBtn").on("click", async function() {
|
|
||||||
await showEditFilterModal(-1);
|
|
||||||
})
|
|
||||||
|
|
||||||
async function showEditFilterModal(filterId) {
|
|
||||||
|
|
||||||
if (filterId === -1) {
|
|
||||||
$("#filterFormModal input, #filterFormModal textarea").val("");
|
|
||||||
$("#filterFormModal input:checkbox").prop("checked", false);
|
|
||||||
|
|
||||||
$("#filterAlgorithm").val("").change();
|
|
||||||
|
|
||||||
$("#filterFormModal .form-create").show();
|
|
||||||
$("#filterFormModal .form-edit").hide();
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
const filter = filtersTable.row(function(idx, data, node) {
|
|
||||||
return data.id === filterId;
|
|
||||||
}).data();
|
|
||||||
|
|
||||||
$("#filterAlgorithm").val("").change();
|
|
||||||
$("#filterAlgorithm").val(filter.matching_algorithm).change();
|
|
||||||
|
|
||||||
$("#filterName").val(filter.name);
|
|
||||||
$("#filterMatch").val(filter.match);
|
|
||||||
$("#filterWhitelist").prop("checked", filter.is_whitelist);
|
|
||||||
$("#filterInsensitive").prop("checked", filter.is_insensitive);
|
|
||||||
|
|
||||||
$("#filterFormModal .form-create").hide();
|
|
||||||
$("#filterFormModal .form-edit").show();
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#filterId").val(filterId);
|
|
||||||
$("#filterFormModal").modal("show");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#filterForm").on("submit", async function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
var id = $("#filterId").val();
|
|
||||||
name = $("#filterName").val();
|
|
||||||
algorithm = $("#filterAlgorithm option:selected").val();
|
|
||||||
match = $("#filterMatch").val();
|
|
||||||
isWhitelist = $("#filterWhitelist").prop("checked");
|
|
||||||
isInsensitive = $("#filterInsensitive").prop("checked");
|
|
||||||
guildId = selectedServer.id;
|
|
||||||
|
|
||||||
var filterPrimaryKey = await saveFilter(id, name, algorithm, match, isWhitelist, isInsensitive, guildId);
|
|
||||||
|
|
||||||
if (filterPrimaryKey) {
|
|
||||||
showToast("success", "Filter Saved", "Filter ID " + filterPrimaryKey);
|
|
||||||
await loadFilters(guildId);
|
|
||||||
await loadFilterOptions(guildId);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#filterFormModal").modal("hide");
|
|
||||||
});
|
|
||||||
|
|
||||||
async function saveFilter(id, name, algorithm, match, isWhitelist, isInsensitive, guildId) {
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append("name", name);
|
|
||||||
formData.append("matching_algorithm", algorithm);
|
|
||||||
formData.append("match", match);
|
|
||||||
formData.append("is_whitelist", isWhitelist);
|
|
||||||
formData.append("is_insensitive", isInsensitive);
|
|
||||||
formData.append("guild_id", guildId);
|
|
||||||
|
|
||||||
var response;
|
|
||||||
|
|
||||||
try {
|
|
||||||
if (id === "-1") response = await newFilter(formData);
|
|
||||||
else response = await editFilter(id, formData);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
showToast("danger", "Filter Error", err.responseText, 18000);
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearExistingFilterRows() {
|
|
||||||
$("#filtersTable thead .table-select-all").prop("checked", false).prop("indeterminate", false)
|
|
||||||
filtersTable.clear().draw(false)
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#filtersTabPane").on("click", ".table-refresh-btn", async function() {
|
|
||||||
loadFilters(selectedServer.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadFilters(guildId) {
|
|
||||||
if (!guildId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
setTableFilter("filtersTable", "guild_id", guildId);
|
|
||||||
ensureTablePagination("filtersTable");
|
|
||||||
|
|
||||||
$("#filtersTabPane .table-del-btn").prop("disabled", true);
|
|
||||||
clearExistingFilterRows();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var contentFilters = await getFilters(tableFilters["filtersTable"], tableSorts["filtersTable"]);
|
|
||||||
filtersTable.rows.add(contentFilters.results).draw(false);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
showToast("danger", `Error Loading Filters: HTTP ${err.status}`, err, 15000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTableContainer(
|
|
||||||
"filtersTabPane",
|
|
||||||
tableFilters["filtersTable"]["page"],
|
|
||||||
tableFilters["filtersTable"]["page_size"],
|
|
||||||
contentFilters.results.length,
|
|
||||||
contentFilters.count,
|
|
||||||
contentFilters.next,
|
|
||||||
contentFilters.previous
|
|
||||||
);
|
|
||||||
|
|
||||||
$("#filtersTable thead .table-select-all").prop("disabled", contentFilters.results.length === 0);
|
|
||||||
console.debug(`loaded filters, ${contentFilters.results.length} found`);
|
|
||||||
}
|
|
||||||
|
|
||||||
$(document).ready(async function() {
|
|
||||||
await loadMatchingAlgorithms();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadMatchingAlgorithms() {
|
|
||||||
// Disable input while options are loading
|
|
||||||
$("#filterAlgorithm").prop("disabled", true);
|
|
||||||
|
|
||||||
// Delete existing options
|
|
||||||
$("#filterAlgorithm option").each(function() {
|
|
||||||
if ($(this).val())
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear select2 input
|
|
||||||
$("#filterAlgorithm").val("").change();
|
|
||||||
|
|
||||||
try {
|
|
||||||
options = await getFilterOptions();
|
|
||||||
options.actions.POST.matching_algorithm.choices.forEach(algorithm => {
|
|
||||||
$("#filterAlgorithm").append($("<option>", {
|
|
||||||
text: algorithm.display_name,
|
|
||||||
value: algorithm.value > 0 ? algorithm.value : "" // empty string for 'None' option at 0
|
|
||||||
})); // (helps with validation)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Re-enable the input
|
|
||||||
$("#filterAlgorithm").prop("disabled", false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on("selectedServerChange", async function() {
|
|
||||||
await loadFilters(selectedServer.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
// #region Delete Filters
|
|
||||||
|
|
||||||
$("#deleteEditFilter").on("click", async function() {
|
|
||||||
const filterId = parseInt($("#filterId").val());
|
|
||||||
const filter = filtersTable.row(function(idx, row) { return row.id === filterId }).data();
|
|
||||||
const filterName = sanitise(filter.name);
|
|
||||||
|
|
||||||
$("#filterFormModal").modal("hide");
|
|
||||||
|
|
||||||
await confirmationModal(
|
|
||||||
"Delete a Filter",
|
|
||||||
`Do you wish to permanently delete <b>${filterName}</b>?`,
|
|
||||||
"danger",
|
|
||||||
async () => {
|
|
||||||
await deleteFilter(filterId);
|
|
||||||
await loadFilters(selectedServer.id);
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
"Deleted a Filter",
|
|
||||||
filterName,
|
|
||||||
12000
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
$("#filterFormModal").modal("show");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function deleteSelectedFilters() {
|
|
||||||
const rows = filtersTable.rows(".selected").data().toArray();
|
|
||||||
const names = rows.map(row => row.name);
|
|
||||||
const namesString = arrayToHtmlList(names, true).prop("outerHTML");
|
|
||||||
const isMany = names.length > 1;
|
|
||||||
|
|
||||||
await confirmationModal(
|
|
||||||
`Delete ${isMany ? "Many Filters" : "a Filter"}`,
|
|
||||||
`Do you wish to permanently delete ${isMany ? "these" : "this"} <b>${names.length}</b> filter${isMany ? "s" : ""}?<br><br>${namesString}`,
|
|
||||||
"danger",
|
|
||||||
async () => {
|
|
||||||
rows.forEach(async row => { await deleteFilter(row.id) });
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
`Delete ${names.length} Subscription${isMany ? "s" : ""}`,
|
|
||||||
`${arrayToHtmlList(names, false).prop("outerHTML")}`,
|
|
||||||
12000
|
|
||||||
);
|
|
||||||
|
|
||||||
// Multi-deletion can take time, this timeout ensures the refresh is accurate
|
|
||||||
setTimeout(async () => {
|
|
||||||
await loadFilters(selectedServer.id);
|
|
||||||
}, 600);
|
|
||||||
},
|
|
||||||
null
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
@ -1,59 +0,0 @@
|
|||||||
|
|
||||||
$("#serverSettingsBtn").on("click", async function() {
|
|
||||||
await showServerSettingsModal();
|
|
||||||
});
|
|
||||||
|
|
||||||
async function showServerSettingsModal() {
|
|
||||||
const server = selectedServer;
|
|
||||||
var guildSettings;
|
|
||||||
|
|
||||||
try { guildSettings = (await getGuildSettings(server.guild_id)).results[0] }
|
|
||||||
catch (error) {
|
|
||||||
console.error(error)
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#guildSettingsId").val(guildSettings.id);
|
|
||||||
$("#guildSettingsGuildId").val(guildSettings.guild_id);
|
|
||||||
$("#guildSettingsActive").prop("checked", guildSettings.active);
|
|
||||||
updateColourInput("guildSettingsDefaultEmbedColour", guildSettings.default_embed_colour);
|
|
||||||
|
|
||||||
$("#serverSettingsModal").modal("show");
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#serverSettingsForm").on("submit", async function(e) {
|
|
||||||
e.preventDefault();
|
|
||||||
|
|
||||||
var id = $("#guildSettingsId").val();
|
|
||||||
guildId = $("#guildSettingsGuildId").val();
|
|
||||||
active = $("#guildSettingsActive").prop("checked");
|
|
||||||
defaultEmbedColour = getColourInputVal("guildSettingsDefaultEmbedColour", false);
|
|
||||||
|
|
||||||
const pk = await saveGuildSettings(id, guildId, defaultEmbedColour, active);
|
|
||||||
|
|
||||||
if (pk) {
|
|
||||||
showToast("success", "Server Settings Saved", "Primary Key: " + pk);
|
|
||||||
}
|
|
||||||
|
|
||||||
updateDefaultSubEmbedColour();
|
|
||||||
$("#serverSettingsModal").modal("hide");
|
|
||||||
|
|
||||||
})
|
|
||||||
|
|
||||||
async function saveGuildSettings(id, guildId, defaultEmbedColour, active) {
|
|
||||||
var formData = new FormData();
|
|
||||||
formData.append("guild_id", guildId);
|
|
||||||
formData.append("default_embed_colour", defaultEmbedColour);
|
|
||||||
formData.append("active", active);
|
|
||||||
|
|
||||||
var response;
|
|
||||||
try {
|
|
||||||
response = await editGuildSettings(id, formData);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.id;
|
|
||||||
}
|
|
@ -1,725 +0,0 @@
|
|||||||
var subTable = null;
|
|
||||||
discordChannels = [];
|
|
||||||
subSearchTimeout = null;
|
|
||||||
subOptions = null;
|
|
||||||
|
|
||||||
// Create subscription table
|
|
||||||
async function initSubscriptionTable() {
|
|
||||||
// subOptions = await getSubscriptionOptions();
|
|
||||||
await initTable("#subscriptionsTabPane", "subTable", loadSubscriptions, showEditSubModal, deleteSelectedSubscriptions);
|
|
||||||
|
|
||||||
subTable = $("#subTable").DataTable({
|
|
||||||
info: false,
|
|
||||||
paging: false,
|
|
||||||
ordering: false,
|
|
||||||
searching: false,
|
|
||||||
autoWidth: false,
|
|
||||||
order: [],
|
|
||||||
select: {
|
|
||||||
style: "multi+shift",
|
|
||||||
selector: 'th:first-child input[type="checkbox"]'
|
|
||||||
},
|
|
||||||
columnDefs: [
|
|
||||||
{ orderable: false, targets: "no-sort" },
|
|
||||||
{
|
|
||||||
targets: 0,
|
|
||||||
checkboxes: { selectRow: true }
|
|
||||||
},
|
|
||||||
],
|
|
||||||
columns: [
|
|
||||||
{
|
|
||||||
// Select row checkbox column
|
|
||||||
title: '<input type="checkbox" class="form-check-input table-select-all" />',
|
|
||||||
data: null,
|
|
||||||
orderable: false,
|
|
||||||
className: "text-center col-switch-width",
|
|
||||||
render: function() {
|
|
||||||
return '<input type="checkbox" class="form-check-input table-select-row" />'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{ title: "ID", data: "id", visible: false },
|
|
||||||
{
|
|
||||||
title: "Name",
|
|
||||||
data: "name",
|
|
||||||
className: "text-truncate",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const name = sanitise(data);
|
|
||||||
return `<button type="button" onclick="showEditSubModal(${row.id})" class="btn btn-link text-start text-decoration-none">${name}</button>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "URL",
|
|
||||||
data: "url",
|
|
||||||
className: "text-truncate",
|
|
||||||
render: function(data, type) {
|
|
||||||
const url = sanitise(data);
|
|
||||||
return `<a href="${url}" class="btn btn-link text-start text-decoration-none" target="_blank">${url}</a>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Channels",
|
|
||||||
data: "channels_count",
|
|
||||||
className: "text-center",
|
|
||||||
render: function(data) {
|
|
||||||
const channelsCount = sanitise(data);
|
|
||||||
return `<span class="badge text-bg-secondary">${channelsCount}</span>`;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Content Rules",
|
|
||||||
data: "unique_content_rules",
|
|
||||||
className: "text-center text-nowrap",
|
|
||||||
render: function(data, type) {
|
|
||||||
console.log(JSON.stringify(data))
|
|
||||||
|
|
||||||
let badges = $("<div>");
|
|
||||||
|
|
||||||
data.forEach(function(rule, idx) {
|
|
||||||
let badge = $(`<span class="badge text-bg-secondary">${rule.name}</span>`)
|
|
||||||
if (idx > 0) { badge.addClass("ms-2") }
|
|
||||||
badges.append(badge);
|
|
||||||
});
|
|
||||||
|
|
||||||
return badges.html();
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Created",
|
|
||||||
data: "creation_datetime",
|
|
||||||
render: function(data, type) {
|
|
||||||
let dateTime = new Date(data);
|
|
||||||
return $(`
|
|
||||||
<span data-bs-trigger="hover focus"
|
|
||||||
data-bs-html="true"
|
|
||||||
data-bs-custom-class="text-center"
|
|
||||||
data-bs-toggle="popover"
|
|
||||||
data-bs-content="${formatStringDate(dateTime, "%a, %D %B, %Y<br>%H:%M:%S")}">
|
|
||||||
${formatStringDate(dateTime, "%D, %b %Y")}
|
|
||||||
</span>
|
|
||||||
`).popover()[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Notes",
|
|
||||||
data: "extra_notes",
|
|
||||||
orderable: false,
|
|
||||||
className: "text-center",
|
|
||||||
render: function(data, type) {
|
|
||||||
if (!data) { return "" }
|
|
||||||
const extraNotes = sanitise(data);
|
|
||||||
return $(`
|
|
||||||
<i class="bi bi-chat-left-text"
|
|
||||||
data-bs-trigger="hover focus"
|
|
||||||
data-bs-toggle="popover"
|
|
||||||
data-bs-title="Extra Notes"
|
|
||||||
data-bs-content="${extraNotes}">
|
|
||||||
</i>
|
|
||||||
`).popover()[0];
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: "Active",
|
|
||||||
data: "active",
|
|
||||||
orderable: false,
|
|
||||||
className: "text-center form-switch",
|
|
||||||
render: function(data, type) {
|
|
||||||
return `<input type="checkbox" class="sub-toggle-active form-check-input ms-0" ${data ? "checked" : ""} />`
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
orderable: false,
|
|
||||||
className: "p-0",
|
|
||||||
render: function(data, type, row) {
|
|
||||||
const embedColour = sanitise(row.embed_colour);
|
|
||||||
return `<div class="h-100" style="background-color: #${embedColour}; width: .25rem;"> </div>`
|
|
||||||
}
|
|
||||||
}
|
|
||||||
]
|
|
||||||
});
|
|
||||||
|
|
||||||
bindTableCheckboxes("#subTable", subTable, "#subscriptionsTabPane .table-del-btn");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function updateSubFromObject(sub, handleErrorMsg=true) {
|
|
||||||
let data = {
|
|
||||||
"name": sub.name,
|
|
||||||
"url": sub.url,
|
|
||||||
"guild_id": sub.guild_id,
|
|
||||||
"extra_notes": sub.extra_notes,
|
|
||||||
"embed_colour": sub.embed_colour,
|
|
||||||
"article_fetch_image": sub.article_fetch_image,
|
|
||||||
"published_threshold": sub.published_threshold,
|
|
||||||
"active": sub.active
|
|
||||||
};
|
|
||||||
|
|
||||||
let formData = new FormData();
|
|
||||||
|
|
||||||
for (key in data) {
|
|
||||||
formData.append(key, data[key]);
|
|
||||||
}
|
|
||||||
|
|
||||||
sub.article_title_mutators.forEach(mutator => formData.append("article_title_mutators", mutator.id));
|
|
||||||
sub.article_desc_mutators.forEach(mutator => formData.append("article_desc_mutators", mutator.id));
|
|
||||||
sub.filters.forEach(filter => formData.append("filters", filter));
|
|
||||||
|
|
||||||
return await saveSubscription(sub.id, formData, handleErrorMsg=handleErrorMsg);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#subscriptionsTabPane").on("change", ".sub-toggle-active", async function () {
|
|
||||||
|
|
||||||
/*
|
|
||||||
Lock all toggles to soft-prevent spam.
|
|
||||||
There is a rate limit, but allowing the user to
|
|
||||||
reach it from this toggle would be bad.
|
|
||||||
*/
|
|
||||||
$(".sub-toggle-active").prop("disabled", true);
|
|
||||||
|
|
||||||
try {
|
|
||||||
const active = $(this).prop("checked");
|
|
||||||
const sub = subTable.row($(this).closest("tr")).data();
|
|
||||||
|
|
||||||
// Update the table row
|
|
||||||
sub.active = active;
|
|
||||||
subTable.data(sub).draw();
|
|
||||||
|
|
||||||
// Update the database
|
|
||||||
const subId = await updateSubFromObject(sub, handleErrorMsg=false);
|
|
||||||
|
|
||||||
if (!subId) {
|
|
||||||
throw Error("This subscription no longer exists.");
|
|
||||||
}
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
active ? "success" : "danger",
|
|
||||||
"Subscription " + (active ? "Activated" : "Deactivated"),
|
|
||||||
"Subscription ID: " + subId
|
|
||||||
);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
"Error Updating Subscription",
|
|
||||||
`Tried to toggle activeness, but encountered a problem. <br><code>${error}</code>`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Re-enable toggles after 500ms
|
|
||||||
setTimeout(() => {
|
|
||||||
$(".sub-toggle-active").prop("disabled", false); },
|
|
||||||
500
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open new subscription modal
|
|
||||||
$("#addSubscriptionBtn").on("click", async function() {
|
|
||||||
await showEditSubModal(-1);
|
|
||||||
});
|
|
||||||
|
|
||||||
function clearPreviousValidation() {
|
|
||||||
$("#subFormModal, #subAdvancedModal").removeClass("was-validated");
|
|
||||||
$("#subFormModal .invalid-feedback, #subAdvancedModal .invalid-feedback").remove();
|
|
||||||
$("#subFormModal .is-invalid, #subAdvancedModal .is-invalid").removeClass("is-invalid");
|
|
||||||
}
|
|
||||||
|
|
||||||
async function showEditSubModal(subId) {
|
|
||||||
clearPreviousValidation();
|
|
||||||
|
|
||||||
if (subId === -1) {
|
|
||||||
$("#subFormModal .form-create, #subAdvancedModal .form-create").show();
|
|
||||||
$("#subFormModal .form-edit, #subAdvancedModal .form-edit").hide();
|
|
||||||
|
|
||||||
$("#subFormModal input, #subFormModal textarea").val("");
|
|
||||||
$("#subChannels").val("").change();
|
|
||||||
$("#subFilters").val("").change();
|
|
||||||
$("#subTitleMutators").val("").change();
|
|
||||||
$("#subDescMutators").val("").change();
|
|
||||||
$("#subActive").prop("checked", true);
|
|
||||||
$("#subUniqueRules").val(1).change(); // GUID option selected by default
|
|
||||||
|
|
||||||
$("#subEmbedColour .colour-reset").click();
|
|
||||||
$("#subArticleFetchImage").prop("checked", true);
|
|
||||||
|
|
||||||
$("#subPubThreshold").val(getCurrentDateTime());
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
$("#subFormModal .form-create, #subAdvancedModal .form-create").hide();
|
|
||||||
$("#subFormModal .form-edit, #subAdvancedModal .form-edit").show();
|
|
||||||
|
|
||||||
const subscription = subTable.row(function(idx, data, node) {
|
|
||||||
return data.id === subId;
|
|
||||||
}).data();
|
|
||||||
|
|
||||||
$("#subName").val(subscription.name);
|
|
||||||
$("#subUrl").val(subscription.url);
|
|
||||||
$("#subExtraNotes").val(subscription.extra_notes);
|
|
||||||
$("#subActive").prop("checked", subscription.active);
|
|
||||||
|
|
||||||
$("#subTitleMutators").val("").change();
|
|
||||||
$("#subTitleMutators").val(subscription.article_title_mutators.map(mutator => mutator.id)).change();
|
|
||||||
|
|
||||||
$("#subDescMutators").val("").change();
|
|
||||||
$("#subDescMutators").val(subscription.article_desc_mutators.map(mutator => mutator.id)).change();
|
|
||||||
|
|
||||||
const channels = await getSubChannels(subscription.id);
|
|
||||||
$("#subChannels").val("").change();
|
|
||||||
$("#subChannels").val(channels.results.map(channel => channel.channel_id)).change();
|
|
||||||
|
|
||||||
$("#subFilters").val("").change();
|
|
||||||
$("#subFilters").val(subscription.filters).change();
|
|
||||||
|
|
||||||
$("#subUniqueRules").val("").change();
|
|
||||||
$("#subUniqueRules").val(subscription.unique_content_rules.map(rule => rule.id)).change();
|
|
||||||
|
|
||||||
updateColourInput("subEmbedColour", `#${subscription.embed_colour}`);
|
|
||||||
$("#subArticleFetchImage").prop("checked", subscription.article_fetch_image);
|
|
||||||
|
|
||||||
$("#subPubThreshold").val(subscription.published_threshold.split('+')[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#subId").val(subId);
|
|
||||||
$("#subFormModal").modal("show");
|
|
||||||
}
|
|
||||||
|
|
||||||
function getValueFromField(elem) {
|
|
||||||
const tagName = elem.tagName.toLowerCase();
|
|
||||||
const $elem = $(elem);
|
|
||||||
|
|
||||||
if (tagName) { return $elem.val() }
|
|
||||||
|
|
||||||
switch ($elem.attr("type")) {
|
|
||||||
case "checkbox":
|
|
||||||
return $elem.prop("checked");
|
|
||||||
|
|
||||||
default:
|
|
||||||
return $elem.val();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#subForm").on("submit", async function(event) {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
let subId = $("#subId").val();
|
|
||||||
let guildId = selectedServer.id;
|
|
||||||
|
|
||||||
// TODO: move this into a function, so I can fix the active toggle switches which are broken due to this change
|
|
||||||
|
|
||||||
let formData = new FormData();
|
|
||||||
formData.append("guild_id", guildId);
|
|
||||||
|
|
||||||
// Populate formdata with [data-field] control values
|
|
||||||
$('#subForm [data-field], #subAdvancedModal [data-field]').each(function() {
|
|
||||||
const value = getValueFromField(this);
|
|
||||||
if (Array.isArray(value) && !value.length) { return };
|
|
||||||
formData.append($(this).data("field"), value);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Add title mutators to formdata
|
|
||||||
$("#subTitleMutators option:selected").toArray().map(mutator => parseInt(mutator.value)).forEach(
|
|
||||||
mutator => formData.append("article_title_mutators", mutator)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add description mutator to formdata
|
|
||||||
$("#subDescMutators option:selected").toArray().map(mutator => parseInt(mutator.value)).forEach(
|
|
||||||
mutator => formData.append("article_desc_mutators", mutator)
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add Filters to formdata
|
|
||||||
$("#subFilters option:selected").toArray().forEach(
|
|
||||||
filter => formData.append("filters", parseInt(filter.value))
|
|
||||||
);
|
|
||||||
|
|
||||||
for (const [key, value] of formData.entries()) {
|
|
||||||
console.log(`${key}: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// // Unique Content Rules
|
|
||||||
// $("#subUniqueRules option:selected").toArray().forEach(
|
|
||||||
// rule => formData.append("unique_content_rules", parseInt(rule.value))
|
|
||||||
// );
|
|
||||||
|
|
||||||
for (const [key, value] of formData.entries()) {
|
|
||||||
console.log(`${key}: ${value}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This field is constructed differently, so needs to be specifically added
|
|
||||||
formData.append("embed_colour", getColourInputVal("subEmbedColour", false));
|
|
||||||
|
|
||||||
|
|
||||||
subId = await saveSubscription(subId, formData);
|
|
||||||
|
|
||||||
if (subId) {
|
|
||||||
showToast("success", "Subscription Saved", `Subscription ID ${subId}`);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
showToast("danger", "Error Saving Subscription", "");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
await deleteSubChannels(subId);
|
|
||||||
$("#subChannels option:selected").each(async function() {
|
|
||||||
let $channel = $(this);
|
|
||||||
let channelFormData = new FormData();
|
|
||||||
channelFormData.append("channel_id", $channel.val());
|
|
||||||
channelFormData.append("channel_name", $channel.data("name"));
|
|
||||||
channelFormData.append("subscription", subId);
|
|
||||||
await newSubChannel(channelFormData);
|
|
||||||
});
|
|
||||||
|
|
||||||
await loadSubscriptions(guildId);
|
|
||||||
$("#subFormModal").modal("hide");
|
|
||||||
});
|
|
||||||
|
|
||||||
async function saveSubscription(id, formData, handleErrorMsg=true) {
|
|
||||||
let response
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = id === "-1" ? await newSubscription(formData) : await editSubscription(id, formData);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
if (typeof err !== "object" && err.responseJSON) {
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
clearPreviousValidation();
|
|
||||||
for (const [fieldKey, message] of Object.entries(err.responseJSON)) {
|
|
||||||
const $field = $(`#subFormModal [data-field="${fieldKey}"], #subAdvancedModal [data-field="${fieldKey}"]`);
|
|
||||||
const $helpText = $field.closest("div").find(".form-text");
|
|
||||||
const $feedback = $(`<div class="invalid-feedback">${message}</div>`);
|
|
||||||
|
|
||||||
$field.addClass("is-invalid").prop("invalid", true);
|
|
||||||
$feedback.insertAfter($helpText.length ? $helpText : $field);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#subFormModal, #subAdvancedModal").addClass("was-validated");
|
|
||||||
|
|
||||||
if (handleErrorMsg) {
|
|
||||||
showToast("danger", "Subscription Error", err.responseText, 18000);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.id;
|
|
||||||
}
|
|
||||||
|
|
||||||
async function saveSubChannel(formData) {
|
|
||||||
var response
|
|
||||||
|
|
||||||
try {
|
|
||||||
response = await newSubChannel(formData);
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.log(error);
|
|
||||||
showToast("danger", "Failed to save subchannel", error, 18000);
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
return response.id
|
|
||||||
}
|
|
||||||
|
|
||||||
function clearExistingSubRows() {
|
|
||||||
$("#subTable thead .table-select-all").prop("checked", false).prop("indeterminate", false);
|
|
||||||
subTable.clear().draw(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
$("#subscriptionsTabPane").on("click", ".table-refresh-btn", async function() {
|
|
||||||
loadSubscriptions(selectedServer.id);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function loadSubscriptions(guildId) {
|
|
||||||
if (!guildId)
|
|
||||||
return;
|
|
||||||
|
|
||||||
setTableFilter("subTable", "guild_id", guildId);
|
|
||||||
ensureTablePagination("subTable");
|
|
||||||
|
|
||||||
$("#subscriptionsTabPane .table-del-btn").prop("disabled", true);
|
|
||||||
clearExistingSubRows();
|
|
||||||
|
|
||||||
try {
|
|
||||||
var subs = await getSubscriptions(tableFilters["subTable"], tableSorts["subTable"]);
|
|
||||||
subTable.rows.add(subs.results).draw(false);
|
|
||||||
}
|
|
||||||
catch (err) {
|
|
||||||
console.error(err)
|
|
||||||
showToast("danger", `Error Loading Subscriptions: HTTP ${err.status}`, err, 15000);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
updateTableContainer(
|
|
||||||
"subscriptionsTabPane",
|
|
||||||
tableFilters["subTable"]["page"],
|
|
||||||
tableFilters["subTable"]["page_size"],
|
|
||||||
subs.results.length,
|
|
||||||
subs.count,
|
|
||||||
subs.next,
|
|
||||||
subs.previous
|
|
||||||
);
|
|
||||||
|
|
||||||
$("#subTable thead .table-select-all").prop("disabled", subs.results.length === 0);
|
|
||||||
console.debug("loading subs, " + subs.results.length + " found");
|
|
||||||
}
|
|
||||||
|
|
||||||
// #region Server Change Event Handler
|
|
||||||
|
|
||||||
$(document).on("selectedServerChange", async function() {
|
|
||||||
let guildId = selectedServer.id;
|
|
||||||
|
|
||||||
// await updateDefaultSubEmbedColour();
|
|
||||||
|
|
||||||
await loadSubscriptions(guildId);
|
|
||||||
await loadChannelOptions(guildId);
|
|
||||||
await loadFilterOptions(guildId);
|
|
||||||
await loadMutatorOptions();
|
|
||||||
await loadUniqueContentRuleOptions();
|
|
||||||
})
|
|
||||||
|
|
||||||
async function updateDefaultSubEmbedColour(settings=null) {
|
|
||||||
if (!settings){
|
|
||||||
settings = (await getGuildSettings(selectedServer.id)).results[0]
|
|
||||||
}
|
|
||||||
$("#subEmbedColour .colour-reset").attr("data-defaultcolour", "#" + settings.default_embed_colour);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
|
|
||||||
// #region Delete Subscriptions
|
|
||||||
|
|
||||||
// Delete button on the 'edit subscription' modal
|
|
||||||
$("#deleteEditSub").on("click", async function() {
|
|
||||||
const subId = parseInt($("#subId").val());
|
|
||||||
const sub = subTable.row(function(idx, row) { return row.id === subId }).data();
|
|
||||||
const subName = sanitise(sub.name);
|
|
||||||
|
|
||||||
$("#subFormModal").modal("hide");
|
|
||||||
|
|
||||||
await confirmationModal(
|
|
||||||
"Delete a Subscription",
|
|
||||||
`Do you wish to permanently delete <b>${subName}</b>?`,
|
|
||||||
"danger",
|
|
||||||
async () => {
|
|
||||||
await deleteSubscription(subId);
|
|
||||||
await loadSubscriptions(selectedServer.id);
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
"Deleted a Subscription",
|
|
||||||
subName,
|
|
||||||
12000
|
|
||||||
);
|
|
||||||
},
|
|
||||||
async () => {
|
|
||||||
$("#subFormModal").modal("show");
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
async function deleteSelectedSubscriptions() {
|
|
||||||
const rows = subTable.rows(".selected").data().toArray();
|
|
||||||
const names = rows.map(row => row.name);
|
|
||||||
const namesString = arrayToHtmlList(names, true).prop("outerHTML");
|
|
||||||
const isMany = names.length > 1;
|
|
||||||
|
|
||||||
await confirmationModal(
|
|
||||||
`Delete ${isMany ? "Many Subscriptions" : "a Subscription"}`,
|
|
||||||
`Do you wish to permanently delete ${isMany ? "these" : "this"} <b>${names.length}</b> subscription${isMany ? "s" : ""}?<br><br>${namesString}`,
|
|
||||||
"danger",
|
|
||||||
async () => {
|
|
||||||
rows.forEach(async row => { await deleteSubscription(row.id) });
|
|
||||||
|
|
||||||
showToast(
|
|
||||||
"danger",
|
|
||||||
`Deleted ${names.length} Subscription${isMany ? "s" : ""}`,
|
|
||||||
`${arrayToHtmlList(names, false).prop("outerHTML")}`,
|
|
||||||
12000
|
|
||||||
);
|
|
||||||
|
|
||||||
// Multi-deletion can take time, this timeout ensures the refresh is accurate
|
|
||||||
setTimeout(async () => {
|
|
||||||
await loadSubscriptions(selectedServer.id);
|
|
||||||
}, 600);
|
|
||||||
},
|
|
||||||
null
|
|
||||||
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
|
|
||||||
|
|
||||||
// #region Load Modal Options
|
|
||||||
|
|
||||||
async function loadChannelOptions(guildId) {
|
|
||||||
|
|
||||||
// Disable input while options are loading
|
|
||||||
$("#subChannels").prop("disabled", true);
|
|
||||||
|
|
||||||
// Delete existing options
|
|
||||||
$("#subChannels option").each(function() {
|
|
||||||
if ($(this).val())
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear select2 input
|
|
||||||
$("#subChannels").val("").change();
|
|
||||||
try {
|
|
||||||
const channels = await loadChannels(guildId);
|
|
||||||
|
|
||||||
// If we have reached the discord API rate limit
|
|
||||||
if (channels.message && channels.message.includes("rate limit")) {
|
|
||||||
throw new Error(
|
|
||||||
`${channels.message} Retry after ${channels.retry_after} seconds.`
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// If we can't fetch channels due to error
|
|
||||||
if (channels.code === 50001) {
|
|
||||||
let server = getServerFromSnowflake(guildId);
|
|
||||||
|
|
||||||
// Also check that the user hasn't changed the currently active guild, otherwise
|
|
||||||
// the alert will show under the wrong server.
|
|
||||||
if (selectedServer.id === guildId)
|
|
||||||
$("#serverJoinAlert").show();
|
|
||||||
|
|
||||||
// Warning icon on sidebar server select
|
|
||||||
let sidebarItem = $(`#serverList .server-item[data-guild-id="${guildId}"]`);
|
|
||||||
if (!sidebarItem.find(".badge.text-warning").length) {
|
|
||||||
let alertTemplate = $($("#serverItemIconTemplate").html());
|
|
||||||
alertTemplate.attr("data-bs-title", `The Bot isn't a member of ${sanitise(server.name)}`).tooltip();
|
|
||||||
sidebarItem.find(".server-item-selector").append(alertTemplate);
|
|
||||||
}
|
|
||||||
|
|
||||||
const guildName = sanitise(getServerFromSnowflake(guildId).name);
|
|
||||||
|
|
||||||
throw new Error(
|
|
||||||
`Unable to retrieve channels from Guild <b>${guildName}</b>.
|
|
||||||
Ensure that @PYRSS is a member with permissions
|
|
||||||
to view channels.`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Sort by the specified position of each channel object
|
|
||||||
channels.sort((a, b) => a.position - b.position);
|
|
||||||
|
|
||||||
discordChannels = [];
|
|
||||||
channels.forEach(channel => {
|
|
||||||
|
|
||||||
// We only want TextChannels, which have a type of 0
|
|
||||||
if (channel.type !== 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
let channelObj = {text: `#${channel.name}`, value: channel.id, "data-name": channel.name}
|
|
||||||
$("#subChannels").append($("<option>", channelObj));
|
|
||||||
discordChannels.push(channelObj);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast("danger", "Error loading channels", error, 18000);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Re-enable the input
|
|
||||||
$("#subChannels").prop("disabled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadUniqueContentRuleOptions() {
|
|
||||||
$("#subUniqueRules").prop("disabled", true);
|
|
||||||
|
|
||||||
$("#subUniqueRules option").each(function() {
|
|
||||||
if ($(this).val()) { $(this).remove() }
|
|
||||||
});
|
|
||||||
|
|
||||||
$("#subUniqueRules").val("").change();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const rules = await getUniqueContentRules();
|
|
||||||
rules.results.forEach(rule => {
|
|
||||||
$("#subUniqueRules").append($("<option>", {
|
|
||||||
text: rule.name,
|
|
||||||
value: rule.id
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch (error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast("danger", "Error loading Unique Content Rules", error, 18000);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
$("#subUniqueRules").prop("disabled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadMutatorOptions() {
|
|
||||||
|
|
||||||
// Disable input while options are loading
|
|
||||||
$(".sub-mutators-field").prop("disabled", true);
|
|
||||||
|
|
||||||
// Delete existing options
|
|
||||||
$(".sub-mutators-field option").each(function() {
|
|
||||||
if ($(this).val())
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear select2 input
|
|
||||||
$(".sub-mutators-field").val("").change();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const mutators = await getMutators();
|
|
||||||
mutators.forEach(mutator => {
|
|
||||||
$(".sub-mutators-field").append($("<option>", {
|
|
||||||
text: mutator.name,
|
|
||||||
value: mutator.id
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast("danger", "Error loading sub mutators", error, 18000);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Re-enable the input
|
|
||||||
$(".sub-mutators-field").prop("disabled", false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
async function loadFilterOptions(guildId) {
|
|
||||||
|
|
||||||
// Disable input while options are loading
|
|
||||||
$("#subFilters").prop("disabled", true);
|
|
||||||
|
|
||||||
// Delete existing options
|
|
||||||
$("#subFilters option").each(function() {
|
|
||||||
if ($(this).val())
|
|
||||||
$(this).remove();
|
|
||||||
});
|
|
||||||
|
|
||||||
// Clear select2 input
|
|
||||||
$("#subFilters").val("").change();
|
|
||||||
|
|
||||||
try {
|
|
||||||
const filters = await getFilters({guild_id: guildId});
|
|
||||||
console.log(JSON.stringify(filters));
|
|
||||||
|
|
||||||
filters.results.forEach(filter => {
|
|
||||||
$("#subFilters").append($("<option>", {
|
|
||||||
text: filter.name,
|
|
||||||
value: filter.id
|
|
||||||
}));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
catch(error) {
|
|
||||||
console.error(error);
|
|
||||||
showToast("danger", "Error loading sub filters", error, 18000);
|
|
||||||
}
|
|
||||||
finally {
|
|
||||||
// Re-enable the input
|
|
||||||
$("#subFilters").prop("disabled", false);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// #endregion
|
|
Loading…
x
Reference in New Issue
Block a user