Metabook is a clone of Facebook and has the functionality for profiles, newsfeeds, posts, comments, and likes.
- Ruby
- Ruby on Rails
- JavaScript
- jQuery
- React
- Redux
- npm/webpack/babel
- AWS S3/IAM
- Heroku
To use comments with multiple foreign tables including itself, a polymorphic association had to be setup in the models and migration to reduce the need for having numerous foreign keys:
#app/models/comment.rb
class Comment < ApplicationRecord
belongs_to :commentable,
polymorphic: true
has_many :comments,
as: :commentable,
class_name: :Comment,
dependent: :destroy
end
#app/models/post.rb
class Post < ApplicationRecord
has_many :comments,
as: :commentable,
dependent: :destroy
end
A filter will then be set up to separate the two types of comments for posts, as it contains both parent comments and replies, whilst the comments only contain replies, hence not requiring a filter:
//frontend/components/newsfeed/post_item.jsx
{state.comments.map((comment, i) => {
if (comment.commentable_type === "Post"){
return(
<Comment
key={i}
/>
);
};
})}
//frontend/components/newsfeed/comment_item.jsx
{(state.comments.map((reply, i) => {
return(
<Reply
key={i}
/>
);
}))}
Likes also had a similiar polymorphic setup, with the exception of having a uniquness validation to a user_id and the associated liked object, ridding the need for a filter on the frontend:
#app/models/like.rb
class Like < ApplicationRecord
validates :user_id, uniqueness: { scope: [:likeable_id, :likeable_type]}
belongs_to :likeable,
polymorphic: true
end
#app/models/post.rb
class Post < ApplicationRecord
has_many :likes,
as: :likeable,
dependent: :destroy
end
#app/models/comment.rb
class Comment < ApplicationRecord
has_many :likes,
as: :likeable,
dependent: :destroy
end
For comments and replies, in order to perform multiple asynchronous fetch requests at once to set the state of a component, an async/await helper function was used to wait for promises before setting the state:
//frontend/components/newsfeed/comment_item.jsx
const fetchData = async () => {
const userData = await fetchUser(comment.user_id);
const commentData = await fetchComment(comment.id);
setState({
...state,
profilePhoto: userData.user.profilePhoto,
firstName: userData.user.first_name,
lastName: userData.user.last_name,
comments: commentData.comment.comments,
likes: commentData.comment.likes
});
};
useEffect(() => {
fetchData();
}, []);