Skip to content

Commit

Permalink
Merge pull request #90 from deepraj21/main
Browse files Browse the repository at this point in the history
fix: realtime post feed update through callback
  • Loading branch information
deepraj21 authored Nov 18, 2024
2 parents 8672d2d + 16a8b6d commit ba901a7
Show file tree
Hide file tree
Showing 6 changed files with 208 additions and 149 deletions.
3 changes: 2 additions & 1 deletion client/src/components/Posts/AddPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { Icons } from "@/components/ui/icons";

const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';

const CreatePost = () => {
const CreatePost = ({ onPostCreated }: { onPostCreated: () => void }) => {
const [description, setDescription] = useState('');
const [tags, setTags] = useState<string[]>([]);
const [imageFile, setImageFile] = useState<File | null>(null);
Expand Down Expand Up @@ -62,6 +62,7 @@ const CreatePost = () => {

if (response.status === 201) {
toast.success('Post created successfully!');
onPostCreated();
}
} catch (error) {
console.error('Error creating post:', error);
Expand Down
289 changes: 153 additions & 136 deletions client/src/components/Posts/ShowPosts.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import {
AlertDialogTrigger,
} from "@/components/ui/alert-dialog";
import { Cross1Icon } from "@radix-ui/react-icons";
import {toast} from 'sonner';
import { PostContext } from '../Sidebar/Sidebar';

const backendUrl = import.meta.env.VITE_BACKEND_URL || 'http://localhost:5000';

Expand All @@ -36,44 +38,56 @@ interface Comment {
}

export default function ShowPosts() {
const username = localStorage.getItem('devhub_username');
const [posts, setPosts] = useState<Post[]>([]);
const [commentText, setCommentText] = useState<string>('');
const [selectedPost, setSelectedPost] = useState<string | null>(null);

useEffect(() => {
const fetchPosts = () => {
axios.get(`${backendUrl}/posts`)
.then(response => {
setPosts(response.data);
})
.catch(error => {
console.error('Error fetching posts:', error);
});
};
const fetchPosts = () => {
axios.get(`${backendUrl}/posts`)
.then(response => {
setPosts(response.data);
})
.catch(error => {
console.error('Error fetching posts:', error);
});
};

useEffect(() => {
fetchPosts();
}, []);

// New function to handle voting
const handleVote = async (postId: string, voteType: 'upvote' | 'downvote') => {
try {
const response = await axios.post(`${backendUrl}/posts/${postId}/${voteType}`);
const response = await axios.post(
`${backendUrl}/posts/${postId}/${voteType}`,
{ username },
{ headers: { 'Content-Type': 'application/json' } }
);

if (response.status === 200) {
// const updatedPosts = posts.map(post => {
// if (post._id === postId) {
// // Update the vote count based on voteType
// if (voteType === 'upvote') {
// return { ...post, upvotes: post.upvotes + 1 };
// } else if (voteType === 'downvote') {
// return { ...post, downvotes: post.downvotes + 1 };
// }
// }
// return post;
// });
// setPosts(updatedPosts);
setPosts(prevPosts =>
prevPosts.map(post =>
post._id === postId
? {
...post,
upvotes: voteType === 'upvote' ? post.upvotes + 1 : post.upvotes,
downvotes: voteType === 'downvote' ? post.downvotes + 1 : post.downvotes
}
: post
)
);
}
} catch (error: unknown) {
if (axios.isAxiosError(error)) {
console.error(`Error ${voteType}ing post:`, error);
if (error.response?.status === 400) {
toast.error(error.response.data.error);
}
} else {
console.error('An unexpected error occurred:', error);
toast.error('An unexpected error occurred');
}
} catch (error) {
console.error(`Error ${voteType}ing post:`, error);
}
};

Expand Down Expand Up @@ -110,122 +124,125 @@ export default function ShowPosts() {
}, [posts, selectedPost]); // This will re-run whenever `posts` or `selectedPost` changes

return (
<div className="max-w-2xl mx-auto space-y-0 md:space-y-8">
{posts.map((post) => (
<div key={post._id} className="bg-background shadow-lg md:rounded-lg overflow-hidden border">
<div className="flex items-center border-b p-4">
<div className='h-8 w-8 rounded-full overflow-hidden'>
<img src={`https://api.dicebear.com/6.x/initials/svg?seed=${post.author_username}`} />
</div>
<div className="ml-3">
<p className="text-sm font-medium">{post.author_username}</p>
<p className="text-xs text-muted-foreground">
Posted {new Date(post.created_at).toLocaleDateString()}
</p>
<PostContext.Provider value={{ refreshPosts: fetchPosts }}>
<div className="max-w-2xl mx-auto space-y-0 md:space-y-8">
{posts.map((post) => (
<div key={post._id} className="bg-background shadow-lg md:rounded-lg overflow-hidden border">
<div className="flex items-center border-b p-4">
<div className='h-8 w-8 rounded-full overflow-hidden'>
<img src={`https://api.dicebear.com/6.x/initials/svg?seed=${post.author_username}`} />
</div>
<div className="ml-3">
<p className="text-sm font-medium">{post.author_username}</p>
<p className="text-xs text-muted-foreground">
Posted {new Date(post.created_at).toLocaleDateString()}
</p>
</div>
</div>
</div>
{post.image_link && (
<img
src={post.image_link}
alt="Post image"
className="w-full h-96 object-cover mb-1 border-b"
/>
)}
<div className="flex items-center gap-4 ml-1">
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1"
onClick={() => handleVote(post._id, 'upvote')}
>
<ChevronUp className="h-5 w-5" />
<span>{post.upvotes}</span>
</Button>
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1"
onClick={() => handleVote(post._id, 'downvote')}
>
<ChevronDown className="h-5 w-5" />
<span>{post.downvotes}</span>
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" size="sm" className="flex items-center gap-1">
<MessageCircle className="h-5 w-5" />
<span>{post.comments.length}</span>
</Button>
</AlertDialogTrigger>

<AlertDialogContent className="grid gap-6 sm:w-80">
<AlertDialogHeader>
<div className='flex items-center'>
<AlertDialogHeader className='text-2xl'>Comments</AlertDialogHeader>
<div className='flex-grow'></div>
<AlertDialogCancel>
<Cross1Icon className='h-3 w-3' />
</AlertDialogCancel>
{post.image_link && (
<img
src={post.image_link}
alt="Post image"
className="w-full h-96 object-cover mb-1 border-b"
/>
)}
<div className="flex items-center gap-4 ml-1">
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1"
onClick={() => handleVote(post._id, 'upvote')}
>
<ChevronUp className="h-5 w-5" />
<span>{post.upvotes}</span>
</Button>
<Button
variant="ghost"
size="sm"
className="flex items-center gap-1"
onClick={() => handleVote(post._id, 'downvote')}
>
<ChevronDown className="h-5 w-5" />
<span>{post.downvotes}</span>
</Button>
<AlertDialog>
<AlertDialogTrigger asChild>
<Button variant="ghost" size="sm" className="flex items-center gap-1">
<MessageCircle className="h-5 w-5" />
<span>{post.comments.length}</span>
</Button>
</AlertDialogTrigger>

<AlertDialogContent className="grid gap-6 sm:w-80">
<AlertDialogHeader>
<div className='flex items-center'>
<AlertDialogHeader className='text-2xl'>Comments</AlertDialogHeader>
<div className='flex-grow'></div>
<AlertDialogCancel>
<Cross1Icon className='h-3 w-3' />
</AlertDialogCancel>
</div>
</AlertDialogHeader>

<div className="max-h-56 overflow-y-auto">
{post.comments.slice().reverse().map((comment) => (
<div key={comment._id} className="flex items-start gap-2 mb-4">
<div className='h-8 w-8 rounded-full overflow-hidden'>
<img src={`https://api.dicebear.com/6.x/initials/svg?seed=${comment.user_username}`} />
</div>
<div>
<p className="text-sm font-medium">{comment.user_username}</p>
<p className="text-sm">{comment.text}</p>
</div>
</div>
))}
</div>
</AlertDialogHeader>

<div className="max-h-56 overflow-y-auto">
{post.comments.slice().reverse().map((comment) => (
<div key={comment._id} className="flex items-start gap-2 mb-4">
<div className='h-8 w-8 rounded-full overflow-hidden'>
<img src={`https://api.dicebear.com/6.x/initials/svg?seed=${comment.user_username}`} />
</div>
<div>
<p className="text-sm font-medium">{comment.user_username}</p>
<p className="text-sm">{comment.text}</p>
</div>
</div>
))}
</div>

<div className="border-t pt-4 mt-4">
<form
onSubmit={(e) => {
e.preventDefault();
handleCommentSubmit(post._id);
}}
className="flex items-center gap-2"
>
<Input
type="text"
placeholder="Add a comment..."
value={commentText}
onChange={(e) => {
setCommentText(e.target.value);
setSelectedPost(post._id);
<div className="border-t pt-4 mt-4">
<form
onSubmit={(e) => {
e.preventDefault();
handleCommentSubmit(post._id);
}}
className="flex-grow"
/>
<Button type="submit" size="sm">
<Send className="h-4 w-4" />
<span className="sr-only">Send comment</span>
</Button>
</form>
</div>
</AlertDialogContent>
</AlertDialog>
</div>
<div className='p-4'>
<p className="text-sm mb-2">{post.description}</p>
<div className="flex flex-wrap gap-2 mb-2">
{JSON.parse(post.tags).map((tag: string, index: number) => (
<span
key={index}
className="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full"
>
#{tag}
</span>
))}
className="flex items-center gap-2"
>
<Input
type="text"
placeholder="Add a comment..."
value={commentText}
onChange={(e) => {
setCommentText(e.target.value);
setSelectedPost(post._id);
}}
className="flex-grow"
/>
<Button type="submit" size="sm">
<Send className="h-4 w-4" />
<span className="sr-only">Send comment</span>
</Button>
</form>
</div>
</AlertDialogContent>
</AlertDialog>
</div>
<div className='p-4'>
<p className="text-sm mb-2">{post.description}</p>
<div className="flex flex-wrap gap-2 mb-2">
{JSON.parse(post.tags).map((tag: string, index: number) => (
<span
key={index}
className="bg-primary/10 text-primary text-xs px-2 py-1 rounded-full"
>
#{tag}
</span>
))}
</div>

</div>
</div>
</div>
))}
</div>
))}
</div>
</PostContext.Provider>

);
}
4 changes: 2 additions & 2 deletions client/src/components/Settings/Settings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {
import UpdateProfile from "./UpdateProfile"
import WebappSettings from "./WebappSettings"

const Settings = () => {
const Setting = () => {
return (
<Tabs defaultValue="profile" className="mt-5 w-full">
<TabsList className="grid w-full grid-cols-2">
Expand All @@ -24,4 +24,4 @@ const Settings = () => {
)
}

export default Settings
export default Setting
Loading

0 comments on commit ba901a7

Please sign in to comment.