diff --git a/client/src/app/postSlice.ts b/client/src/app/postSlice.ts
index b3cb600..30ff9c4 100644
--- a/client/src/app/postSlice.ts
+++ b/client/src/app/postSlice.ts
@@ -1,15 +1,15 @@
import { Store, createSlice } from "@reduxjs/toolkit";
import { Configuration, Post, PostsApi } from "../api";
import { Status } from "../util/types";
-import { store } from "./store";
+import { AppThunk, store } from "./store";
-interface postSlice {
+interface postSliceInterface {
status: Status;
followedPosts: Post[];
globalPosts: Post[];
}
-const initialState: postSlice = {
+const initialState: postSliceInterface = {
status: Status.idle,
followedPosts: [],
globalPosts: [],
@@ -31,7 +31,7 @@ export const postSlice = createSlice({
},
});
-export const fetchFollowedPosts = () => async (dispatch: any) => {
+export const fetchFollowedPosts = (): AppThunk => async (dispatch: any) => {
const postApi = createApi(store);
dispatch(setStatus(Status.loading));
@@ -40,7 +40,7 @@ export const fetchFollowedPosts = () => async (dispatch: any) => {
dispatch(setStatus(Status.idle));
};
-export const fetchGlobalPosts = () => async (dispatch: any) => {
+export const fetchGlobalPosts = (): AppThunk => async (dispatch: any) => {
const postApi = createApi(store);
dispatch(setStatus(Status.loading));
@@ -49,6 +49,26 @@ export const fetchGlobalPosts = () => async (dispatch: any) => {
dispatch(setStatus(Status.idle));
};
+export const likePost =
+ (postId: number): AppThunk =>
+ async (dispatch: any) => {
+ const postApi = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ await postApi.likePost(postId);
+ dispatch(setStatus(Status.idle));
+ };
+
+export const unLikePost =
+ (postId: number): AppThunk =>
+ async (dispatch: any) => {
+ const postApi = createApi(store);
+
+ dispatch(setStatus(Status.loading));
+ await postApi.unlikePost(postId);
+ dispatch(setStatus(Status.idle));
+ };
+
export const { setFollowedPosts, setGlobalPosts, setStatus } =
postSlice.actions;
diff --git a/client/src/components/notificationBell.tsx b/client/src/components/notificationBell.tsx
index 97e17e9..064cfb6 100644
--- a/client/src/components/notificationBell.tsx
+++ b/client/src/components/notificationBell.tsx
@@ -4,7 +4,7 @@ import { Badge, Tooltip } from "@mui/material";
export default function NotificationBell() {
return (
-
+
diff --git a/client/src/components/postListItem.tsx b/client/src/components/postListItem.tsx
index 1e77f2c..dee8d3c 100644
--- a/client/src/components/postListItem.tsx
+++ b/client/src/components/postListItem.tsx
@@ -1,19 +1,136 @@
-import { ListItem, ListItemText } from "@mui/material";
+import {
+ Box,
+ Button,
+ Card,
+ CardActionArea,
+ CardContent,
+ Grid,
+ ListItem,
+ styled,
+ Typography,
+} from "@mui/material";
import { Post } from "../api";
+import MessageIcon from "@mui/icons-material/Message";
+import ThumbUpOffAltIcon from "@mui/icons-material/ThumbUpOffAlt";
+import ThumbUpAltIcon from "@mui/icons-material/ThumbUpAlt";
+import { useNavigate } from "react-router-dom";
+import { likePost, unLikePost } from "../app/postSlice";
+import { useAppDispatch } from "../app/store";
+import { useEffect, useState } from "react";
+import { useSelector } from "react-redux";
+import { selectUserInfo } from "../app/loginSlice";
interface PostListItemProps {
post: Post;
+ postType: "all" | "user";
}
export default function PostListItem(props: PostListItemProps) {
- console.log(props.post);
+ const dispatch = useAppDispatch();
+ const navigate = useNavigate();
+ const userInfo = useSelector(selectUserInfo);
+ const [liked, setLiked] = useState(false);
+ const [numberOfLikes, setNumberOfLikes] = useState(0);
+
+ // On component mount, set the number of likes
+ // and whether the user has liked the post
+ useEffect(() => {
+ setNumberOfLikes(props.post.likedBy?.length || 0);
+ setLiked(props.post.likedBy?.includes(userInfo) || false);
+ }, []);
+
+ const handleLike = () => {
+ if (!liked) {
+ // Instant feedback to the user
+ setLiked(true);
+ setNumberOfLikes(numberOfLikes + 1);
+ // Dispatch the call to the API
+ dispatch(likePost(props.post.id as number));
+ }
+ // If the user has already liked the post, unlike it
+ else {
+ setLiked(false);
+ setNumberOfLikes(numberOfLikes - 1);
+
+ dispatch(unLikePost(props.post.id as number));
+ }
+ };
+
+ const handlePostClick = () => {
+ navigate(`/post/${props.post.id}`);
+ };
+ const handleUserClick = () => {
+ navigate(`/user/${props.post.createdBy?.id}`);
+ };
return (
-
-
+
+
+
+
+
+
+ {props.post.title}
+
+
+ {props.post.content}
+
+
+
+
+
+
+
+
+
+
+
+
);
}
+
+const StyledGrid = styled(Grid)({
+ display: "flex",
+ alignItems: "center",
+ justifyContent: "center",
+ padding: "auto",
+ marginBottom: "0.8rem",
+ marginRight: "0.8rem",
+});
+const StyledTypography = styled(Typography)({
+ padding: "0 0.5rem",
+});
diff --git a/client/src/components/topAppBar.tsx b/client/src/components/topAppBar.tsx
index 4d13a55..a1a65b0 100644
--- a/client/src/components/topAppBar.tsx
+++ b/client/src/components/topAppBar.tsx
@@ -17,7 +17,11 @@ import { useNavigate } from "react-router-dom";
const settings = ["Profile", "Account", "Dashboard", "Logout"];
-function TopAppBar() {
+interface TopAppBarProps {
+ height: number;
+}
+
+function TopAppBar(props: TopAppBarProps) {
const dispatch = useAppDispatch();
const navigate = useNavigate();
const userInfo = useSelector(selectUserInfo);
@@ -25,7 +29,6 @@ function TopAppBar() {
null
);
- console.log(userInfo);
const handleOpenUserMenu = (event: React.MouseEvent) => {
setAnchorElUser(event.currentTarget);
@@ -41,15 +44,14 @@ function TopAppBar() {
};
return (
-
-
+
-
-
+
+
-
);
}
diff --git a/client/src/routes/Auth/authRoot.tsx b/client/src/routes/Auth/authRoot.tsx
index 641cd64..7cd9f52 100644
--- a/client/src/routes/Auth/authRoot.tsx
+++ b/client/src/routes/Auth/authRoot.tsx
@@ -12,7 +12,7 @@ export default function AuthRoot() {
useEffect(() => {
if (loggedIn) {
- return navigate("/");
+ return navigate("/feed");
}
});
diff --git a/client/src/routes/postList.tsx b/client/src/routes/postList.tsx
index a6f98dc..860603e 100644
--- a/client/src/routes/postList.tsx
+++ b/client/src/routes/postList.tsx
@@ -7,7 +7,7 @@ import {
} from "../app/postSlice";
import { store, useAppDispatch } from "../app/store";
import { useEffect } from "react";
-import { List, ListItem, ListItemText } from "@mui/material";
+import { Box, List, ListItem, ListItemText, Typography } from "@mui/material";
import { Post } from "../api";
import PostListItem from "../components/postListItem";
@@ -25,17 +25,49 @@ export default function PostList(props: PostListProps) {
if (props.type === "user") dispatch(fetchFollowedPosts());
}, []);
- console.log(globalPosts);
return (
- <>
+
{props.type === "all" && (
{globalPosts.map((post: Post) => (
-
+
))}
)}
- {props.type === "user" &&
}
- >
+ {props.type === "user" && (
+
+ {followedPosts.map((post: Post) => (
+
+ ))}
+ {followedPosts.length === 0 && (
+
+
+ Whops!
+
+
+ There is no content here! Try following some users, or check the
+ global feed
+
+
+ )}
+
+ )}
+
);
}
diff --git a/client/src/routes/root.tsx b/client/src/routes/root.tsx
index 97dd365..97d025f 100644
--- a/client/src/routes/root.tsx
+++ b/client/src/routes/root.tsx
@@ -10,6 +10,7 @@ import BottomAppBar from "../components/bottomAppBar";
import TopAppBar from "../components/topAppBar";
const drawerWidth = 240;
+const topAppBarHeight = 64;
export default function Root() {
const navigate = useNavigate();
@@ -30,7 +31,7 @@ export default function Root() {
return (
<>
-
+
600 ? drawerWidth : 0}px)` },
+ marginTop: { xs: `${topAppBarHeight}px`},
}}
>