Skip to content

Commit

Permalink
Optimizations added with global posts state
Browse files Browse the repository at this point in the history
  • Loading branch information
burakorkmez committed Aug 25, 2023
1 parent 03bf46d commit fe7e7bf
Show file tree
Hide file tree
Showing 12 changed files with 162 additions and 44 deletions.
4 changes: 2 additions & 2 deletions backend/controllers/postController.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ const createPost = async (req, res) => {
const newPost = new Post({ postedBy, text, img });
await newPost.save();

res.status(201).json({ message: "Post created successfully", newPost });
res.status(201).json(newPost);
} catch (err) {
res.status(500).json({ error: err.message });
console.log(err);
Expand Down Expand Up @@ -128,7 +128,7 @@ const replyToPost = async (req, res) => {
post.replies.push(reply);
await post.save();

res.status(200).json({ message: "Reply added successfully", post });
res.status(200).json(reply);
} catch (err) {
res.status(500).json({ error: err.message });
}
Expand Down
13 changes: 13 additions & 0 deletions backend/controllers/userController.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import User from "../models/userModel.js";
import Post from "../models/postModel.js";
import bcrypt from "bcryptjs";
import generateTokenAndSetCookie from "../utils/helpers/generateTokenAndSetCookie.js";
import { v2 as cloudinary } from "cloudinary";
Expand Down Expand Up @@ -167,6 +168,18 @@ const updateUser = async (req, res) => {

user = await user.save();

// Find all posts that this user replied and update username and userProfilePic fields
await Post.updateMany(
{ "replies.userId": userId },
{
$set: {
"replies.$[reply].username": user.username,
"replies.$[reply].userProfilePic": user.profilePic,
},
},
{ arrayFilters: [{ "reply.userId": userId }] }
);

// password should be null in response
user.password = null;

Expand Down
18 changes: 13 additions & 5 deletions frontend/src/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import HomePage from "./pages/HomePage";
import AuthPage from "./pages/AuthPage";
import { useRecoilValue } from "recoil";
import userAtom from "./atoms/userAtom";
import LogoutButton from "./components/LogoutButton";
import UpdateProfilePage from "./pages/UpdateProfilePage";
import CreatePost from "./components/CreatePost";
function App() {
Expand All @@ -21,12 +20,21 @@ function App() {
<Route path='/auth' element={!user ? <AuthPage /> : <Navigate to='/' />} />
<Route path='/update' element={user ? <UpdateProfilePage /> : <Navigate to='/auth' />} />

<Route path='/:username' element={<UserPage />} />
<Route
path='/:username'
element={
user ? (
<>
<UserPage />
<CreatePost />
</>
) : (
<UserPage />
)
}
/>
<Route path='/:username/post/:pid' element={<PostPage />} />
</Routes>

{user && <LogoutButton />}
{user && <CreatePost />}
</Container>
);
}
Expand Down
8 changes: 8 additions & 0 deletions frontend/src/atoms/postsAtom.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import { atom } from "recoil";

const postsAtom = atom({
key: "postsAtom",
default: [],
});

export default postsAtom;
33 changes: 26 additions & 7 deletions frontend/src/components/Actions.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,14 +15,15 @@ import {
useDisclosure,
} from "@chakra-ui/react";
import { useState } from "react";
import { useRecoilValue } from "recoil";
import { useRecoilState, useRecoilValue } from "recoil";
import userAtom from "../atoms/userAtom";
import useShowToast from "../hooks/useShowToast";
import postsAtom from "../atoms/postsAtom";

const Actions = ({ post: post_ }) => {
const Actions = ({ post }) => {
const user = useRecoilValue(userAtom);
const [liked, setLiked] = useState(post_.likes.includes(user?._id));
const [post, setPost] = useState(post_);
const [liked, setLiked] = useState(post.likes.includes(user?._id));
const [posts, setPosts] = useRecoilState(postsAtom);
const [isLiking, setIsLiking] = useState(false);
const [isReplying, setIsReplying] = useState(false);
const [reply, setReply] = useState("");
Expand All @@ -46,10 +47,22 @@ const Actions = ({ post: post_ }) => {

if (!liked) {
// add the id of the current user to post.likes array
setPost({ ...post, likes: [...post.likes, user._id] });
const updatedPosts = posts.map((p) => {
if (p._id === post._id) {
return { ...p, likes: [...p.likes, user._id] };
}
return p;
});
setPosts(updatedPosts);
} else {
// remove the id of the current user from post.likes array
setPost({ ...post, likes: post.likes.filter((id) => id !== user._id) });
const updatedPosts = posts.map((p) => {
if (p._id === post._id) {
return { ...p, likes: p.likes.filter((id) => id !== user._id) };
}
return p;
});
setPosts(updatedPosts);
}

setLiked(!liked);
Expand All @@ -75,7 +88,13 @@ const Actions = ({ post: post_ }) => {
const data = await res.json();
if (data.error) return showToast("Error", data.error, "error");

setPost({ ...post, replies: [...post.replies, data.reply] });
const updatedPosts = posts.map((p) => {
if (p._id === post._id) {
return { ...p, replies: [...p.replies, data] };
}
return p;
});
setPosts(updatedPosts);
showToast("Success", "Reply posted successfully", "success");
onClose();
setReply("");
Expand Down
15 changes: 11 additions & 4 deletions frontend/src/components/CreatePost.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,11 @@ import {
import { useRef, useState } from "react";
import usePreviewImg from "../hooks/usePreviewImg";
import { BsFillImageFill } from "react-icons/bs";
import { useRecoilValue } from "recoil";
import { useRecoilState, useRecoilValue } from "recoil";
import userAtom from "../atoms/userAtom";
import useShowToast from "../hooks/useShowToast";
import postsAtom from "../atoms/postsAtom";
import { useParams } from "react-router-dom";

const MAX_CHAR = 500;

Expand All @@ -36,6 +38,8 @@ const CreatePost = () => {
const user = useRecoilValue(userAtom);
const showToast = useShowToast();
const [loading, setLoading] = useState(false);
const [posts, setPosts] = useRecoilState(postsAtom);
const { username } = useParams();

const handleTextChange = (e) => {
const inputText = e.target.value;
Expand Down Expand Up @@ -67,6 +71,9 @@ const CreatePost = () => {
return;
}
showToast("Success", "Post created successfully", "success");
if (username === user.username) {
setPosts([data, ...posts]);
}
onClose();
setPostText("");
setImgUrl("");
Expand All @@ -82,12 +89,12 @@ const CreatePost = () => {
<Button
position={"fixed"}
bottom={10}
right={10}
leftIcon={<AddIcon />}
right={5}
bg={useColorModeValue("gray.300", "gray.dark")}
onClick={onOpen}
size={{ base: "sm", sm: "md" }}
>
Post
<AddIcon />
</Button>

<Modal isOpen={isOpen} onClose={onClose}>
Expand Down
29 changes: 25 additions & 4 deletions frontend/src/components/Header.jsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,18 @@
import { Flex, Image, Link, useColorMode } from "@chakra-ui/react";
import { useRecoilValue } from "recoil";
import { Button, Flex, Image, Link, useColorMode } from "@chakra-ui/react";
import { useRecoilValue, useSetRecoilState } from "recoil";
import userAtom from "../atoms/userAtom";
import { AiFillHome } from "react-icons/ai";
import { RxAvatar } from "react-icons/rx";
import { Link as RouterLink } from "react-router-dom";
import { FiLogOut } from "react-icons/fi";
import useLogout from "../hooks/useLogout";
import authScreenAtom from "../atoms/authAtom";

const Header = () => {
const { colorMode, toggleColorMode } = useColorMode();
const user = useRecoilValue(userAtom);
const logout = useLogout();
const setAuthScreen = useSetRecoilState(authScreenAtom);

return (
<Flex justifyContent={"space-between"} mt={6} mb='12'>
Expand All @@ -16,6 +21,11 @@ const Header = () => {
<AiFillHome size={24} />
</Link>
)}
{!user && (
<Link as={RouterLink} onClick={() => setAuthScreen("login")}>
Login
</Link>
)}

<Image
cursor={"pointer"}
Expand All @@ -26,8 +36,19 @@ const Header = () => {
/>

{user && (
<Link as={RouterLink} to={`/${user.username}`}>
<RxAvatar size={24} />
<Flex alignItems={"center"} gap={4}>
<Link as={RouterLink} to={`/${user.username}`}>
<RxAvatar size={24} />
</Link>
<Button size={"xs"} onClick={logout}>
<FiLogOut size={20} />
</Button>
</Flex>
)}

{!user && (
<Link as={RouterLink} onClick={() => setAuthScreen("signup")}>
Sign up
</Link>
)}
</Flex>
Expand Down
6 changes: 4 additions & 2 deletions frontend/src/components/Post.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,15 @@ import { useEffect, useState } from "react";
import useShowToast from "../hooks/useShowToast";
import { formatDistanceToNow } from "date-fns";
import { DeleteIcon } from "@chakra-ui/icons";
import { useRecoilValue } from "recoil";
import { useRecoilState, useRecoilValue } from "recoil";
import userAtom from "../atoms/userAtom";
import postsAtom from "../atoms/postsAtom";

const Post = ({ post, postedBy }) => {
const [user, setUser] = useState(null);
const showToast = useShowToast();
const currentUser = useRecoilValue(userAtom);

const [posts, setPosts] = useRecoilState(postsAtom);
const navigate = useNavigate();

useEffect(() => {
Expand Down Expand Up @@ -50,6 +51,7 @@ const Post = ({ post, postedBy }) => {
return;
}
showToast("Success", "Post deleted", "success");
setPosts(posts.filter((p) => p._id !== post._id));
} catch (error) {
showToast("Error", error.message, "error");
}
Expand Down
34 changes: 34 additions & 0 deletions frontend/src/hooks/useLogout.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import userAtom from "../atoms/userAtom";
import { useSetRecoilState } from "recoil";
import useShowToast from "./useShowToast";

const useLogout = () => {
const setUser = useSetRecoilState(userAtom);
const showToast = useShowToast();

const logout = async () => {
try {
const res = await fetch("/api/users/logout", {
method: "POST",
headers: {
"Content-Type": "application/json",
},
});
const data = await res.json();

if (data.error) {
showToast("Error", data.error, "error");
return;
}

localStorage.removeItem("user-threads");
setUser(null);
} catch (error) {
showToast("Error", error, "error");
}
};

return logout;
};

export default useLogout;
7 changes: 5 additions & 2 deletions frontend/src/pages/HomePage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,17 @@ import { Flex, Spinner } from "@chakra-ui/react";
import { useEffect, useState } from "react";
import useShowToast from "../hooks/useShowToast";
import Post from "../components/Post";
import { useRecoilState } from "recoil";
import postsAtom from "../atoms/postsAtom";

const HomePage = () => {
const [posts, setPosts] = useState([]);
const [posts, setPosts] = useRecoilState(postsAtom);
const [loading, setLoading] = useState(true);
const showToast = useShowToast();
useEffect(() => {
const getFeedPosts = async () => {
setLoading(true);
setPosts([]);
try {
const res = await fetch("/api/posts/feed");
const data = await res.json();
Expand All @@ -26,7 +29,7 @@ const HomePage = () => {
}
};
getFeedPosts();
}, [showToast]);
}, [showToast, setPosts]);

return (
<>
Expand Down
Loading

0 comments on commit fe7e7bf

Please sign in to comment.