517 lines
20 KiB
HTML
517 lines
20 KiB
HTML
{% extends "layouts/base.html" %}
|
|
{% load static %}
|
|
|
|
{% block title %} Main Page {% endblock title %}
|
|
|
|
<!-- Specific CSS goes HERE -->
|
|
{% block stylesheets %}
|
|
<style>
|
|
|
|
.draw-border-success:hover {
|
|
color: var(--bs-success);
|
|
}
|
|
.draw-border-success:hover::before, .draw-border-success:hover::after {
|
|
border-color: var(--bs-success);
|
|
}
|
|
|
|
.draw-border-primary:hover {
|
|
color: var(--bs-primary);
|
|
}
|
|
.draw-border-primary:hover::before, .draw-border-primary:hover::after {
|
|
border-color: var(--bs-primary);
|
|
}
|
|
|
|
.draw-border-secondary:hover {
|
|
color: var(--bs-secondary);
|
|
}
|
|
.draw-border-secondary:hover::before, .draw-border-secondary:hover::after {
|
|
border-color: var(--bs-secondary);
|
|
}
|
|
|
|
.draw-border-danger:hover {
|
|
color: var(--bs-danger);
|
|
}
|
|
.draw-border-danger:hover::before, .draw-border-danger:hover::after {
|
|
border-color: var(--bs-danger);
|
|
}
|
|
|
|
|
|
.draw-border {
|
|
box-shadow: inset 0 0 0 1px var(--border-colour);
|
|
color: var(--bs-body-color);
|
|
transition: color 0.15s 0.0833333333s;
|
|
position: relative;
|
|
}
|
|
.draw-border::before, .draw-border::after {
|
|
border: 0 solid transparent;
|
|
box-sizing: border-box;
|
|
content: '';
|
|
pointer-events: none;
|
|
position: absolute;
|
|
width: 0;
|
|
height: 0;
|
|
bottom: 0;
|
|
right: 0;
|
|
}
|
|
.draw-border::before {
|
|
border-bottom-width: 1px;
|
|
border-left-width: 1px;
|
|
}
|
|
.draw-border::after {
|
|
border-top-width: 1px;
|
|
border-right-width: 1px;
|
|
}
|
|
.draw-border:hover::before, .draw-border:hover::after {
|
|
transition: border-color 0s, width 0.15s, height 0.15s;
|
|
width: 100%;
|
|
height: 100%;
|
|
}
|
|
.draw-border:hover::before {
|
|
transition-delay: 0s, 0s, 0.15s;
|
|
}
|
|
.draw-border:hover::after {
|
|
transition-delay: 0s, 0.15s, 0s;
|
|
}
|
|
|
|
/* modal tabs */
|
|
.modal-tabs {
|
|
background-color: var(--bs-tertiary-bg);
|
|
}
|
|
|
|
.modal-tabs .nav-link {
|
|
border-top: none;
|
|
border-radius: 0;
|
|
}
|
|
|
|
.modal-tabs .nav-link:first-child {
|
|
margin-left: 1rem;
|
|
}
|
|
|
|
.modal-tabs .nav-link:last-child {
|
|
margin-right: 1rem;
|
|
}
|
|
|
|
</style>
|
|
{% endblock stylesheets %}
|
|
|
|
{% block content %}
|
|
|
|
<!-- ### $App Screen Content ### -->
|
|
<main class="main-content bg-body-tertiary">
|
|
<div id="mainContent">
|
|
<!--
|
|
<div class="row gap-20 masonry pos-r">
|
|
<div class="masonry-sizer col-md-6"></div>
|
|
|
|
<div class="masonry-item col-12">
|
|
<div class="row gap-20"> -->
|
|
|
|
<!-- Total Subscriptions -->
|
|
<!-- <div class="col-md-3">
|
|
<div class="layers bd bg-body p-20">
|
|
<div class="layer w-100 mB-10">
|
|
<h6 class="lh-1">Total Subscriptions</h6>
|
|
</div>
|
|
<div class="layer w-100">
|
|
<div class="peers ai-sb fxw-nw">
|
|
<div class="peer peer-greed">
|
|
<span id="sparklinedash"></span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="d-ib lh-0 va-m fw-600 bdrs-10em pX-15 pY-15 bgc-green-50 c-green-500">
|
|
<span class="subs-count">--</span>
|
|
</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
|
|
<!-- Subscription Activity (24hrs) -->
|
|
<!-- <div class="col-md-3">
|
|
<div class="layers bd bg-body p-20">
|
|
<div class="layer w-100 mB-10">
|
|
<h6 class="lh-1">Subscription Activity (24hrs)</h6>
|
|
</div>
|
|
<div class="layer w-100">
|
|
<div class="peers ai-sb fxw-nw">
|
|
<div class="peer peer-greed">
|
|
<span id="sparklinedash2"></span>
|
|
</div>
|
|
<div class="peer">
|
|
<span class="d-ib lh-0 va-m fw-600 bdrs-10em pX-15 pY-15 bgc-deep-purple-50 c-deep-purple-500">47</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div> -->
|
|
|
|
<!-- </div>
|
|
</div>
|
|
|
|
<div class="masonry-item col-12">
|
|
<hr>
|
|
</div>
|
|
</div> -->
|
|
<div class="container-fluid mT-10 pX-0">
|
|
<div class="lh-1 pX-5 pB-20 peers">
|
|
<div class="peer peer-greed">
|
|
<h3 class="mB-0">Subscriptions</h3>
|
|
</div>
|
|
<div class="peer fs-5 fw-bold">
|
|
<button class="btn bg-body bdrs-2 mR-3 cur-p draw-border draw-border-success" onclick="subEditModal(-1);">
|
|
<i class="bi bi-plus"></i>
|
|
New Subscription
|
|
</button>
|
|
</div>
|
|
</div>
|
|
|
|
<div id="subscriptionContainer" class="row gap-20">
|
|
|
|
<!-- Subscriptions Here! -->
|
|
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</main>
|
|
|
|
<div id="subEditModal" class="modal" data-bs-backdrop="static" data-bs-keyboard="false" tabindex="-1" aria-hidden="true">
|
|
<div class="modal-dialog">
|
|
<div class="modal-content bdrs-2 bd">
|
|
<div class="modal-header">
|
|
<h5 class="modal-title"></h5>
|
|
</div>
|
|
<nav>
|
|
<div class="nav nav-tabs modal-tabs" role="tablist">
|
|
<button id="navDetailsTab" class="nav-link" data-bs-toggle="tab" data-bs-target="#navDetailsPanel" type="button" role="tab" aria-controls="navDetailsPanel" aria-selected="true">Details</button>
|
|
<button id="navChannelTab" class="nav-link" data-bs-toggle="tab" data-bs-target="#navChannelPanel" type="button" role="tab" aria-controls="navChannelPanel" aria-selected="true">Channels</button>
|
|
<button id="navExtrasTab" class="nav-link" data-bs-toggle="tab" data-bs-target="#navExtrasPanel" type="button" role="tab" aria-controls="navExtrasPanel" aria-selected="true">Extras</button>
|
|
</div>
|
|
</nav>
|
|
<div class="modal-body">
|
|
<form id="subEditForm" class="needs-validation" novalidate>
|
|
<div class="tab-content">
|
|
<div id="navDetailsPanel" class="tab-pane fade" role="tabpanel" aria-labelledby="navDetailsTab" tabindex="0">
|
|
<div class="mb-3">
|
|
<label for="editSubName" class="form-label">Name</label>
|
|
<input type="text" name="editSubName" id="editSubName" class="form-control bdrs-2 bd" required maxlength="32">
|
|
<div class="invalid-feedback">Required, Maximum of 32 Characters.</div>
|
|
</div>
|
|
<div class="mb-3">
|
|
<label for="editSubURL" class="form-label">URL</label>
|
|
<input type="url" name="editSubURL" id="editSubURL" class="form-control bdrs-2 bd" required>
|
|
<div class="invalid-feedback">Please Enter a Valid URL.</div>
|
|
</div>
|
|
<div>
|
|
<label for="editSubImage" class="form-label">Image</label>
|
|
<input type="file" name="editSubImage" id="editSubImage" class="form-control bdrs-2 bd">
|
|
<div class="form-text">Optional</div>
|
|
<div class="invalid-feedback">This Image Is Invalid.</div>
|
|
</div>
|
|
</div>
|
|
<div id="navChannelPanel" class="tab-pane fade" role="tabpanel" aria-labelledby="navChannelTab" tabindex="0">
|
|
<div class="mb-3">
|
|
<label for="editSubServer" class="form-label">Server</label>
|
|
<select name="editSubServer" id="editSubServer" class="form-select bdrs-2 bd" required>
|
|
<option value="">Select a server</option>
|
|
</select>
|
|
<div class="form-help">Ensure that PYRSS Bot is a member of the selected server.</div>
|
|
<div class="invalid-feedback">Please Select a Server.</div>
|
|
</div>
|
|
<div>
|
|
<label for="editSubChannels">Channels</label>
|
|
<select name="editSubChannels" id="editSubChannels" class="form-select bdrs-2 bd" required multiple>
|
|
<option value="1039201459188805692">CodeHub</option>
|
|
<option value="753323563381031042">Orange</option>
|
|
<option value="1204426362794811453">PYRSS Home</option>
|
|
<option value="410208534861447168">Ryujinx</option>
|
|
</select>
|
|
</div>
|
|
</div>
|
|
<div id="navExtrasPanel" class="tab-pane fade" role="tabpanel" aria-labelledby="navExtrasTab" tabindex="0">
|
|
<div>
|
|
<label for="editSubNotes" class="form-label">Notes</label>
|
|
<textarea name="editSubNotes" id="editSubNotes" class="form-control" style="height: 200px;"></textarea>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
<div class="modal-footer">
|
|
<button class="btn bg-body-tertiary bdrs-2 cur-p draw-border draw-border-success" onclick="submitSubEditModal();">
|
|
<i class="bi bi-check me-1`"></i>
|
|
Submit
|
|
</button>
|
|
<button class="btn bg-body-tertiary bdrs-2 cur-p draw-border draw-border-secondary" data-bs-dismiss="modal">
|
|
<i class="bi bi-x me-1`"></i>
|
|
Cancel
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{% endblock content %}
|
|
|
|
<!-- Specific Page JS goes HERE -->
|
|
{% block javascripts %}
|
|
<script id="subItemTemplate" type="text/template">
|
|
<div class="col-md-4 col-xxl-3">
|
|
<div class="sub-item layers bd bg-body h-100 rounded-3" data-uuid="">
|
|
<!-- <div class="layer w-100 bdB pb-4">
|
|
<div class="d-flex px-4">
|
|
<span class="h5 lh-1 sub-name mb-0"></span>
|
|
<img class="sub-img h-100">
|
|
</div>
|
|
</div>
|
|
<div class="layer w-100 py-4 px-4">
|
|
<p class="sub-desc mb-0"></p>
|
|
</div>
|
|
<div class="layer w-100 mb-3 px-4">
|
|
<span class=sub-uuid></span>
|
|
</div>
|
|
<div class="layer w-100 mb-3 px-4">
|
|
<a class="sub-rss"></a>
|
|
</div>
|
|
<div class="layer w-100 mb-3 px-4">
|
|
<img class="sub-img mw-100" height="75">
|
|
</div>
|
|
<div class="layer d-flex flex-wrap w-100 mt-auto bdT pb-3 px-4">
|
|
<div class="me-auto d-flex ai-c">
|
|
<div class="form-check">
|
|
<input type="checkbox" class="form-check-input mt-3" style="transform:scale(1.25);">
|
|
</div>
|
|
</div>
|
|
<div>
|
|
<button class="sub-edit btn btn-sm bg-body-tertiary bdrs-2 mt-3 cur-p draw-border draw-border-secondary">
|
|
<i class="bi bi-pencil me-1`"></i>
|
|
Edit
|
|
</button>
|
|
<button class="sub-delete btn btn-sm bg-body-tertiary bdrs-2 mt-3 cur-p draw-border draw-border-danger">
|
|
<i class="bi bi-trash me-1`"></i>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div> -->
|
|
<div class="layer w-100">
|
|
<div class="peers p-4 flex-nowrap">
|
|
<div class="peer peer-greed me-4">
|
|
<h5 class="sub-name"></h5>
|
|
<span class="sub-uuid"></span>
|
|
</div>
|
|
<div class="peer">
|
|
<img src="" alt="" class="sub-img">
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="w-50 align-self-start bd mx-4"></div>
|
|
<div class="layer w-100 p-4">
|
|
<p class="sub-desc mb-4"></p>
|
|
<a class="sub-rss"></a>
|
|
</div>
|
|
<div class="layer w-100 bdT p-4 mt-auto">
|
|
<div class="peers ai-c">
|
|
<div class="peer peer-greed">
|
|
<div class="form-check form-switch ms-2">
|
|
<input type="checkbox" name="" id="" class="form-check-input" style="transform: scale(1.5);">
|
|
</div>
|
|
</div>
|
|
<div class="peer">
|
|
<button class="sub-edit btn bg-body-tertiary rounded-3">
|
|
<i class="bi bi-pencil me-1"></i>
|
|
Edit
|
|
</button>
|
|
</div>
|
|
<div class="peer ms-3">
|
|
<button class="sub-delete btn bg-body-tertiary rounded-3">
|
|
<i class="bi bi-trash3 me-1"></i>
|
|
Delete
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</script>
|
|
<script src="{% static 'js/api.js' %}"></script>
|
|
<script type="text/javascript">
|
|
function createSubscriptionItem(data) {
|
|
var template = $($("#subItemTemplate").html());
|
|
template.find(".sub-item").attr("data-uuid", data.uuid);
|
|
template.find(".sub-name").text(data.name);
|
|
template.find(".sub-uuid").text(data.uuid);
|
|
template.find(".sub-rss").text(data.rss_url).attr("href", data.rss_url);
|
|
template.find(".sub-img").attr("src", data.image);
|
|
template.find(".sub-desc").text(data.extra_notes);
|
|
template.find(".sub-edit").attr("onclick", `subEditModal("${data.uuid}");`);
|
|
template.find(".sub-delete").attr("onclick", `unsubscribe("${data.uuid}");`);
|
|
return template
|
|
}
|
|
|
|
$(document).ready(function() {
|
|
loadGuilds();
|
|
loadSubscriptions();
|
|
|
|
$("#editSubServer").change(function() {
|
|
loadChannels($(this).find("option:selected").attr("value"));
|
|
});
|
|
});
|
|
|
|
function loadGuilds() {
|
|
$.ajax({
|
|
url: "/guilds",
|
|
type: "GET",
|
|
success: function(response) {
|
|
for (i = 1; i < response.length; i++) {
|
|
var guild = response[i];
|
|
$("#editSubServer").append($("<option>", {
|
|
value: guild.id,
|
|
text: guild.name
|
|
}));
|
|
}
|
|
},
|
|
error: function(response) {
|
|
alert(JSON.stringify(response, null, 4));
|
|
}
|
|
});
|
|
}
|
|
|
|
function loadChannels(guildID) {
|
|
$("#editSubChannels").empty();
|
|
$.ajax({
|
|
url: `/channels?guild=${guildID}`,
|
|
type: "GET",
|
|
success: function(response) {
|
|
for (i = 1; i < response.length; i++) {
|
|
var channel = response[i];
|
|
|
|
if (channel.type !== 0) {
|
|
continue
|
|
}
|
|
|
|
var selectedChannelIDs = $("#editSubChannels").attr("data-current").split(";");
|
|
|
|
$("#editSubChannels").append($("<option>", {
|
|
value: channel.id,
|
|
text: "#" + channel.name,
|
|
selected: selectedChannelIDs.includes(channel.id.toString())
|
|
}));
|
|
}
|
|
},
|
|
error: function(response) {
|
|
console.error(JSON.stringify(response, null, 4));
|
|
|
|
if (response.code == "50001") {
|
|
alert("PYRSS Bot is Missing Access to this Server");
|
|
}
|
|
else {
|
|
alert("unknown error fetching channels " + response.code)
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
function updateSubscriptionCount(difference, overwrite) {
|
|
const beforeChange = overwrite ? 0 : Number($(".subs-count").text());
|
|
$(".subs-count").text(beforeChange + difference);
|
|
}
|
|
|
|
function loadSubscriptions() {
|
|
$("#subscriptionContainer").empty();
|
|
getSubscriptions().then(resp => {
|
|
updateSubscriptionCount(resp.results.length, true);
|
|
for (i = 0; i < resp.results.length; i++) {
|
|
var sub = resp.results[i];
|
|
console.log(JSON.stringify(sub));
|
|
var subElem = createSubscriptionItem(sub);
|
|
$("#subscriptionContainer").append(subElem);
|
|
}
|
|
});
|
|
}
|
|
|
|
function unsubscribe(uuid) {
|
|
var subElem = $(`#subscriptionContainer .sub-item[data-uuid="${uuid}"]`);
|
|
subElem.find("button").prop("disabled", true);
|
|
|
|
deleteSubscription(uuid).then(resp => {
|
|
subElem.parent().remove();
|
|
updateSubscriptionCount(-1);
|
|
});
|
|
}
|
|
|
|
|
|
function subEditModal(uuid) {
|
|
|
|
var modal = $("#subEditModal");
|
|
|
|
modal.find("input").val(null);
|
|
modal.find("textarea").val(null);
|
|
modal.find("select").val("");
|
|
$("#editSubChannels").empty();
|
|
$("#subEditForm").removeClass("was-validated");
|
|
$("#navDetailsTab").click();
|
|
|
|
if (uuid === -1) {
|
|
modal.find(".modal-title").text("New Subscription");
|
|
}
|
|
else {
|
|
modal.find(".modal-title").text("Edit Subscription");
|
|
|
|
getSubscription(uuid).then(resp => {
|
|
// alert(JSON.stringify(resp, null, 4));
|
|
$("#editSubName").val(resp.name);
|
|
$("#editSubURL").val(resp.rss_url);
|
|
$("#editSubServer").val(String(resp.server)).trigger("change");
|
|
$("#editSubChannels").attr("data-current", resp.targets);
|
|
$("#editSubNotes").val(resp.extra_notes);
|
|
});
|
|
|
|
}
|
|
|
|
$("#subEditModal").attr("data-uuid", uuid);
|
|
$("#subEditModal").modal("show");
|
|
}
|
|
|
|
function submitSubEditModal() {
|
|
// Validation
|
|
var form = $("#subEditForm");
|
|
if (!form[0].checkValidity()) {
|
|
form.addClass("was-validated");
|
|
return;
|
|
}
|
|
|
|
const uuid = $("#subEditModal").attr("data-uuid");
|
|
|
|
var formData = new FormData();
|
|
formData.append("uuid", uuid);
|
|
formData.append("name", $("#editSubName").val());
|
|
formData.append("rss_url", $("#editSubURL").val());
|
|
formData.append("server", $("#editSubServer").val());
|
|
formData.append("extra_notes", $("#editSubNotes").val());
|
|
|
|
var selectedTargets = $("#editSubChannels option:selected").toArray().map(item => item.value).join(';');
|
|
formData.append("targets", selectedTargets);
|
|
|
|
var imageFile = $("#editSubImage")[0].files[0];
|
|
if (imageFile) {
|
|
formData.append("image", imageFile);
|
|
}
|
|
|
|
if (uuid === "-1") {
|
|
newSubscription(formData).then(resp => {
|
|
loadSubscriptions();
|
|
$("#subEditModal").modal("hide");
|
|
})
|
|
}
|
|
else {
|
|
editSubscription(uuid, formData).then(resp => {
|
|
loadSubscriptions();
|
|
$("#subEditModal").modal("hide");
|
|
});
|
|
}
|
|
|
|
|
|
}
|
|
</script>
|
|
{% endblock javascripts %}
|