Add Arcadia Notifications to Front end

This commit is contained in:
Andrew 2023-09-01 14:48:20 -04:00
parent 834f6f3c3b
commit 6c1e1b4737
6 changed files with 248 additions and 18 deletions

View File

@ -45,6 +45,7 @@ class Users(BaseModel):
manager = IntegerField(default=None, null=True)
pfp = CharField(default="/static/assets/images/faces-clipart/pic-3.png")
theme = CharField(default="default")
cleared_notifs = CharField(default="default")
class Meta:
table_name = "users"
@ -171,6 +172,7 @@ class HelperUsers:
"roles": [],
"servers": [],
"support_logs": "",
"cleared_notifs": "",
}
user = model_to_dict(Users.get(Users.user_id == user_id))

View File

@ -581,12 +581,16 @@ class Helpers:
@staticmethod
def get_announcements():
data = (
'[{"id":"1","date":"Unknown",'
'"title":"Error getting Announcements",'
'"desc":"Error getting Announcements","link":""}]'
)
data = []
data = [
{
"id": "4db1a43b-d451-4abc-bb10-5a45676d95f7",
"date": "Unknown",
"title": "Error getting Announcements",
"desc": "Error getting Announcements",
"link": "",
}
]
try:
response = requests.get("https://craftycontrol.com/notify.json", timeout=2)
data = json.loads(response.content)

View File

@ -40,6 +40,7 @@ from app.classes.web.routes.api.users.user.permissions import (
)
from app.classes.web.routes.api.users.user.pfp import ApiUsersUserPfpHandler
from app.classes.web.routes.api.users.user.public import ApiUsersUserPublicHandler
from app.classes.web.routes.api.crafty.announcements.index import ApiAnnounceIndexHandler
def api_handlers(handler_args):
@ -55,6 +56,11 @@ def api_handlers(handler_args):
ApiAuthInvalidateTokensHandler,
handler_args,
),
(
r"/api/v2/crafty/announcements/?",
ApiAnnounceIndexHandler,
handler_args,
),
# User routes
(
r"/api/v2/users/?",

View File

@ -0,0 +1,106 @@
import logging
import json
from jsonschema import ValidationError, validate
from app.classes.web.base_api_handler import BaseApiHandler
logger = logging.getLogger(__name__)
notif_schema = {
"type": "object",
"properties": {
"id": {"type": "string"},
},
"additionalProperties": False,
"minProperties": 1,
}
class ApiAnnounceIndexHandler(BaseApiHandler):
def get(self):
auth_data = self.authenticate_user()
if not auth_data:
return
(
_,
_exec_user_crafty_permissions,
_,
_,
_user,
) = auth_data
data = self.helper.get_announcements()
cleared = str(
self.controller.users.get_user_by_id(auth_data[4]["user_id"])[
"cleared_notifs"
]
).split(",")
res = [d.get("id", None) for d in data]
# remove notifs that are no longer in Crafty.
for item in cleared[:]:
if item not in res:
cleared.remove(item)
if len(cleared) > 0:
for item in data[:]:
if item["id"] in cleared:
data.remove(item)
self.finish_json(
200,
{
"status": "ok",
"data": data,
},
)
def post(self):
auth_data = self.authenticate_user()
if not auth_data:
return
(
_,
_exec_user_crafty_permissions,
_,
_,
_user,
) = auth_data
print(self.request)
try:
data = json.loads(self.request.body)
print(data)
except json.decoder.JSONDecodeError as e:
return self.finish_json(
400, {"status": "error", "error": "INVALID_JSON", "error_data": str(e)}
)
try:
validate(data, notif_schema)
except ValidationError as e:
return self.finish_json(
400,
{
"status": "error",
"error": "INVALID_JSON_SCHEMA",
"error_data": str(e),
},
)
announcements = self.helper.get_announcements()
res = [d.get("id", None) for d in announcements]
cleared_notifs = str(
self.controller.users.get_user_by_id(auth_data[4]["user_id"])[
"cleared_notifs"
]
).split(",")
# remove notifs that are no longer in Crafty.
for item in cleared_notifs[:]:
if item not in res:
cleared_notifs.remove(item)
cleared_notifs.append(data["id"])
updata = {"cleared_notifs": ",".join(cleared_notifs)}
self.controller.users.update_user(auth_data[4]["user_id"], updata)
self.finish_json(
200,
{
"status": "ok",
"data": {},
},
)

View File

@ -1,27 +1,32 @@
<ul class="navbar-nav ml-auto">
<li class="nav-item dropdown">
<a class="nav-link count-indicator">
<a class="nav-link count-indicator dropdown-toggle" id="notifDropdown" href="#" data-toggle="dropdown"
aria-expanded="false">
<i class="fas fa-broadcast-tower
{% if data.get('update_available') %}
text-danger
{% end %}
"></i>
<!-- <span class="count bg-success">3</span>-->
</a>
"></i><span id="notif-count" class="badge badge-notify"></span> </a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" style="width: 40vw;" aria-labelledby="notifDropdown">
<ul id="announcements">
</ul>
</div>
</li>
<li class="nav-item dropdown">
<a class="nav-link count-indicator" href="/panel/panel_config">
<a class="nav-link" href="/panel/panel_config">
<i class="fas fa-cogs"></i>
</a>
</li>
<li class="nav-item dropdown user-dropdown">
<a class="nav-link dropdown-toggle" id="UserDropdown" href="#" data-toggle="dropdown" aria-expanded="false">
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image"> </a>
<img class="img-xs rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}"
alt="Profile image"> </a>
<div class="dropdown-menu dropdown-menu-right navbar-dropdown" aria-labelledby="UserDropdown">
<div class="dropdown-header text-center">
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}" alt="Profile image">
<img class="img-md rounded-circle profile-picture" onerror="pfpError(this)" src="{{ data['user_data']['pfp'] }}"
alt="Profile image">
<p class="mb-1 mt-3 font-weight-semibold">{{ data['user_data']['username'] }}</p>
<p class="font-weight-light text-muted mb-0">Roles: </p>
{% for r in data['user_role'] %}
@ -33,27 +38,118 @@
<p class="font-weight-light text-muted mb-0">Email: {{ data['user_data']['email'] }}</p>
</div>
{% if data['user_data']['preparing'] %}
<span class="dropdown-item" id="support_progress"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}<br><br></span>
<span class="dropdown-item" id="support_progress"><i
class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs',
data['lang']) }}<br><br></span>
<span class="dropdown-item" id="support_progress">
<div class="support_progress" style="height: 15px; width: 100%;">
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar" style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
<div class="progress-bar progress-bar-striped progress-bar-animated" id="logs_progress_bar" role="progressbar"
style="width:0%;" aria-valuenow="0" aria-valuemin="0" aria-valuemax="100">0%</div>
</div>
</span>
{% else %}
<a class="dropdown-item" id="support_logs"><i class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs', data['lang']) }}</i></a>
<a class="dropdown-item" id="support_logs"><i
class="dropdown-item-icon mdi mdi-download-outline text-primary"></i>{{ translate('notify', 'supportLogs',
data['lang']) }}</i></a>
{% end %}
{% if data['superuser'] %}
<a class="dropdown-item" href="/panel/activity_logs"><i class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify', 'activityLog', data['lang']) }}</a>
<a class="dropdown-item" href="/panel/activity_logs"><i
class="dropdown-item-icon mdi mdi-calendar-check-outline text-primary"></i>{{ translate('notify',
'activityLog', data['lang']) }}</a>
{% end %}
<a class="dropdown-item" href="/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{ translate('notify', 'logout', data['lang']) }}</a>
<a class="dropdown-item" href="/logout"><i class="dropdown-item-icon mdi mdi-power text-primary"></i>{{
translate('notify', 'logout', data['lang']) }}</a>
</div>
</li>
</ul>
<style>
.badge-notify {
background: var(--purple);
position: absolute;
-moz-transform: translate(-70%, -70%);
/* For Firefox */
-ms-transform: translate(-70%, -70%);
/* for IE */
-webkit-transform: translate(-70%, -70%);
/* For Safari, Chrome, iOS */
-o-transform: translate(-70%, -70%);
}
.clear-button:hover {
cursor: pointer;
}
</style>
<script>
function pfpError(image) {
image.onerror = "";
image.src = "/static/assets/images/faces-clipart/pic-3.png";
return true;
}
function updateAnnouncements(data) {
console.log(data)
let text = "";
for (i = 0; i < data.length; i++) {
text += `<li id="${data[i].id}"><p style="float: right;"><i data-id="${data[i].id}"class="clear-button fa-regular fa-x"></i></p><a style="color: var(--purple);" href=${data[i].link} target="_blank"><h6>${data[i].title}</h6><small><p>${data[i].date}</p></small><p>${data[i].desc}</p></li></a>`
}
if (data.length > 0) {
localStorage.setItem("notif-count", data.length);
$("#notif-count").html(data.length);
$("#announcements").html(text);
} else {
$("#announcements").html("<p style='margin-top: 15px;' class='text-center'>No notifications</p>");
}
$(".clear-button").on("click", function (event) {
console.log("CLEAR BUTTON")
let uuid = event.target.getAttribute("data-id");
$(`#${uuid}`).remove();
send_clear(uuid);
notif_count = localStorage.getItem("notif-count") - 1;
if (notif_count > 0) {
localStorage.setItem("notif-count", notif_count);
$("#notif-count").html(notif_count);
} else {
$("#announcements").html("<p style='margin-top: 15px;' class='text-center'>No notifications</p>")
$("#notif-count").html("");
}
});
}
async function getAnnouncements() {
var token = getCookie("_xsrf");
let res = await fetch(`/api/v2/crafty/announcements/`, {
method: 'GET',
headers: {
'X-XSRFToken': token
},
});
let responseData = await res.json();
console.log(responseData);
if (responseData.status === "ok") {
updateAnnouncements(responseData.data)
} else {
updateAnnouncements("<li><p>Trouble Getting Annoucements</p></li>")
}
}
async function send_clear(uuid) {
var token = getCookie("_xsrf");
let body = JSON.stringify({ "id": uuid });
console.log(body)
let res = await fetch(`/api/v2/crafty/announcements/`, {
method: 'POST',
headers: {
'X-XSRFToken': token,
},
body: body,
});
let responseData = await res.json();
console.log(responseData);
if (responseData.status === "ok") {
return
} else {
bootbox.alert(responseData.error)
}
}
$(document).ready(function () {
getAnnouncements();
})
</script>

View File

@ -0,0 +1,16 @@
# Generated by database migrator
import peewee
def migrate(migrator, database, **kwargs):
migrator.add_columns("users", cleared_notifs=peewee.CharField(default=""))
"""
Write your migrations here.
"""
def rollback(migrator, database, **kwargs):
migrator.drop_columns("users", ["cleared_notifs"])
"""
Write your rollback migrations here.
"""