Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
…te-Plus into working
  • Loading branch information
saumon-brule committed Sep 15, 2024
2 parents 6a625d6 + f8c8688 commit 88b68aa
Show file tree
Hide file tree
Showing 14 changed files with 202 additions and 32 deletions.
4 changes: 4 additions & 0 deletions public/images/mail-notification-svgrepo-com.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
48 changes: 46 additions & 2 deletions src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ function consoleLogEDPLogo() {
consoleLogEDPLogo();

const currentEDPVersion = "0.3.1";
const apiVersion = "4.60.4";
const apiVersion = "4.60.5";

// secret webhooks
const carpeConviviale = "CARPE_CONVIVIALE_WEBHOOK_URL";
Expand Down Expand Up @@ -1772,6 +1772,50 @@ export default function App() {
})
}

async function fetchMessageMarkAsUnread(ids=[], controller) {
if (ids.length < 1) {
return;
}
abortControllers.current.push(controller);
const userId = activeAccount;
const data = {
anneeMessages: getUserSettingValue("isSchoolYearEnabled") ? getUserSettingValue("schoolYear").join("-") : getCurrentSchoolYear().join("-"),
action: "marquerCommeNonLu",
ids: ids
}
fetch(
getProxiedURL(`https://api.ecoledirecte.com/v3/${accountsListState[userId].accountType === "E" ? "eleves/" + accountsListState[userId].id : "familles/" + accountsListState[userId].familyId}/messages.awp?verbe=put&v=${apiVersion}`, true),
{
method: "POST",
headers: {
"x-token": tokenState
},
body: `data=${JSON.stringify(data)}`,
signal: controller.signal,
referrerPolicy: "no-referrer",
},
)
.then((response) => response.json())
.then((response) => {
let code;
if (accountsListState[activeAccount].firstName === "Guest") {
code = 49969;
} else {
code = response.code;
}
if (code === 200) {
// message successfully marked as unread
} else if (code === 520 || code === 525) {
// token invalide
requireLogin();
}
setTokenState((old) => (response?.token || old));
})
.finally(() => {
abortControllers.current.splice(abortControllers.current.indexOf(controller), 1);
})
}


async function fetchSchoolLife(controller = (new AbortController())) {
abortControllers.current.push(controller);
Expand Down Expand Up @@ -2175,7 +2219,7 @@ export default function App() {
path: "messaging"
},
{
element: <Messaging isLoggedIn={isLoggedIn} activeAccount={activeAccount} fetchMessages={fetchMessages} fetchMessageContent={fetchMessageContent} />,
element: <Messaging isLoggedIn={isLoggedIn} activeAccount={activeAccount} fetchMessages={fetchMessages} fetchMessageContent={fetchMessageContent} fetchMessageMarkAsUnread={fetchMessageMarkAsUnread} />,
path: ":userId/messaging"
},
],
Expand Down
4 changes: 2 additions & 2 deletions src/components/app/Header/Header.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export default function Header({ currentEDPVersion, token, accountsList, setActi
link: `/app/${activeAccount}/homeworks`,
icon: <HomeworksIconOfficial />,
notifications: notifications?.homeworks || 0,
isNew: true
isNew: false
},
{
enabled: accountsList[activeAccount]?.modules?.filter((item) => item.code === "EDT").map((item) => item.enable).includes(true) ?? true,
Expand All @@ -189,7 +189,7 @@ export default function Header({ currentEDPVersion, token, accountsList, setActi
link: `/app/${activeAccount}/messaging`,
icon: <MessagingIcon />,
notifications: notifications?.messaging || 0,
isNew: false
isNew: true
}
]
// Behavior
Expand Down
16 changes: 10 additions & 6 deletions src/components/app/Header/HeaderNavigationButton.css
Original file line number Diff line number Diff line change
Expand Up @@ -69,26 +69,30 @@
}

.header-button .notifications.new {
background-color: rgb(var(--background-color-1));
background-color: black;
color: gold;
width: 52px;
color: rgb(var(--text-color-main));
animation: new-feature-notification-animation .8s ease alternate forwards;
animation-iteration-count: 20;
/* animation: new-feature-notification-animation .8s ease alternate forwards;
animation-iteration-count: 20; */
left: 40px;
}
.light .header-button .notifications.new {
background-color: gold;
color: black;
}

.light .header-button .notifications.new {
box-shadow: 0 0 20px rgba(0, 0, 0, .2);
}

@keyframes new-feature-notification-animation {
/* @keyframes new-feature-notification-animation {
from {
scale: .9;
}
to {
scale: 1;
}
}
} */

@keyframes notification-pop-in {
from {
Expand Down
39 changes: 39 additions & 0 deletions src/components/app/Messaging/Inbox.css
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@

#inbox {
height: 100%;
display: flex;
flex-flow: column nowrap;
}

#inbox .inbox-search-input {
Expand Down Expand Up @@ -73,12 +75,49 @@
flex-flow: row nowrap;
justify-content: space-between;
width: 100%;
gap: 5px;
}

#inbox .message-container .message-subject .author-name {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
flex-shrink: 1;
}
#inbox .message-container .message-subject .actions {
display: flex;
flex-flow: row nowrap;
gap: 12px;
}

#inbox .message-container .attachment-icon {
height: 16px;
transform: scale(1.3);
}

#inbox .message-container .mark-as-unread {
display: none;
border: none;
background-color: transparent;
transition: .1s;
cursor: pointer;
outline: none;
}
#inbox .message-container .mark-as-unread:is(:hover, :focus-visible) .mark-as-unread-icon path {
stroke: rgb(var(--text-color-alt));
}
#inbox .message-container .mark-as-unread .mark-as-unread-icon {
height: 16px;
transform: scale(1.35) translateY(-1px);
}
#inbox .message-container:is(:hover, :focus-visible, :focus-within, .selected) .mark-as-unread {
display: block;
}
#inbox .message-container[data-read=false] .mark-as-unread {
opacity: .2;
pointer-events: none;
}

:fullscreen #inbox ul {
display: flex;
flex-flow: row wrap;
Expand Down
43 changes: 33 additions & 10 deletions src/components/app/Messaging/Inbox.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,44 @@ import ScrollShadedDiv from "../../generic/CustomDivs/ScrollShadedDiv";
import TextInput from "../../generic/UserInputs/TextInput";
import { removeAccents } from "../../../utils/utils";
import AttachmentIcon from "../../graphics/AttachmentIcon";
import MarkAsUnread from "../../graphics/MarkAsUnread";


export default function Inbox({ isLoggedIn, activeAccount, selectedMessage, setSelectedMessage }) {
export default function Inbox({ selectedMessage, setSelectedMessage, fetchMessageMarkAsUnread }) {
// States
const { useUserData } = useContext(AppContext);
const [search, setSearch] = useState("");
const messages = useUserData("sortedMessages").get();
console.log("Inbox ~ messages:", messages)
const messages = useUserData("sortedMessages");

// behavior
// TODO: handle keyboard navigation
const handleClick = (message) => {
setSelectedMessage(message.id);
}

const handleKeyDown = (event, msg) => {
if (event.key === "Enter" || event.key === " ") {
handleClick(msg);
}
}

const handleMarkAsUnread = (event, msg) => {
event.preventDefault();
event.stopPropagation();
const controller = new AbortController();
fetchMessageMarkAsUnread([msg.id], controller);

if (msg.id === selectedMessage) {
setSelectedMessage(null);
}

// mark as unread locally and kick the content so as to trigger a refetch the next reading (as the "mark as read" feature is trigger when fetching the message)
const oldMsg = messages.get();
const msgIdx = oldMsg.findIndex((item) => item.id === msg.id);
oldMsg[msgIdx].read = false;
oldMsg[msgIdx].content = null;
messages.set(oldMsg);

}

const handleChange = (event) => {
setSearch(event.target.value)
Expand All @@ -32,7 +56,6 @@ export default function Inbox({ isLoggedIn, activeAccount, selectedMessage, setS
regexp = new RegExp(removeAccents(search.toLowerCase()));
} catch {return -1}
const filterBy = [message.subject, message.from.name, message.content?.content, message.files?.map((file) => file.name)].flat();
console.log("filterResearch ~ filterBy:", filterBy)
for (let filter of filterBy) {
if (filter) {
filter = removeAccents(filter.toLowerCase());
Expand All @@ -47,13 +70,13 @@ export default function Inbox({ isLoggedIn, activeAccount, selectedMessage, setS
// JSX
return (
<div id="inbox">
{messages !== undefined
? (messages.length > 0
<TextInput onChange={handleChange} value={search} textType={"text"} placeholder={"Rechercher"} className="inbox-search-input" />
{messages.get() !== undefined
? (messages.get().length > 0
? <ScrollShadedDiv className="messages-container">
<TextInput onChange={handleChange} value={search} textType={"text"} placeholder={"Rechercher"} className="inbox-search-input" />
<ul>
{messages.filter(filterResearch).map((message) => <li className={"message-container" + (selectedMessage === message.id ? " selected" : "")} data-read={message.read} onClick={() => handleClick(message)} key={message.id} role="button" tabIndex={0}>
<h4 className="message-subject">{message.from.name} {message.files?.length > 0 && <AttachmentIcon className="attachment-icon" />}</h4>
{messages.get().filter(filterResearch).map((message) => <li className={"message-container" + (selectedMessage === message.id ? " selected" : "")} data-read={message.read} onClick={() => handleClick(message)} onKeyDown={(event) => handleKeyDown(event, message)} key={message.id} role="button" tabIndex={0}>
<h4 className="message-subject"><span className="author-name">{message.from.name}</span> <span className="actions"><button disabled={!message.read} onClick={(event) => handleMarkAsUnread(event, message)} className="mark-as-unread" title="Marquer comme non lu"><MarkAsUnread className="mark-as-unread-icon"/></button> {message.files?.length > 0 && <AttachmentIcon className="attachment-icon" />}</span></h4>
<p className="message-author">{message.subject}</p>
<p className="message-date">{(new Date(message.date)).toLocaleDateString("fr-FR", {
month: "long",
Expand Down
7 changes: 7 additions & 0 deletions src/components/app/Messaging/MessageReader.css
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,18 @@
padding: 20px;
overflow: auto;
}

/* We take some liberties to make the emails more readable overall */
#message-reader .message-content > div {
max-width: 800px;
margin: 0 auto;
}

#message-reader .message-content > div > p:has(> img) {
text-align: center;
}
/* end */

#message-reader .email-footer {
height: 100px;
overflow: auto;
Expand Down
3 changes: 1 addition & 2 deletions src/components/app/Messaging/MessageReader.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ export default function MessageReader({ selectedMessage }) {
const { useUserData, actualDisplayTheme } = useContext(AppContext);
const messages = useUserData("sortedMessages").get();
const message = messages ? messages.find((item) => item.id === selectedMessage) : null;
console.log("MessageReader ~ message:", message)

// behavior

Expand Down Expand Up @@ -45,7 +44,7 @@ export default function MessageReader({ selectedMessage }) {
<hr />
<div className="email-footer">
<ul className="attachments-container">
{message.files.map((file) => <li><button className="attachment" onClick={() => file.download()}><DownloadIcon className="download-icon" />{file.name + "." + file.extension}</button></li>)}
{message.files.map((file) => <li key={file.id}><button className="attachment" onClick={() => file.download()}><DownloadIcon className="download-icon" />{file.name + "." + file.extension}</button></li>)}
</ul>
</div>
</>
Expand Down
47 changes: 41 additions & 6 deletions src/components/app/Messaging/Messaging.jsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@

import { useState, useEffect, useContext } from "react";
import { useState, useRef, useEffect, useContext } from "react";
import { useNavigate, useLocation, Navigate, Link } from "react-router-dom";

import {
WindowsContainer,
WindowsLayout,
Expand All @@ -15,11 +17,16 @@ import Inbox from "./Inbox";
import MessageReader from "./MessageReader";


export default function Messaging({ isLoggedIn, activeAccount, fetchMessages, fetchMessageContent }) {
export default function Messaging({ isLoggedIn, activeAccount, fetchMessages, fetchMessageContent, fetchMessageMarkAsUnread }) {
// States
const navigate = useNavigate();
const location = useLocation();

const { useUserData } = useContext(AppContext);
const [selectedMessage, setSelectedMessage] = useState(null);
const [selectedMessage, setSelectedMessage] = useState(isNaN(parseInt(location.hash.slice(1))) ? null : parseInt(location.hash.slice(1)));
const oldSelectedMessage = useRef(selectedMessage);
const messages = useUserData("sortedMessages");


// behavior
useEffect(() => {
Expand All @@ -30,7 +37,6 @@ export default function Messaging({ isLoggedIn, activeAccount, fetchMessages, fe
const controller = new AbortController();
if (isLoggedIn) {
if (messages.get() === undefined) {
console.log("fetching messages");
fetchMessages(controller);
setSelectedMessage(null);
}
Expand All @@ -43,14 +49,43 @@ export default function Messaging({ isLoggedIn, activeAccount, fetchMessages, fe

useEffect(() => {
const controller = new AbortController();
console.log("useEffect ~ selectedMessage:", selectedMessage)
if (selectedMessage !== null) {
fetchMessageContent(selectedMessage, controller);
const parsedHash = parseInt(location.hash.slice(1));
if (parsedHash !== selectedMessage) {
const newHash = "#" + selectedMessage;
navigate(newHash);
}
} else {
if (location.hash) {
navigate("#");
}
}

return () => {
controller.abort();
}
}, [location, selectedMessage]);

useEffect(() => {
if (oldSelectedMessage.current !== selectedMessage) {
return;
}
const parsedHash = parseInt(location.hash.slice(1));
if (!isNaN(parsedHash) && parsedHash !== selectedMessage) {
if (messages.get()) {
const doesMessageExist = messages.get()?.findIndex((item) => item.id === parsedHash) !== -1;
if (doesMessageExist) {
setSelectedMessage(parsedHash);
} else {
navigate("#");
}
}
}
}, [location, messages.get(), oldSelectedMessage.current, selectedMessage]);

useEffect(() => {
oldSelectedMessage.current = selectedMessage;
}, [selectedMessage]);

// JSX
Expand All @@ -63,7 +98,7 @@ export default function Messaging({ isLoggedIn, activeAccount, fetchMessages, fe
<h2>Boîte de réception</h2>
</WindowHeader>
<WindowContent>
<Inbox isLoggedIn={isLoggedIn} activeAccount={activeAccount} selectedMessage={selectedMessage} setSelectedMessage={setSelectedMessage} />
<Inbox isLoggedIn={isLoggedIn} activeAccount={activeAccount} selectedMessage={selectedMessage} setSelectedMessage={setSelectedMessage} fetchMessageMarkAsUnread={fetchMessageMarkAsUnread} />
</WindowContent>
</Window>
<Window growthFactor={3} className="message-content" allowFullscreen={true}>
Expand Down
Loading

0 comments on commit 88b68aa

Please sign in to comment.