Skip to content

Commit

Permalink
Merge pull request #23 from techx/Sabrina/unread_emails
Browse files Browse the repository at this point in the history
Sabrina/unread emails
  • Loading branch information
azliu0 committed May 8, 2024
2 parents 785adff + 7ed9418 commit 53fd5b6
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 21 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ After that, follow [this tutorial](https://www.youtube.com/watch?v=Uvf2FVS1F8k)
To start the server, run

```sh
python3 run.py
python3 wsgi.py
```

To start the client, in a different terminal, run
Expand Down
2 changes: 1 addition & 1 deletion client/.eslintrc.cjs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module.exports = {
root: true,
env: { browser: true, es2020: true },
env: { browser: true, es2022: true },
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
Expand Down
24 changes: 20 additions & 4 deletions client/src/routes/inbox.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
.grid {
height: 100vh;
}

.grid_inner {
height: 100%;
margin: 0;
Expand Down Expand Up @@ -61,12 +62,11 @@
}

.inboxText {
padding: var(--mantine-spacing-md);
font-size: 3vh;
font-weight: 500;
margin-bottom: 0;
font-weight: 600;
text-align: center;
height: 6vh;
border-bottom: 1px solid var(--mantine-color-gray-4);
height: 100%;
}

.subjectText {
Expand Down Expand Up @@ -110,6 +110,7 @@
justify-content: space-between;
padding-right: 10px;
}

.sourceHeader {
font-size: 1.5rem;
font-weight: 500;
Expand All @@ -119,6 +120,7 @@
border-top: 1px solid var(--mantine-color-gray-4);
border-bottom: 1px solid var(--mantine-color-gray-4);
}

.sourceText {
font-size: 1rem;
font-weight: 500;
Expand Down Expand Up @@ -154,3 +156,17 @@
margin-bottom: 0;
color: var(--mantine-color-red-6);
}

.inboxReadButton {
display: flex;
background-color: #e3e3e3;
padding: 4px 6px;
border-radius: var(--mantine-radius-md);
margin-right: var(--mantine-spacing-md);
}

.inboxHeader {
justify-content: space-between;
align-items: center;
border-bottom: 1px solid var(--mantine-color-gray-4);
}
90 changes: 80 additions & 10 deletions client/src/routes/inbox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import {
Accordion,
Center,
} from "@mantine/core";
import { useState, useEffect, useRef } from "react";
import { useState, useEffect, useRef, useCallback } from "react";
import classes from "./inbox.module.css";
import { RichTextEditor } from "@mantine/tiptap";
import { useEditor } from "@tiptap/react";
Expand All @@ -37,6 +37,7 @@ interface Thread {
id: number;
emailList: Email[];
resolved: boolean;
read: boolean;
}

interface Email {
Expand Down Expand Up @@ -73,6 +74,9 @@ export default function InboxPage() {
const [threads, setThreads] = useState<Array<Thread>>([]);
const [active, setActive] = useState(-1);
const [content, setContent] = useState("");
const [showAllMail, setShowAllMail] = useState(true);
const [showUnreadMail, setShowUnreadMail] = useState(false);

const activeThread = threads.filter((thread) => {
return thread.id === active;
})[0];
Expand Down Expand Up @@ -103,6 +107,24 @@ export default function InboxPage() {
},
[response],
);
const markAsRead = (threadId: number) => {
fetch(`/api/emails/mark_as_read/${threadId}`).then((res) => {
if (res.ok) {
getThreads();
}
});
};
const handleThreadClick = (threadId: number) => {
markAsRead(threadId);
};
const filteredThreads = threads.filter((thread) => {
if (showAllMail) {
return true;
} else if (showUnreadMail) {
return !thread.read;
}
});

const getThreads = () => {
fetch(`/api/emails/get_threads`)
.then((res) => res.json())
Expand Down Expand Up @@ -139,7 +161,7 @@ export default function InboxPage() {
return d.toLocaleString("en-US", { timeZone: "America/New_York" });
};
const parseBody = (body: string) => {
const lines = body.replace(new RegExp("\r?\n", "g"), "<br />");
const lines = body.replace(new RegExp("\\r?\\n", "g"), "<br />");
const linesArray = lines.split("<br />");
return (
<Text>
Expand All @@ -157,14 +179,14 @@ export default function InboxPage() {
);
};

const getResponse = () => {
const getResponse = useCallback(() => {
// Checks if response is already stored
const currEmailID =
activeThread.emailList[activeThread.emailList.length - 1].id;
if (storedResponses[currEmailID]) {
const oldResponse = storedResponses[currEmailID];
setResponse(oldResponse);
setContent(oldResponse.content.replace("\n", "<br/>"));
setContent(oldResponse.content.replaceAll("\n", "<br/>"));
return;
}

Expand All @@ -191,10 +213,11 @@ export default function InboxPage() {
setResponse(data);
setContent(data.content.replaceAll("\n", "<br/>"));
});
};
}, [activeThread, storedResponses]);

useEffect(() => {
if (activeThread && !activeThread.resolved) getResponse();
}, [active]);
}, [activeThread, getResponse]);

const sendEmail = () => {
const strippedContent = strip(content);
Expand Down Expand Up @@ -463,7 +486,7 @@ export default function InboxPage() {
setActive(Math.min(...actualThreads));
}
}
}, [active, threads]);
}, [active, threads, activeThread, getResponse, threadSize]);

const cmpDate: (a: string, b: string) => number = (a, b) => {
const d1 = new Date(a as string | number | Date);
Expand All @@ -489,7 +512,7 @@ export default function InboxPage() {
else return -1;
};

const threadList = threads.sort(sortThreads).map((thread) => {
const threadList = filteredThreads.sort(sortThreads).map((thread) => {
if (thread.emailList.length === 0) return <></>;
const sender =
thread.emailList[thread.emailList.length - 1].sender.indexOf("<") !== -1
Expand All @@ -511,6 +534,7 @@ export default function InboxPage() {
})[0].emailList.length,
);
}
handleThreadClick(thread.id);
}}
>
<Box
Expand All @@ -522,7 +546,19 @@ export default function InboxPage() {
(!thread.resolved ? classes.unresolved : "")
}
>
<Title size="md">{sender}</Title>
<Title size="md">
<span>{sender}</span>

{!thread.read && (
<ThemeIcon
size={10}
color="indigo"
radius="xl"
style={{ marginLeft: "5px" }}
></ThemeIcon>
)}
</Title>

<Flex className={classes.between}>
<Text>{thread.emailList[thread.emailList.length - 1].subject}</Text>
<Text>
Expand Down Expand Up @@ -607,7 +643,41 @@ export default function InboxPage() {
>
{!sourceActive && (
<Grid.Col span={30} className={classes.threads}>
<Text className={classes.inboxText}>Inbox</Text>
<Flex className={classes.inboxHeader}>
<Box>
<Text className={classes.inboxText}>Inbox</Text>
</Box>
<Box className={classes.inboxReadButton}>
<Button
style={{
backgroundColor: showUnreadMail ? "#E3E3E3" : "white",
color: showUnreadMail ? "#787878" : "black",
borderRadius: "var(--mantine-radius-md)",
padding: "4px 12px",
}}
onClick={() => {
setShowAllMail(true);
setShowUnreadMail(false);
}}
>
All Mail
</Button>
<Button
style={{
backgroundColor: showAllMail ? "#E3E3E3" : "white",
color: showAllMail ? "#787878" : "black",
borderRadius: "var(--mantine-radius-md)",
padding: "4px 12px",
}}
onClick={() => {
setShowAllMail(false);
setShowUnreadMail(true);
}}
>
Unread
</Button>
</Box>
</Flex>
<Stack gap={0} className={classes.threadList}>
{threadList}
</Stack>
Expand Down
12 changes: 7 additions & 5 deletions client/tsconfig.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,27 @@
{
"compilerOptions": {
"target": "ES2020",
"target": "es2022",
"useDefineForClassFields": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"lib": ["es2022", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,

/* Bundler mode */
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",

/* Linting */
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
"references": [
{
"path": "./tsconfig.node.json"
}
]
}
16 changes: 16 additions & 0 deletions server/controllers/emails.py
Original file line number Diff line number Diff line change
Expand Up @@ -557,6 +557,7 @@ def get_threads():
{
"id": thread.id,
"resolved": thread.resolved,
"read": thread.read,
"emailList": [
thread_email.map()
for thread_email in db.session.execute(
Expand All @@ -569,3 +570,18 @@ def get_threads():
for thread in thread_list
]
return email_list


@emails.route("/mark_as_read/<int:thread_id>", methods=["GET"])
def mark_as_read(thread_id):
"""GET /mark_as_read/<threadId>
Mark a thread as read.
"""
thread = db.session.execute(select(Thread).where(Thread.id == thread_id)).scalar()
if not thread:
return {"message": "Thread not found"}, 400
thread.read = True
db.session.commit()

return {"message": "Successfully updated"}, 200
2 changes: 2 additions & 0 deletions server/models/thread.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ class Thread(db.Model):
resolved (bool): Whether the thread is resolved.
last_email (int): The ID of the last email in the thread.
emails (list): The emails in the thread.
read (bool): Whether the thread is read.
"""

__tablename__ = "thread"

id: Mapped[int] = mapped_column(primary_key=True, init=False, autoincrement=True)
last_email: Mapped[Optional[int]] = mapped_column(nullable=True, init=False)
resolved: Mapped[bool] = mapped_column(nullable=False, default=False, init=False)
read: Mapped[bool] = mapped_column(nullable=False, default=False, init=False)

emails: Mapped[List["Email"]] = relationship(
"Email",
Expand Down

0 comments on commit 53fd5b6

Please sign in to comment.