Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
43 commits
Select commit Hold shift + click to select a range
42ce89b
Merge pull request #113 from EmailEssence/main
S-Shahla May 20, 2025
1021990
Initialize Localized Filtering Function
S-Shahla May 20, 2025
5a3c896
Implement Localized Email Filter Function
S-Shahla May 25, 2025
c781a04
Make Search Work On Key Press
S-Shahla May 25, 2025
fee1f23
Move Inline Styles From Inbox To CSS
S-Shahla May 25, 2025
8b610f3
Fix InboxSearchBar Styles & Constraints
S-Shahla May 25, 2025
ca8dba2
Refactor Loading Page
S-Shahla May 28, 2025
e34c905
Fix Client Routes
S-Shahla Jun 2, 2025
e9b467c
Create A Second Loading Phase
S-Shahla Jun 3, 2025
38e8b94
Add Auth Loading Page To Auth Flow
S-Shahla Jun 3, 2025
49a8b37
Add Email Storage To Client
S-Shahla Jun 3, 2025
364508b
Complete Initial Email Loading Function Flow
S-Shahla Jun 3, 2025
b6c9f6e
Finalize Initial Email Retreival Flow
S-Shahla Jun 3, 2025
d138f51
Add Set Current Email On Initialization
S-Shahla Jun 3, 2025
93cd724
Add Function For Expanding Current Emails
S-Shahla Jun 3, 2025
e2b9603
Modify Trimming Function To Work On Parameter
S-Shahla Jun 3, 2025
9d1957d
Add Dynamic EmailsPerPage
S-Shahla Jun 3, 2025
09aea67
Implement Refresh And Start Args For Email Fetch
S-Shahla Jun 3, 2025
e48d1bf
Update New Email Fetch Functions To Work With Flow
S-Shahla Jun 3, 2025
74aa604
Connect Pages & Fetching In Dashboard To Client
S-Shahla Jun 3, 2025
74430cc
Add Summary Fetching & Setting
S-Shahla Jun 3, 2025
12419fc
Extend Summary Fetching To Weighted Email List
S-Shahla Jun 3, 2025
e26e3fd
Remove Deprecated Email Storage In Router & Inbox
S-Shahla Jun 3, 2025
8adfb77
Summaries Function Initialized & Linked To WEList
S-Shahla Jun 10, 2025
ba54c64
Fix Subsequent Email Retreival
S-Shahla Jun 11, 2025
ee4aaa0
Fix Email Loading On MiniView & WEList
S-Shahla Jun 13, 2025
37df53b
Move HasUnloadedEmails To Client
S-Shahla Jun 13, 2025
8cbf141
Fix Summaries Call
S-Shahla Jun 13, 2025
33f3bfd
Fix WEList Email Loading Visuals
S-Shahla Jun 13, 2025
87d0ce5
Link Summaries & Email Fetching To Inbox
S-Shahla Jun 13, 2025
8d80c47
Commit
S-Shahla Jun 14, 2025
2cc540a
Summaries Fetch Syncronus
S-Shahla Jun 14, 2025
70f8469
Merge remote-tracking branch 'origin/main' into InboxSearchBar
mads-jm Jun 14, 2025
5d843c3
Add JSDoc comments for email handling functions
mads-jm Jun 14, 2025
c196936
Fix Batch Summary Fetch
S-Shahla Jun 15, 2025
2e0d4a7
Fix Search In Inbox
S-Shahla Jun 16, 2025
571ecb0
Fix Emails In Inbox Taking Up Full Space
S-Shahla Jun 16, 2025
1ce18f8
Merge branch 'InboxSearchBar-BatchFetch' into NEUDemo
S-Shahla Jun 17, 2025
0928c94
Merge pull request #134 from EmailEssence/NEUDemo
S-Shahla Jun 17, 2025
d91dc7c
Merge branch 'InboxSearchBar' of github.com:EmailEssence/EmailEssence…
S-Shahla Jun 17, 2025
c997dbf
Integrate Rest Of Merging Issues
S-Shahla Jun 17, 2025
2d59845
Fix Email New Email Retreival
S-Shahla Jun 17, 2025
fc1ccb4
Skip old tests
S-Shahla Jun 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 6 additions & 20 deletions frontend/src/authentication/authenticate.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { baseUrl, retrieveUserData } from "../emails/emailHandler";
import { baseUrl } from "../emails/emailHandler";

/**
* Initiates the OAuth authentication flow by redirecting to the backend login endpoint.
Expand Down Expand Up @@ -29,37 +29,23 @@ export const handleOAuthCallback = async () => {
if (authState.authenticated && authState.token) {
const isAuthenticated = checkAuthStatus(authState.token);
if (isAuthenticated) {
await handleAuthenticate(authState.token);
localStorage.setItem("auth_token", authState.token);
} else {
handleAuthError("Unable to authenticate");
return false;
}
}
return;
} catch (error) {
window.location.hash = "";
console.error("Error parsing auth state:", error);
handleAuthError(error);
return false;
}
return true;
}
return false;
};

/**
* Stores the authentication token and retrieves user data.
* Navigates to the error page if authentication fails.
* @async
* @param {string} token - The authentication token.
* @returns {Promise<void>}
*/
export const handleAuthenticate = async (token) => {
try {
localStorage.setItem("auth_token", token);
await retrieveUserData();
} catch (error) {
handleAuthError(error);
}
};


/**
* Handles authentication errors by logging out, storing the error message,
* and redirecting to the error page.
Expand Down
214 changes: 146 additions & 68 deletions frontend/src/components/client/client.jsx
Original file line number Diff line number Diff line change
@@ -1,38 +1,31 @@
import PropTypes from "prop-types";
import { useEffect, useReducer } from "react";
import { useEffect, useReducer, useState } from "react";
import { Outlet, Route, Routes, useNavigate } from "react-router";
import { fetchNewEmails } from "../../emails/emailHandler";
import "../client/dashboard/client.css";
import fetchEmails, {
handleNewEmails,
setSummary,
} from "../../emails/emailHandler";
import "./client.css";
import Dashboard from "./dashboard/dashboard";
import Inbox from "./inbox/inbox";
import { clientReducer, userPreferencesReducer } from "./reducers";
import { Settings } from "./settings/settings";
import SideBar from "./sidebar/sidebar";
import Loading from "../login/Loading";

/**
* Main client component for authenticated user experience.
* Handles sidebar, routing, user preferences, and periodic email fetching.
* @param {Object} props
* @param {Array<Email>} props.emailsByDate - List of emails grouped by date.
* @param {Object} props.defaultUserPreferences - Default user preferences.
* @returns {JSX.Element}
*/
function Client({
emailsByDate,
defaultUserPreferences = {
isChecked: true,
emailFetchInterval: 120,
theme: "light",
},
}) {
function Client() {
const navigate = useNavigate();
const [emailsPerPage, setEmailsPerPage] = useState(
Math.max(1, Math.floor(window.innerHeight / 35))
);
const [hasUnloadedEmails, setHasUnloadedEmails] = useState(true);
const [client, dispatchClient] = useReducer(clientReducer, {
expandedSideBar: false,
curEmail: emailsByDate[0],
emails: [],
curEmail: {},
});
const [userPreferences, dispatchUserPreferences] = useReducer(
userPreferencesReducer,
defaultUserPreferences
{ isChecked: true, emailFetchInterval: 120, theme: "light" }
);

/**
Expand All @@ -42,15 +35,35 @@ function Client({
useEffect(() => {
const clock = setInterval(async () => {
try {
await fetchNewEmails();
const requestedEmails = await fetchEmails(client.emails.length, true);
const newEmails = handleNewEmails(client.emails, requestedEmails);
if (newEmails.length > 0) handleAddEmails(newEmails, true);
console.log(newEmails.length);
} catch (error) {
console.error(`Loading Emails Error: ${error}`);
}
}, userPreferences.emailFetchInterval * 1000);
return () => clearInterval(clock);
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [userPreferences.emailFetchInterval]);

// Dynamically update sidebar width
useEffect(() => {
function updateEmailsPerPage() {
setEmailsPerPage(Math.max(1, Math.floor(window.innerHeight / 35)));
}

let resizeTimeout = null;
function handleResize() {
if (resizeTimeout) clearTimeout(resizeTimeout);
resizeTimeout = setTimeout(updateEmailsPerPage, 50);
}

window.addEventListener("resize", handleResize);
return () => {
window.removeEventListener("resize", handleResize);
if (resizeTimeout) clearTimeout(resizeTimeout);
};
}, []);
const root = document.querySelector(":root");
root.style.setProperty(
"--sidebar-width",
Expand Down Expand Up @@ -107,69 +120,134 @@ function Client({
theme: theme,
});
};
const handleAddEmails = (emailsToAdd, addToFront = false) => {
if (addToFront) {
// add emails to the Front
dispatchClient({
type: "emailAdd",
email: [...emailsToAdd, ...client.emails],
});
} else {
// add emails to the back
dispatchClient({
type: "emailAdd",
email: [...client.emails, ...emailsToAdd],
});
}
};

const handleSetEmails = async (emails) => {
dispatchClient({
type: "emailAdd",
email: emails,
});
};

/**
* Sets the currently selected email.
* @param {Email} email - The email object to set as current.
*/
const handleSetCurEmail = (email) => {
// requests a page worth of emails and adds to the current email list,
// returns whether more emails exist or not
const requestMoreEmails = async () => {
const newEmails = await fetchEmails(emailsPerPage, client.emails.length);
if (newEmails.length > 0) {
handleAddEmails(newEmails);
} else {
setHasUnloadedEmails(false);
}
};

const handleSetCurEmail = async (email) => {
dispatchClient({
type: "emailChange",
email: email,
});
};

const handleRequestSummaries = async (emails) => {
const ids = emails.map((email) => {
return email.email_id;
});
const result = await setSummary(ids, client.emails);
const settledEmails = await Promise.allSettled(result);
const toSet = settledEmails
.filter((r) => r.status === "fulfilled")
.map((r) => r.value || r.result);
dispatchClient({
type: "emailAdd",
email: toSet,
});
};

return (
<div className="client">
<SideBar
onLogoClick={handleLogoClick}
expanded={client.expandedSideBar}
handlePageChange={handlePageChange}
/>
<>
<Routes>
<Route
path="home"
path="loading"
element={
<Dashboard
emailList={emailsByDate}
handlePageChange={handlePageChange}
setCurEmail={handleSetCurEmail}
<Loading
emailsPerPage={emailsPerPage}
setInitialEmails={handleSetEmails}
setInitialEmail={handleSetCurEmail}
/>
}
/>
<Route
path="inbox"
path="*"
element={
<Inbox
displaySummaries={userPreferences.isChecked}
emailList={emailsByDate}
setCurEmail={handleSetCurEmail}
curEmail={client.curEmail}
/>
}
/>
<Route
path="settings"
element={
<Settings
isChecked={userPreferences.isChecked}
handleToggleSummariesInInbox={handleToggleSummariesInInbox}
emailFetchInterval={userPreferences.emailFetchInterval}
handleSetEmailFetchInterval={handleSetEmailFetchInterval}
theme={userPreferences.theme}
handleSetTheme={handleSetTheme}
/>
<div className="client">
<SideBar
onLogoClick={handleLogoClick}
expanded={client.expandedSideBar}
handlePageChange={handlePageChange}
/>
<Outlet />
</div>
}
/>
>
<Route
path="inbox"
element={
<Inbox
displaySummaries={userPreferences.isChecked}
emailList={client.emails}
setCurEmail={handleSetCurEmail}
curEmail={client.curEmail}
requestMoreEmails={requestMoreEmails}
requestSummaries={handleRequestSummaries}
hasUnloadedEmails={hasUnloadedEmails}
emailsPerPage={emailsPerPage}
/>
}
/>
<Route
path="settings"
element={
<Settings
isChecked={userPreferences.isChecked}
handleToggleSummariesInInbox={handleToggleSummariesInInbox}
emailFetchInterval={userPreferences.emailFetchInterval}
handleSetEmailFetchInterval={handleSetEmailFetchInterval}
theme={userPreferences.theme}
handleSetTheme={handleSetTheme}
/>
}
/>
<Route
path="home"
element={
<Dashboard
emailList={client.emails}
handlePageChange={handlePageChange}
setCurEmail={handleSetCurEmail}
requestMoreEmails={requestMoreEmails}
emailsPerPage={emailsPerPage}
requestSummaries={handleRequestSummaries}
hasUnloadedEmails={hasUnloadedEmails}
/>
}
/>
</Route>
</Routes>
<Outlet />
</div>
</>
);
}

Client.propTypes = {
emailsByDate: PropTypes.array,
defaultUserPreferences: PropTypes.object,
};

export default Client;
4 changes: 4 additions & 0 deletions frontend/src/components/client/dashboard/dashboard.css
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,10 @@
gap: 1rem;
}

.dashboard .welist-email-container.solo {
grid-template-columns: 1fr;
}

.dashboard .email-link {
color: var(--icon-color);
border-radius: 0.2rem;
Expand Down
Loading