Snapgram Profile Page
Task 🎯
Your goal is to complete the post details page by developing the “Related Posts” feature.
Example 🧩
Visit the Figma Design and see how the feature should look like
Resources 📖
Hint 💡
Stuck somewhere? No worries, we’re here to help!
Ask yourself —
What is expected (The Output)?
We want a nice-looking Profile page showcasing both the posts created by that user as well as the posts that the user has liked
What do we need in order to build that?
- User Info
- Conditionally rendering of “Edit Profile”
- User’s posts
- Posts users have liked
- …?
We’ll take baby steps to finish this from start to end,
- The UI
Again, “Think in the Layouts”!
We can see that there is some user info at the top and also an “Edit Profile” button. What’s the direction of this content to each other?
Row? Yes, so can you break these into three parts,
- One part: User Profile Image
- Second part: User Info, i.e., name, username, some stats
- Other part: Edit/Follow button Done? Good! Now create the elements inside each of these parts
- Create the image element first by taking the accurate numbers from the Figma
- Create the elements inside the second part, where we have a couple of things. Now for this as well, “Think in Layouts”. In which direction the content has been laid? Column? Yup! So can you use Flex and create these elements?
- We’ll have to conditionally render the button, meaning if the user ID of the profile we’re checking is the same as the user ID of the current user that has logged in, then show the “Edit Profile” button otherwise, show the “Follow” button.
A simple check, i.e.,
currentUser.$id === user.id ? <EditButton /> : <FollowButton />;
How’s it so far? Hope you’re getting there.
Now, we’ll have to conditionally do one more thing and it is to show/hide liked posts.
If a user is checking their profile, then we’ll show the option to see both their post as well as the posts they liked but if the user is checking other user’s profiles, then we’ll only show the posts they have created. The traditional Instagram way!
And yes, as you might have anticipated, we’ll use the same condition as above to render the content accordingly
But how do we create the tabs?
- Use the shadcn tabs
- Create a custom using div. How?
- Create a container with two boxes in the row direction
- Style them according to the Figma Design, i.e.,
- Background Color
- Text Color
- Font size
- …?
- For the active state to see if that’s what the user has clicked in, we can use the router as,
- For created posts, the URL will be the same, i.e.,
/profile/${id}
- For liked posts, the URL will be
/profile/${id}/liked-posts
- For created posts, the URL will be the same, i.e.,
- But how do we know which path the user is currently viewing?
- Using the window location (yeah, you can try that)
- Using the
useLocation
hook of react-router-dom. You can read about it React Router useLocation
Makes sense?
Now let’s render the content inside these tabs. And for this, if you’re going for the second option of creating a tab, we’ll have to do something differently
We’ll render other Routes inside this page separately.
- Setup Routes inside the component
<Routes><Route />{currentUser.$id === user.id && <Route />}</Routes>
- First Route will show the posts created by the user. In order to render that route directly to show the content inside the route we are, we have to provide an
index
to the Route. It determines if the route is an index route. Index routes render into their parent's React Router Outlet at their parent's URL (like a default child route).<Routes>**<Route index element={<GridPostList />} />** ...</Routes> - As we planned, the other route can be the liked posts route which should only show up when the ID of the logged-in user matches the ID of the user profile
<Routes><Route index element={<GridPostList />} />**{currentUser.$id === user.id && (<Route path='/liked-posts' element={<LikedPosts />} />)}**</Routes>
- Lastly, we’ll have to add Outlet. Why?
The outlet component allows nested routes. It tells the route element when and where the child routes should be rendered<Routes><Route index element={<GridPostList />} />{currentUser.$id === user.id && (<Route path="/liked-posts" element={<LikedPosts />} />)}</Routes>**<Outlet />**
Understood?
Now the final part, creating the queries for these:
-
Get Posts Created by the user
-
Create a query hook, say,
useGetUserPosts
, that will fetch the posts created by that particular userexport const useGetUserPosts = (userId?: string) => {return useQuery({...});}; -
Create the Appwrite API function using try-catch, say,
getUserPosts
, and assign that function to thequeryFn
export async function getUserPosts(userId?: string) {try {} catch (error) {console.log(error);}}export const useGetUserPosts = (userId?: string) => {return useQuery({queryFn: () => getUserPosts(userId),});}; -
Now inside the Appwrite API, implement the logic to get the posts created by that particular user. What we’ll need for that?
- User ID
- Appwrite List documents endpoint
- Database ID
- User Collection ID
- Queries to filter out the posts where the creator ID is the same as the user ID passed to the function
-
There is one more thing to do and it’s about handling the cases properly. What if there is no user id? Or what if the query we created gets executed much before it gets the user ID?
That may potentially break the application. So we need to determine when and when not to execute the query
To avoid this, we can explicitly tell the react query to execute the query only when the user ID is there. How?
Using enabled, which decides when and when not to execute the query hook, and using the
queryKey
to revalidate the cacheexport const useGetUserPosts = (userId?: string) => {return useQuery({**queryKey: ["getUserPosts", userId],**queryFn: () => getUserPosts(userId),**enabled: !!userId,**});};And your query is ready! Go call it 😃
-
But there is one last thing we have to do, i.e., show the liked posts of user
-
Liked Posts
If you remember the database structure we have created, you’ll realize that we have created a liked array inside the user collection, which is a relation to the User post.
What does that mean? It means that all we have to do is get the user info, and Appwrite will send the liked info or that array alongside. So, what are the steps?
- Create a separate component where we can render the Liked Posts
- Pass the component to the Route we have created
- Inside the component, call the
useGetCurrentUser
, which holds the information of the user - Retrieve the data and loading state from the hook
const { data: currentUser, isLoading } = useGetCurrentUser();
- If it’s loading, render the
Loader
component we have created - If not, pass the data we get inside the currentUser, i.e.,
currentUser.liked
to the reusableGridPostList
component
And that’s it, basically. You should be able to see the liked posts liked by you!
These challenges or features are more logic-oriented, and there may be other solutions. So take your time, think carefully, develop your logical abilities, and give it a try.
We believe in your capabilities ❤️