Skip to content

Commit

Permalink
Merge pull request #147 from NIAEFEUP/feature/notifications-popup
Browse files Browse the repository at this point in the history
  • Loading branch information
ttoino authored Dec 2, 2023
2 parents 08de2e4 + d9132be commit b0a2e04
Show file tree
Hide file tree
Showing 2 changed files with 259 additions and 4 deletions.
131 changes: 128 additions & 3 deletions content-scripts/src/modules/layout.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { elementFromHtml } from "./utilities/elementFromHtml";
import { createPopover } from "./utilities/popover";
import { fetchSigarraPage } from "./utilities/pageUtils";

const HEADER_LINKS = {
Estudantes: {
Expand Down Expand Up @@ -40,13 +41,32 @@ const HEADER_LINKS = {
const authentication = (auth) =>
auth
? /*html*/ `
<a
href="gnots_geral.all_nots_list?pv_user=${auth.number}"
<button
id="se-auth-notifications-button"
class="se-button se-icon-button ${
auth.notifications ? "se-badge" : ""
}">
<span class="se-icon ri-notification-line"></span>
</a>
</button>
<div id="se-auth-notifications-menu">
<input type="radio" name="se-auth-notifications" id="se-auth-new-notifications-radio" checked>
<input type="radio" name="se-auth-notifications" id="se-auth-read-notifications-radio">
<div id="se-auth-notifications-header">
<label for="se-auth-new-notifications-radio">Novas</label>
<label for="se-auth-read-notifications-radio">Lidas</label>
</div>
<div id="se-auth-notifications-list">
<ol id="se-auth-new-notifications"></ol>
<ol id="se-auth-read-notifications"></ol>
<div id="se-auth-empty-notifications">
<span class="se-icon ri-notification-off-line"></span>
<span>Sem notificações</span>
</div>
<div class="se-loading-indicator">
<span class="se-icon ri-refresh-line"></span>
</div>
</div>
</div>
<button id="se-auth-profile-button">
<img
src="fotografias_service.foto?pct_cod=${auth.number}"
Expand Down Expand Up @@ -155,6 +175,96 @@ const createNewHeader = (auth) =>
</header>
`);

const loadNotifications = async () => {
const notificationsList = document.querySelector(
"#se-auth-notifications-list"
);

if (
notificationsList?.hasAttribute("data-se-loaded") ||
notificationsList?.classList.contains("se-loading")
)
return;

notificationsList.classList.add("se-loading");

const newNotifications = document.querySelector(
"#se-auth-new-notifications"
);
const readNotifications = document.querySelector(
"#se-auth-read-notifications"
);

const dateFormatter = new Intl.DateTimeFormat("pt-PT", {
dateStyle: "short",
});

await Promise.allSettled(
[
[newNotifications, "P"],
[readNotifications, "F"],
].map(async ([list, type]) => {
const r = await fetchSigarraPage(
`gnots_ajax.show_lista_notifs?pv_estado=${type}`
);

r.querySelectorAll("tr.d").forEach((x) => {
const date = x.querySelector("td:nth-child(3)")?.textContent;
const title = x.querySelector("td:nth-child(4)")?.textContent;
const answer = x.querySelector("td:nth-child(7) input");

const li = document.createElement("li");
li.classList.add("se-notification");

const time = document.createElement("time");
time.classList.add("se-notification-time");
time.dateTime = date;
time.textContent = dateFormatter.format(new Date(date));

const span = document.createElement("span");
span.classList.add("se-notification-title");
span.textContent = title;

li.append(span, time);

if (answer) {
const markAsRead = async (e) => {
e.preventDefault();
e.stopPropagation();

// Seems to always succeed
await fetchSigarraPage(
`gnots_geral.nots_list_sub?${answer.name}=${answer.value}`
);

e.target.remove();
readNotifications.insertBefore(
li,
readNotifications.firstChild
);
};

const button = document.createElement("button");
button.classList.add("se-notification-button");
button.type = "button";
button.addEventListener("click", markAsRead);

const icon = document.createElement("span");
icon.classList.add("se-icon", "ri-check-line");

button.append(icon);
li.append(button);
}

list.append(li);
});
})
);

notificationsList.classList.remove("se-loading");
notificationsList.setAttribute("data-se-loaded", "");
};

const replaceHeader = () => {
const oldHeader = document.querySelector("#cabecalho");

Expand All @@ -181,6 +291,21 @@ const replaceHeader = () => {
.querySelectorAll(":is(#se-auth-button, #se-auth-profile-button)")
.forEach((x) => x.addEventListener("click", openAuth));

const notificationsPopover = newHeader.querySelector(
"#se-auth-notifications-menu"
);
const notificationsButton = newHeader.querySelector(
"#se-auth-notifications-button"
);

if (notificationsPopover && notificationsButton) {
const openNotifications = createPopover(notificationsPopover);
notificationsButton.addEventListener("click", () => {
loadNotifications();
openNotifications();
});
}

newHeader.querySelectorAll(".se-header-link").forEach((x) => {
const popover = x.querySelector(".se-header-link-popover");
const togglePopover = createPopover(popover);
Expand Down
132 changes: 131 additions & 1 deletion css/simpler.css
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,33 @@ body:is(body) {
color: white !important;
}

.se-loading-indicator {
display: none;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 0.5em;
font-size: 1.25em;
font-weight: 500;
}

.se-loading .se-loading-indicator {
display: flex;
}

.se-loading-indicator > .se-icon {
animation: infinite linear rotate 1s;
}

@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}

#se-auth-federate {
background-color: #4a9cd4 !important;
color: white !important;
Expand Down Expand Up @@ -239,6 +266,7 @@ body:is(body) {

#se-auth-form,
#se-auth-profile-menu,
#se-auth-notifications-menu,
.se-header-link-popover {
display: none;
flex-direction: column;
Expand Down Expand Up @@ -276,6 +304,7 @@ body:is(body) {
}

.se-popover-open :is(#se-auth-form, #se-auth-profile-menu),
#se-auth-notifications-menu.se-popover-open,
.se-header-link-popover.se-popover-open,
.se-header-link:hover > .se-header-link-popover {
display: flex;
Expand All @@ -290,10 +319,11 @@ body:is(body) {
padding: 1em;
}

#se-auth-profile-menu {
#se-auth-profile-menu, #se-auth-notifications-menu {
top: 6em;
right: 1em;
width: 16em;
max-height: calc(100vh - 7em);
}

#se-auth-header {
Expand Down Expand Up @@ -380,6 +410,106 @@ body:is(body) {
object-fit: cover;
}

#se-auth-notifications-menu input {
display: none;
}

#se-auth-notifications-header {
display: flex;
flex-direction: row;
border-bottom: 1px solid #d1d1d1;
}

#se-auth-notifications-header label {
flex: 1;
padding: .5em;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}

#se-auth-notifications-header label:hover {
background-color: #f0f0f0;
}

#se-auth-new-notifications-radio:checked ~ #se-auth-notifications-header label[for="se-auth-new-notifications-radio"],
#se-auth-read-notifications-radio:checked ~ #se-auth-notifications-header label[for="se-auth-read-notifications-radio"] {
font-weight: bold;
}

#se-auth-notifications-list {
display: flex;
flex-direction: column;
overflow-y: auto;
}

.se-notification {
display: grid;
grid-template: "title button" auto
"time button" auto
/ 1fr auto;
align-items: center;
gap: .25em;
padding: .5em;
border-bottom: 1px solid #d1d1d1;
}

.se-notification:last-of-type {
border: none;
}

.se-notification-title {
grid-area: title;
font-weight: 500;
}

.se-notification-time {
grid-area: time;
font-size: .8em;
}

.se-notification-button {
grid-area: button;
background: none !important;
border: none !important;
}

#se-auth-new-notifications,
#se-auth-read-notifications {
display: none;
flex-direction: column;
padding: 0;
margin: 0;
}

#se-auth-new-notifications-radio:checked ~ #se-auth-notifications-list #se-auth-new-notifications:not(:empty),
#se-auth-read-notifications-radio:checked ~ #se-auth-notifications-list #se-auth-read-notifications:not(:empty) {
display: flex;
}

#se-auth-empty-notifications {
display: none;
}

#se-auth-empty-notifications,
#se-auth-notifications-list .se-loading-indicator {
flex-direction: column;
align-items: center;
justify-content: center;
gap: .5em;
padding: 1em;
}

#se-auth-new-notifications-radio:checked ~ #se-auth-notifications-list #se-auth-new-notifications:empty ~ #se-auth-empty-notifications,
#se-auth-read-notifications-radio:checked ~ #se-auth-notifications-list #se-auth-read-notifications:empty ~ #se-auth-empty-notifications {
display: flex;
}

.se-loading > :is(#se-auth-new-notifications, #se-auth-read-notifications, #se-auth-empty-notifications) {
display: none !important;
}

#conteudo {
grid-area: main;
overflow: auto;
Expand Down

0 comments on commit b0a2e04

Please sign in to comment.