Skip to content

Commit

Permalink
feat: add posts
Browse files Browse the repository at this point in the history
  • Loading branch information
AntonioErdeljac committed Mar 5, 2023
1 parent bdb3c03 commit d0a2574
Show file tree
Hide file tree
Showing 14 changed files with 260 additions and 44 deletions.
13 changes: 8 additions & 5 deletions components/Feed.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
import Header from '@/components/Header';
import PostItem from '@/components/posts/PostItem';

import usePosts from '@/hooks/usePosts';

interface FeedProps {
posts?: Record<string, any>[];
userId?: string;
}

const Feed: React.FC<FeedProps> = ({ posts = [] }) => {
const Feed: React.FC<FeedProps> = ({ userId }) => {
const { data: posts = [] } = usePosts(userId);

return (
<>
{posts.map((post, i) => (
<PostItem key={`post-${i}`} />
{posts.map((post: Record<string, any>,) => (
<PostItem key={post.id} data={post} />
))}
</>
);
Expand Down
34 changes: 32 additions & 2 deletions components/Form.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import axios from 'axios';
import { useCallback, useState } from 'react';
import { toast } from 'react-hot-toast';
import { BiImage } from 'react-icons/bi';
import { HiOutlineGif } from 'react-icons/hi2';
import { IoLocationSharp } from 'react-icons/io5';

import useLoginModal from '@/hooks/useLoginModal';
import useRegisterModal from '@/hooks/useRegisterModal';
import useCurrentUser from '@/hooks/useCurrentUser';
import usePosts from '@/hooks/usePosts';

import Avatar from './Avatar';
import Button from './Button';
Expand All @@ -18,15 +22,41 @@ const Form: React.FC<FormProps> = ({ placeholder }) => {
const loginModal = useLoginModal();

const { data: currentUser } = useCurrentUser();
const { mutate: mutatePosts } = usePosts();

const [body, setBody] = useState('');
const [isLoading, setIsLoading] = useState(false);

const onSubmit = useCallback(async () => {
try {
setIsLoading(true);

console.log({ body })

await axios.post('/api/posts', { body });

toast.success('Tweet created');
setBody('');
mutatePosts();
} catch (error) {
toast.error('Something went wrong');
} finally {
setIsLoading(false);
}
}, [body, mutatePosts]);

return (
<div className="border-b-[1px] border-neutral-800 px-5 py-2">
{currentUser ? (
<div className="flex flex-row gap-4">
<Avatar userId={currentUser?.id} />
<div className="w-full">
<textarea
<textarea
disabled={isLoading}
onChange={(event) => setBody(event.target.value)}
value={body}
className="
disabled:opacity-80
peer
resize-none
mt-3
Expand Down Expand Up @@ -64,7 +94,7 @@ const Form: React.FC<FormProps> = ({ placeholder }) => {
className="text-sky-500 cursor-pointer"
/>
</div>
<Button onClick={() => {}} label="Tweet" />
<Button disabled={isLoading || !body} onClick={onSubmit} label="Tweet" />
</div>
</div>
</div>
Expand Down
24 changes: 14 additions & 10 deletions components/posts/PostItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,23 @@ import useCurrentUser from '@/hooks/useCurrentUser';

import Avatar from '../Avatar';

const PostItem = () => {
interface PostItemProps {
data: Record<string, any>;
}

const PostItem: React.FC<PostItemProps> = ({ data = {} }) => {
const router = useRouter();
const loginModal = useLoginModal();
const { data: currentUser } = useCurrentUser();

const goToUser = useCallback((ev: any) => {
ev.stopPropagation();
router.push('/users/123')
}, [router]);
router.push(`/users/${data.user.id}`)
}, [router, data.user.id]);

const goToPost = useCallback(() => {
router.push('/posts/123');
}, [router]);
router.push(`/posts/${data.id}`);
}, [router, data.id]);

const onLike = useCallback((ev: any) => {
ev.stopPropagation();
Expand All @@ -41,7 +45,7 @@ const PostItem = () => {
transition
">
<div className="flex flex-row items-start gap-3">
<Avatar href="/users/123" />
<Avatar userId={data.user.id} />
<div>
<div className="flex flex-row items-center gap-2">
<p
Expand All @@ -52,7 +56,7 @@ const PostItem = () => {
cursor-pointer
hover:underline
">
Elon Musk
{data.user.name}
</p>
<span
onClick={goToUser}
Expand All @@ -61,14 +65,14 @@ const PostItem = () => {
cursor-pointer
hover:underline
">
@elonmusk
@{data.user.username}
</span>
<span className="text-neutral-500">
2h
</span>
</div>
<div className="text-white mt-1">
This is my very real Tweet!
{data.body}
</div>
<div className="flex flex-row items-center mt-3 gap-10">
<div
Expand Down
10 changes: 5 additions & 5 deletions components/users/UserBio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ const UserBio: React.FC<UserBioProps> = ({ userId }) => {
const { data: currentUser } = useCurrentUser();
const { data: fetchedUser } = useUser(userId);

const { isFollowing, onFollow, onUnfollow } = useFollow(userId);
const { isFollowing, toggleFollow } = useFollow(userId);

const createdAt = useMemo(() => {
if (!fetchedUser?.createdAt) {
Expand All @@ -32,9 +32,9 @@ const UserBio: React.FC<UserBioProps> = ({ userId }) => {
<div className="flex justify-end p-2">
<Button
disabled={currentUser?.id === userId}
onClick={isFollowing ? onUnfollow : onFollow}
secondary
onClick={toggleFollow}
label={isFollowing ? 'Unfollow' : 'Follow'}
secondary={!isFollowing}
outline={isFollowing}
/>
</div>
Expand Down Expand Up @@ -68,11 +68,11 @@ const UserBio: React.FC<UserBioProps> = ({ userId }) => {
</div>
<div className="flex flex-row items-center mt-4 gap-6">
<div className="flex flex-row items-center gap-1">
<p className="text-white">0</p>
<p className="text-white">{fetchedUser?.followingIds?.length}</p>
<p className="text-neutral-500">Following</p>
</div>
<div className="flex flex-row items-center gap-1">
<p className="text-white">0</p>
<p className="text-white">{fetchedUser?.followersCount || 0}</p>
<p className="text-neutral-500">Followers</p>
</div>
</div>
Expand Down
29 changes: 15 additions & 14 deletions hooks/useFollow.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,11 @@ import { toast } from "react-hot-toast";

import useCurrentUser from "./useCurrentUser";
import useLoginModal from "./useLoginModal";
import useUser from "./useUser";

const useFollow = (userId: string) => {
const { data: currentUser, mutate: mutateCurrentUser } = useCurrentUser();
const { mutate: mutateFetchedUser } = useUser(userId);

const loginModal = useLoginModal();

Expand All @@ -16,34 +18,33 @@ const useFollow = (userId: string) => {
return list.includes(userId);
}, [currentUser, userId]);

const onFollow = useCallback(async () => {
const toggleFollow = useCallback(async () => {
if (!currentUser) {
return loginModal.onOpen();
}

try {
await axios.post('/api/follow', { userId });
mutateCurrentUser();
toast.success('Success');
} catch (error) {
toast.error('Something went wrong');
}
}, [currentUser, loginModal, userId, mutateCurrentUser]);
let request;

const onUnfollow = useCallback(async () => {
try {
await axios.delete('/api/follow', { data: { userId } });
if (isFollowing) {
request = () => axios.delete('/api/follow', { data: { userId } });
} else {
request = () => axios.post('/api/follow', { userId });
}

await request();
mutateCurrentUser();
mutateFetchedUser();

toast.success('Success');
} catch (error) {
toast.error('Something went wrong');
}
}, [userId, mutateCurrentUser]);
}, [currentUser, isFollowing, userId, mutateCurrentUser, mutateFetchedUser, loginModal]);

return {
isFollowing,
onFollow,
onUnfollow
toggleFollow,
}
}

Expand Down
16 changes: 16 additions & 0 deletions hooks/usePost.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import useSWR from 'swr';

import fetcher from '@/libs/fetcher';

const usePost = (postId: string) => {
const { data, error, isLoading, mutate } = useSWR(postId ? `/api/posts/${postId}` : null, fetcher);

return {
data,
error,
isLoading,
mutate
}
};

export default usePost;
17 changes: 17 additions & 0 deletions hooks/usePosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import useSWR from 'swr';

import fetcher from '@/libs/fetcher';

const usePosts = (userId?: string) => {
const url = userId ? `/api/posts?userId=${userId}` : '/api/posts';
const { data, error, isLoading, mutate } = useSWR(url, fetcher);

return {
data,
error,
isLoading,
mutate
}
};

export default usePosts;
34 changes: 34 additions & 0 deletions pages/api/posts/[postId].ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { NextApiRequest, NextApiResponse } from "next";

import prisma from "@/libs/prismadb";
import serverAuth from "@/libs/serverAuth";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'GET') {
return res.status(405).end();
}

try {
await serverAuth(req);

const { postId } = req.query;

if (!postId || typeof postId !== 'string') {
throw new Error('Invalid ID');
}

const post = await prisma.post.findUnique({
where: {
id: postId,
},
include: {
user: true
}
});

return res.status(200).json(post);
} catch (error) {
console.log(error);
return res.status(400).end();
}
}
63 changes: 63 additions & 0 deletions pages/api/posts/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { NextApiRequest, NextApiResponse } from "next";

import serverAuth from "@/libs/serverAuth";
import prisma from "@/libs/prismadb";

export default async function handler(req: NextApiRequest, res: NextApiResponse) {
if (req.method !== 'POST' && req.method !== 'GET') {
return res.status(405).end();
}

try {
const { currentUser } = await serverAuth(req);

if (req.method === 'POST') {
const { body } = req.body;

const post = await prisma.post.create({
data: {
body,
userId: currentUser.id
}
});

return res.status(200).json(post);
}

if (req.method === 'GET') {
const { userId } = req.query;

console.log({ userId })

let posts;

if (userId && typeof userId === 'string') {
posts = await prisma.post.findMany({
where: {
userId
},
include: {
user: true
},
orderBy: {
createdAt: 'desc'
}
});
} else {
posts = await prisma.post.findMany({
include: {
user: true
},
orderBy: {
createdAt: 'desc'
}
});
}

return res.status(200).json(posts);
}
} catch (error) {
console.log(error);
return res.status(400).end();
}
}
Loading

0 comments on commit d0a2574

Please sign in to comment.