Build a Complete Social Media Platform with Remix(V2), Supabase(with OAuth) and TailwindCSS

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
hi if you haven't been to my channel my name is Raj and I love to talk about the tech that I work with on a day-to-day basis few weeks back I wanted to build this small app using remix and super base and as I was working on this app I fell in love with the Tex tag and ended up adding more and more features to my app and in the end it turned out to be a fairly decent sized app and in this video we're going to do the same we're going to build a fully responsive and a functional social media platform using remix and superbase you're going to learn how to build a scalable app from scratch build ux flows database design GitHub ooth infinite content feeds managing user profiles optimistic UI and pending UI and also deploy the app to actual users this took a lot of time for me to prepare I hope you find Value in this with that being said let's get straight into the video let's take a look at what we're going to build this is our app and the landing page where we have some nice fancy text and a demo video of our app and if I click on the try now button we are taken to the login route and in the login route we have another button that when clicked will log us into using GitHub o once logged in uh you can see that we have a navbar with some user information such as username and Avatar and a log out button and underneath that we have two tabs uh View Post and write post if I go and write a post let's say hey this app is great and post and switch back to view post you can see that the post has been already updated on the list and this list is not a normal list but a virtual infinite list a virtual infinite list is an optimized list that only renders what users see in the viewport and for this I've used a library called react virtuoso and more on that later I can filter this list based on this input Fields And if I type something in this Fields then the whole list is filter based on this text and you can also see that this update is reflected in the URL as well on top of this you can see the likes count and also the comments count on the list items and if I click on either of those you can see that there is a dialogue that pops up for that specific post and that contains the like Li and the comments of the post and if I click on the like button you can see that it's immediately updating the like even before the database is updated and this is an optimistic UI in action with that you can like and unlike any post and right below you can also add comments on this post let me go ahead and add a comment hey this post looks cool and you can see that the update immediately pops up on the screen um you should know that there is no local state on this app and every update is a revalidation happening through the remix framework after every action and once done if I dismiss this dialogue you can see that the new count of likes and the comments have been updated on our list item um on the same lines we have a profile page and it also has a list of all the posts from the owner of the profile and a link to the cab of the owner and similar to the infinite feed you can like and unlik and also comment on the post of that specific user and the updated count will be reflected on the list item this is going to be a long form content so be prepared for that and I've also split this content to the chapters you can also skip to sections based on your liking uh the code is already deployed and is available on Mega Hub um the link is in the description you can go take a look and play around with the code with that being said let's get straight into the video so we've already talked about the demo of the app so the next thing that we're going to talk about is a UI and the flow of the page the app starts with a simple landing page under the root path and then from there the user goes on to the login page once the user logs in they are redirected to the git Post Feed where they would see the infinite list the infinite list when clicked on will open a dialogue that contains the information of that post under this row on the same line you can go to the profile of the user by clicking the username and you will land on the profile page with user details and posts from the user the behavior of the infinite list is the same for list inside profile route and the get post route finally the functional requirements of the app are all users can see posts comments likes and profiles all users can post their own post and all users should be able to like or unlike any post and all users should be able to comment on any post let's design our database as you can see we have four tables plus one table that's provided by superbase o called the user table we create a profile stable based on the ar. user stable meaning if there is a new user added to the od. users table then we copy the information into the profiles table using a function this function is triggered on an after insert events on the alt. users table we have our post table that contains post for specific users and we also have this user ID that's a foreign key pointing to the ID on the profiles table and the title is the content of the post and then we have likes table that points to the likes on the post and you can see that this is tied to both the post and the user table we will also have to limit the likes to one like per user and for that we will create a unique index on the likes table then we have a comments table and the users can have multiple comments on the comments table they are tied to the post and the users similar to the likes table super base enables us to control the RO level access of all the tables using something called as the orless policies we have to create one for all the tables based on the following assumptions any authenticated user can access posts like basic profile and comment information only authenticated users can post like and comment on behalf of their logged in session meaning I cannot do an action on behalf of another person's post with my logged in session if this is confusing don't worry we will we'll shortly see how to do this now that we are clear about the schema let's go to super base and do everything we talked about so first we will create all the four tables that we talked about and then we will create a unique index on lik table to ensure that only one like per user is stored and then we will create the function and the trigger and finally we will have the hourless policies on all the tables this is my super base dashboard and I've got two projects underneath my dashboard and the one on the left side you see so there is already been deployed the one on the right that's a new one that I've just started right now so what we're going to do is we're going to go inside this project and I'm going to go to the database and you can see that there are no table so the first thing that I'm going to do is I'm going to create a new table and I'm going to call it the profiles table and we've got I and I'm going to change it to UU ID and I'm going to add three columns so the first is going to be name and it's going to be text and this is non-nullable and another column for username is also going to be text and also non Lael and the last column is going to be the avatar URL and it's also going to be text and it's non aable and I'm going to save this information and this will create our profiles table once done we're going to make our next table um called post table and the ID is going to be a uyu ID and I'm going to add a column called title that's going to be text and it's non aable and also on top of this we're going to have a user ID that's a foreign key of the profile table and on top of that you can see that we also have to define the action so the action is that if the record on the public. profiles table is deleted then we also want it to Cascade and delete the record on this table so I'm going to choose Cascade and click on Save and it's also going to be nonble that looks good and after that I'm going to create a new table for likes table and it's going to be U ID and um I'm going to add user ID and it should be the likes for that specific user and I'm going to choose Cascade and save and nonble and I'm going to pass the post ID that's also going to be tied to the post table and I'm going to choose Cascade and this is going to be notable and our last table is going to be the comments table and this is going to look similar to the likes table so we're going to have a user ID that's going to be tied to the profile stable and we're going to have a post ID that's tied to the post table and both of these informations are non aable and I'm going to save this table so we've got four tables right now under our database so the next thing what we're going to do is we're going to create a unique index on the likes table so for that I'm I'm going to run a small SQL so I'm going to go to the SQL editor and then paste this code so what it says is that create unique index the name of the index on the likes table post ID user ID so this is going to be public. likes I think it should just simply work if any user attempts to store more than one like per user per post post then we should throw a database error I'm going to run this all right so now this should be ready so for example if I go to indexes on the database you can see that we have one index on the likes Table after this we're going to create a function and this function is going to be um insert Pro profile for new user the return type is going to be trigger and this is the function so let me quickly walk you through this function so what this function does is um insert into the profiles table these values from the record on the table that we run this function on so this new is going to be the record on the au. users table on the o. us table we have access to the ID and we also have an access to a jsonb data type inside that Json B data type it will have a name username and an avatar URL and we return the record and then we end the whole thing and I'm going to show the advanced settings and this is going to be a security definer and I confirm all right we have a function so now we need to trigger this function somehow so for that let me go back to our SQL edit and run another trigger method and it's going to look like this so I'm going to create a trigger and this is the name of the trigger this trigger runs on after insert on off. us table for every row and if I run it this is executed successfully and if I go back to my database and check our triggers and choose or you can see that we have a trigger on the or table and this trigger runs on after insert event we have four tables right now and what we're going to do is we're going to create an rless policy on all the tables so what I'm going to do is I'm going to go to table editor and go to profiles and um I'm going to create a new policy and I'm going to start with this one and this is going to be select query any author user can select a profile so that means anyone can see anyone's profile uh it doesn't really contain any sensitive information just the the GitHub profile of the user and I'm going to return true because this is only available for all users authenticated and I'm going to return through and I'm going to save policy and on top of this um I'm going to insert another insert only o users can insert their profile only I can insert my profile on behalf of my logged in session so how do I do that I'm going to add a small piece of code here so here it checks for the user ID or in in our case it's going to be ID of the profile is equal to the o. uid so this is the logged in user so this is the logged in users ID and we check if the ID is the same as the ID of the profile then we go forward all right so I'm going to do a review and save policy let's do something similar for the Post table so we have a new policy and anyone any o user can view any post so I'm going to mark it as authenticated and I'm going to return true and then for insert we're going to do the same thing only or users can insert their post authenticated I've chosen insert and here we use the user ID of the post table and check if it's the same user ID that's logged in user uh and then we do a review and then we save policy so that looks good and let's do the same thing for likes table any orth user can like can see likes authenticated and here it's going to be true and we have a new policy for insert only o user can like on behalf of their login so only I can like a post with my login so that means I can't really like a post as someone else and then I insert the likes table and then I'm going to do something similar for the delete and we check if the user ID is equal to the od. ID and then we save policy the likes and the comments are L are going to be similar and what I'm going to do is um I'm going to select any or user can see any comment and authenticate true and I'm going to save it and for insert the same thing only o users can comment on their behalf authenticated and we check for the user ID before they enter a comment all right so everything looks pretty much as I expect it to be right before we go to the next step let's take a look at our tables and then just ensure that they are proper um for example the comments table should have the ID as uu ID and then it should have two active oress policies the lik table should have ID is uu ID and then should have three active orless policies and post table the same ID should be uu ID and then should have two active oress policies and profile stable should also have ID as uu ID and then should have to active all this policies why are we checking this cuz super base will fail silently if there is something wrong with the schema or the oress policy so that's a reason why we just need to ensure that everything is what we expect them to be in this step we're going to set up our remix app and also all the basic routes and components that we need so let me go to shadine UI docs and then switch to installation step for remix the setup is fairly straightforward so we're just going to Simply follow the instructions so first I'm going to install the remix app all right so we've installed the remix app and then um I'm going to install chatsi in UI let me go ahead and do the next step we create a post CSS config on the route and then we're going to go to our route. TSX file and import tail Vin us Styles and add it to our links function and that's pretty much it and then we're going to install all the components that we need for this we're going to be needing quite a few components let me just list them out we're going to need all this components so I'm just going to install all of them in one go and let's see if this works is expected so for example I'm just going to go to my root route and add a simple component all right so we can see that the shatzi and UI button component it's getting rendered so our setup is ready all right so in Step number two we're going to build all the basic routes that we need for our app so it's going to be index route login route home route that's uh layout route and git post route so first we're going to go to our index route so and our index route is going to look like this so we need to make sure that our index route looks like this for example if I inspect it it's also responsive the layout should change from Flex box to block let me go back to index. TSX file that's this one and then remove this code first thing that we will need is the app logo component so this just a simple SVG that I have and I pass the class name to it and we use the class name inside the SVG item I just made my own logo you can also make your own we're going to use this logo component that's an SVG item in our app so let's go back to index. TSX file and start building our view we're going to have a section so that's a parent it's going to have a full width and background's going to be white then Heights green I want it to be a flex column inside the flex box we're going to have a nav bar so it's going to be a flex box and items need to be centered and I want a small space between the item and we're going to have a padding of four so inside this we're going to have our app logo what I'm going to do is I'm going to use my app logo inside and I'm going to import that for MD and about it's going to have 2.5 RM underneath we're going to have a H1 tag I want the font to be semi bold and it needs to be good poster let's see if if this looks good all right it looks good but then it still needs some padding all right so it needs a proper padding and if I resize you can see that the icon decreases in size all right so that's exactly what we want let's go back to the app and build the rest of the view underneath the nav I'm going to have a div and it's going to be a container and this container is going to be Flex for MD devices in Bow for the devices under MD size it's going to be display block and one interesting thing that I'm going to add is I'm going to give a flex one to it this Flex one enables this item to take up the rest of the vports height and inside that I'm going to have another Flex box and it's going to be a flex column I want the items to be Center y4 I want the text to be Center as well and I want the padding to be four and for empty devices in about I want this item to take half the width of the page inside this I'm going to add a H1 t tag that's going to have a default 3XL for MD devices in the bow it's going to have text 5 XL and I want the font to be bold and I want the tracking to be tighter and inside that I'm going to have a text called a with some space and inside that I'm going to have a pan I'm going to explain you why I need this pan so inside this pan I'm going to add one text that I want to have a background gradient color I want the font to be extra bold and I'm going to add my background gradient colors and the text is going to be transparent and I want a background clip text all right so let's go check it out you can see that we have a beautiful text that looks pretty neat for example on the mobile devices The View turns into a display block and for the desktop it's turning into a flex box and I also want to add some more item right right underneath so it's going to be a P tag with a class Nam text Gray and a top margin of two we have got two spans underneath with a blue font and a green font all right so we've got a nice looking text that looks like that let me go back to my app and add another interesting item underneath underneath the P tag I'm going to add shadu eyes button with as child prop and then underneath that I'm going to add a link so that people can navigate to login view this link comes from remix react for example this is how it looks if you click on this item then it will take you to the login page inside this div apart from the first item that we have we going to have another component that's the card component this card component will have the demo video that we have so I'm going to add some classes to it it's going to have half the page width for MD devices and inside the C we have a Cod content and the class name's going to be just going to give a small padding and on top of that we're going to have a video tag and I'm going to give some classes to it so that it looks nice and it needs to autop playay play in Loop and it needs to be muted and the s source of the video is going to be Source SRC and it's going to come from assets videos demo. MP4 the type's going to be video MP4 all right so now our job is to add a video item I'm going to go into public and I'm going to have a new folder called assets and inside assets I'm going to have videos and inside that I'm going to have a demo video MP4 let's see our view is pretty much complete we have to see if this works and expected so let me go back to our page and check if this works as expected so yeah this is working great and if I click on the join Community it should take us to the login rck and it does exactly the same if I go to the gpost dodev app you can see that the background gradient is animated so we somehow need to animate our background gradient so we just need to add some styles to animate that the first thing that we need to do is that we need to create an animated gradient key frames inside our tailand doc config.js file where we flow through three steps in order to animate the background position of the component and on top of that I'm going to add another configuration for the background size what I'm going to do is I'm going to go back to my index.ts file and go to our div and add a background 300 animate gradient so we still haven't really made this animate gradient yet so what I'm going to do is I'm going to go back to animation and add this field and what this animate gradient class would do is that it will just take this animated gradient key frame and run this in Loop forever so let's see if this works is expected all right so you can see that this is working is expected now we have animated gradient and you also see that we have an error in the console so we're going to fix that so we're going to go to app logo and we're going to add fill rule all right now you can see that our error is gone and and our animated gradient is working as expected now that we've done with our index. T6 file we're going to implement our login. T6 file so for that we're going to come inside our routes and make a new file called log. TX file and we're going to copy the contents of the index. TSX file into the login. TSX file and just rename this to login and we're going to make some changes so the first change that we're going to make is you're going to convert this app logo into a link so I'm going to import the link from remix react and if I click on this app logo I should be navigated back to homepage that's exactly what I'm going to do I'm going to put the app logo inside the link component from remix so that's one and on top of that I'm going to come inside my container I want this to be a flex box but I want this to be a flex column I'm going to update that and I no longer need this video content so I'm just going to remove that and also I no longer need this button components so I'm going to take this and move this inside this file and remove the link from it and inside the button it's going to be a GitHub button and I'm going to update the text so what I'm going to do I'm going to add some text it's going to be login using and then I want to have a break after this and then this text is going to be Hub and then this text is going to be it's going to have a break and it's going to be an discover more and underneath this I'm going to changes to our posts and comments are powered by markon all right let's see how that looks cool so that looks pretty good so what we want to do is we want to have an animated gradient for the card content background so I'm going to take this and paste here and also copy these classes so that I can have an animated gradient all right so you can see that we have an animated gradient I want just to have justify start and I want to give a margin top of 24 and I want this guub to not clip the B so I'm going to give a padding X of one all right that looks good and if I resize everything should work as expected so that looks pretty good and I'm going to remove this with I'm going to install another Library called lose side react this is an icon library and inside this icon Library I'm going to import GitHub icon and I'm going to create a gab icon inside my button all right so it's got a margin right two and a height four and a width four if I refresh you can see this button with a background is beautifully animate all right so we're done with index rod and also the login route now we're going to build our home layout route so how does it look like for example if I go to the G booster dodev um so you can see that this layout is called home layout if I switch between the profile and the git post you can see that the nav bar is not changing at all and also if I resize the page you can see that this is a fully responsive nav bar and it's also part of the home layout so what we're going to do is we're going to build the home layout and use that home layout to load our gpst route inside the home layout I'm going to come into routes and I'm going to create a new route called underscore home. TSX file and you may ask why there is underscore cuz I don't really want this layout to be part of the URL segment so that's the reason why I've used underscore home for now we don't really have any data so we're just going to Simply use hardcoded data we have our main container and inside that we're going to have a Navar and this Navar is going to be sticky I'm going to give top zero and a z index of 50 Flex full width items need to be Center and justify between and padding four and it's going to have a b bottom and I want the border to have a color all right so inside that we're going to have the same um app logo that we had for login page so I'm going to copy this information and paste it and I'm going to import all the components that are required and also add my good posted text inside and I'm going to give a class name for it I want the class name to be flex and items need to be Center and space need to be X2 between these two items all right let's see how this works okay it's it's not found so what I'm going to do is I'm going to first change its name to home and see how the layout looks and then I will change back this information all right so this is the home layout looks pretty good let's go ahead and add more items so on top of this what we want is we want to have a responsive nav bar so for that we're going to have a use State and this use state is going to control the open and close state of the nav bar and we're going to have a button that's shown inside the nav bar and this button is shown only on the mobile screen and for MD and about this button is hidden for example if I refresh and if I go to size that's under MD you can see that we have this button and it's got two different states so this is working on top of that we're going to have a container that's going to contain the name and the logo button and it's going to be Flex Row for MD devices in a bow and we're going to have a space between all the items on the desktop we're going to have space on the xais and we're going to check for one small condition if the nav is open then we need to change the order of the flex items and the width needs to be full for the mobile view and for the desktop it needs to be Auto and when the nav is closed then it needs to be hidden and for the desktop it needs to be a flex box all right so this looks pretty good so there is a container that's going to contain our Avatar logo button and username so first what I'm going to do is I'm going to create a link L that would contain the username and I'm just going to hardcode the information for now and it's it should take me to the link of the username and then I have an avatar URL for example I should have an avatar image so for this I'm going to use my GitHub Avatar URL and hard Cod it for now and finally I'm going to have a button that is a log out button and this button comes from Shi and UI all right so the this is the nav container on top of that I'm going to have an outlet that's going to load the items that are part of this layout and this should come from remix react all right let's see if our home layout works as expected you can see that we have a nice nav bar on the MD view on the desktop and if I resize The View you can see that this is changing but the arrangement is not proper this layout is working as expected but then the whole layout is not taking the full width in order to make it work is expected so what I'm going to do is I'm going to add a flex wrap on the parent but only for the mobile view on the desktop I don't want to wrap so now the desktop looks the same if I resize and then click on open you can see that this container is taking the full width of the nav bar and also moving right underneath the nav bar this is exactly what we want and and what I'm going to do is I'm going to rename this toore home so that I can use this as part of the good post view so we're done with our index route and login route and home layout route now we have to build our gitpost route this gitpost route will be part of the home layout so that means it would looks something like this so it'll have the nav bar that we built in the home layout and underneath the nav bar we'll have a tab that switches between View Post and right post and we have a search item that will enable us to search the list so for example if I type something here you can see that there's updated the search query parameter that we have on the location and on top of that we have post that will enable us to view the number of likes and view the number of comments on each post so we're not going to build a list rather we're going to build all the components that you see on the screen so that we can build the list in the next steps so first thing I'm going to do is I'm going to create a tab for View Post and right post let me go ahead and create a new route and this route should use the home layout so I'm going to use uncore home. GP pos. TSX and and this will load inside the outlet of the home layout so that's the reason why we have this I'm going to have a top level container and this is going to be full width and this is going to be full width and the max with is going to be XL and padding X4 and flex Flex column and inside that I'm going to have the tabs component that you just saw and the default value it's going to be View Post and class name is going to be margin Y2 and I'm going to import the tabs component from our component folder and inside that we're going to have a tab list and this and this is a grid that's got full width and it's got two columns under that we're going to have two more components that's going to be tabs trigger for View Post and tab trigger for right post let me just import everything that you see here all right I'm just doing all the missing ports and that looks good now we're going to have two content so the first one is going to be the content for the view post and then the next one's going to be the content for right post and inside the Taps content I'm going to have a separator and then I'm going to have a component called post search component which we will build shortly uh so for now we're just going to build only one post and inside that post we will pass couple of children view likes and view comments so these two components will show the number of likes and the number of comments and inside the right post we're going to have a component called right post and that's pretty much it so let me just import all the Imports that I can import and then the rest all have to build the components so first let's go ahead and build the post search component so the post search component is an interesting component um let's understand the behavior first it's a standalone component and it just mimics the behavior of a simple HTML form you might have used form tag with post but if you do not specify the method then the form tag would have get as a default method on it and this get would convert the input's name and value into search query params on Submachine so if the input has a name called query and then any value that it has will be used as a value inside the search query parabs and not only that any form action would trigger a page navigation event so your page will be reloaded in our case we want to submit the form with a debounce of 250 millisecs meaning we wait until the user stops typing for 250 milliseconds now that we're clear about the component design and the behavior let's go ahead and implement it so this is our post search component let me just go give this a name and it's going to return a top level container and it's a div and the class name is going to be Flex justify between item Center and margin Y3 and inside this St we're going to have a h2 tag and this is going to be for desktop we're going to have text XO and it's going to be font heading and font semi Bold and the width is going to be 7x 12 so that's roughly 60% inside that we're going to check for query which would come from a state and if it's true then we're going to show this text if not then we're going to show all Poe and underneath that we're going to have a div that would contain the loading spinner when the search is happening and I'm going to give it a flex and withd 1 by 12 and justify Center and inside this we're going to check for another variable called a searching and if it's true then we're going to show the loading spinner and the loaded to comes from loose side react and finally we're going to have the form that we talked about it's going to be a simple form get and inside that we're going to assign a role to this form it's going to be search form and we're going to pass a ref which we will use to get the handle of the form and we will have a simple ID and then we'll have a class name and this is going to take a width of 4X 12 and it's going to be a flex box on check change we will call the submit method because we are not going to let the form submit itself because of the fact that we're going to intercept this event and submit the form on our own so for that I'm going to import a use submit Hook from remix react so let me just come back to this in a bit after I implement the input field and inside the form we're going to have an input field I'm going to assign a type for this and it's going to be search and I'm going to assign a name for this and I'm going to call this query and the value is going to be query and on change we're going to set the value of the query so query is going to be a state variable and placeholder is going to have search posts to it all right so now we have to add the state variable and the post search component also accepts two properties the first one is going to be search query that's a string if it's null then we assign an empty string and the next one's going to be is searching so this is a FL flag that comes from the parent and based on this flag we either show or hide the spinner and for form ref we have a ref item and we also have another ref for tracking the timeout so this timeout is going to be used inside the forms on change so we will check if the timeout is active and if it's active then we clear the timer and then we set the timer for for for 250 milliseconds what this set timeout function does is it will check for the ref of the farm and call the submit with the form breff and this would just intercept the form submission and then just takes over entire control on top of that we're going to have this use effect so that we can clean up any cleaned time art references so this component looks pretty good so now we have to pass search query and a search from the parent so on the parent I'm going to have a hook called navigation hook so what this hook does is this hook tracks when the navigation event is happening so I can simply use this navigation and check if a navigation event is happening this means that I'm typing something in my search field at the time I want the is searching to be true so that I can show a loading spinner and I'm going to pass a searching to be true and I also have to pass search query I'm going to pass an empty search query CU this needs to come from the loader all right so let me just comment out all the other components and see if this post search works as expected so this is our get post page and the items are not aligned let me just align the items in the home layout uh I'm going to go to home layouts and then pass item Center I assume this would Center the items right that's great and we only have two components right now we have the tabs component and we have the post search if I just do a search operation in this field then I should be able to see the search query params updating with the value all right so you can see that this is working as expected so if I type something it's updating my search crew params and it's also de balancing as well R is typing something so yeah so this is working is expected that's great so we have buildt our post search component successfully but there is one small problem so for example if I make a search you can see that the search query paramet is getting updated but the page is not getting reloaded all right so that's the problem so for that what we're going to do is we're going to add a loader to this page I'm going to add a simple loader and what I'm going to do is I'm going to extract the search query params from the incoming URL and I'm going to return it as a Json object and once I return the data from the loader I'm going to use use loader data hook to access the data on the client side and I'm going to pause the query information in place of the empty string that I have okay so now if I go back to my app and reload the whole thing if you take a look at the networks tab my form is making a navigation request every single time I type something so this is exactly what we want you can see that every time I type something my form is making a navigation request so with this our post searches complete with a form navigation event all right so now that we are done with our post search component let's go ahead and build our post component and our view likes and view comments component I'm going to go into my components and then make a new file called post. TSX and first thing what we're going to do is we're going to build the props for our component our post component it's going to have an avatar URL it's going to have a name and it's going to have an ID and it's going to have a username and a title so that's the actual content and it's going to have a daytime string and it's going to have a user ID and it's going to have children that looks good and let's use our props and build our components so this is our components so we've just used all the props that we need and this is going to be a card component and inside the card component we're going to have a class name and we're going to style this whole card so it's going to have rounded borders and we also going to have a shadow type of FL should be hidden and we have a minight of 12 RM and underneath that we're going to have a flex box and I'm going to have another diff that's going to have a padding of P4 and for desktop it's going to be p8 and wd's going to be full and this is going to contain a flex box and I'm going to Center the items and justify between and inside this we're going to have our first item and that again is a flex box that's going to contain the Avatar Euro so that's the first item and the next item is going to be another div that would contain the name of the username so the first one is going to contain the name and the text is going to be small for our small devices and for desktop it's going to be LG text large and I'm going to add font semi bold and inside this what I'm going to do is I still want this username to take me to the profile of the user so what I'm going to do is I'm going to add a link that would take me to the profile of the user based on the username and this link needs to be imported from from remix react on the same lines I'm going to have username I'm going to give a margin left of four and this is going to be text MD for a desktop and I want the text to be a bit lighter in color so I'm going to assign a gray 400 and right next to this item I want to have the app logo all right so that looks good the next div that I'm going to have is going to be the react mark down what I'm going to do is I'm going to give this margin top and the text needs to be slightly darker gray and text needs to be small and we're going to have the pros class so this Pro class comes from the tailin typography which we're going to implement in a bit before that let's just uh wrap our title inside react markon all right so we have to install react markon so I'm going to do that all right that looks good and right underneath this I'm going to have another div this div will hold the daytime string and also the likes and the comments button so let me just give a class to it I'm going to give a margin top six and items need to be Center and this will contain do class name is going to be flex boox and I want some space between the buttons and the text going to be gray and I'm going to pass the children item here and right beside that I'm going to show the datetime string and the class name is going to be text Gray 400 and the text needs to be small all right so for now I'm going to make the children optional and see if this works so I'm going to import the post component and then I'm going to comment out the children now I have to pass all the props manually all right so now I just have to return the children item from the post and let's see how this component looks this component looks great the only thing that I have to do is I have to install the Tailwind typography so I'm going to go back to my app and add this as part of my tailin plugins list now you can see that my posts are in markdown and if I go and change something and my title for example if I just go down to my title and then changes to a subtitle you can see that the change will be reflected all right so that's great so we've just implemented our post component and we still have to implement our view likes and view comments button that's exactly what I'm going to do I'm going to create a new file for view likes and I'm going to create another file for view comments inside my components folded and the view likes needs some properties we need to have the number of likes and then we need to have a flag to check if this post is liked by this logged in user and we also need a path name to navigate to when user clicks on the like button so now this whole thing is going to be a link and I'm going to extract out all the properties from The View likes props it's going to be liked by user and then path name and inside the link I'm going to pass the path name I'm going to add a class name to it it's going to be Flex justify Center item Center and I'm going to add a class called group uh this will be used when we do a group hover in tail BND inside the link I'm going to have a star icon and it's going to have the following class name so it's going to have withd four height four text blue 700 and a group hover that's text blue 400 but only thing that I want to do is if it's liked by this user I want the text to be blue if it's not liked by the logged in user then it needs to be gray so for that I'm going to add a small condition called liked by user if it's liked by user then it's going to be a blue text if it's not liked by the user it's going to be a gray Tech all right that looks good and underneath that I'm going to have a span and the span's going to have a class name a margin left text needs to be small group hover should be text blue and the same style that we just applied here all right that looks good let's go check this out I'm going to add this to my component here I'm going to pass view likes legs are going to be 69 and liked by user it's going to be true and path name it's going to be for now I'm just going to hardcore this path all right that looks good I'm going to import this component from the component's folder now I'm going to run the app just to see how this looks cool so that looks great but we don't see the numbers so we just have to use likes here all right that looks great now our job is to build our comments component which I'm going to do right now view comments let's define the props for this component let's say comment it's going to be number and we have a path name similar to The View likes and we have another flag called read only if this flag is true then this view components it's going to be non-clickable so let's just build our component if this is false then it's going to be a link that you can click so if read only is true I'm going to return Dev and I'm going to have class name Flex justify center items need to be Center and I'm going to add a group class for our group hover and inside that I'm going to have a message Mage Circle and class name is going to be height four with four and text Gray 500 right next to it we're going to have a span that carries a number of comons and let me give a class name to it it's going to have a margin left of two and we're going to have a text small and text Gray 500 if this is false then it's going to be a link component let me just quickly copy the information from my view likes let me extract out all the things from the props Commons path name read only and the Link's going to come from remix react and instead of the star we're going to have the icon is going to be message Circle and the class names going to be height four with four text Gray 500 and group hover it's going to be text green 400 and in this case we don't really have a comment by user flag so we're going to simply remove these classes and then just add the same class that we just have here let's see if this works as expected so I'm going to go back to my get post view comments component and I'm going to pass the comments that's going to be 420 path name is going to be the same and that's it let's see I'm just going to test this out just to see if this works cool so if I place my cursor on top you can see that we have a group hover style applied and the same thing for our comments if I click on them it should take me to this path that's also working as expected and let's do the last thing we just have to build our right post component all right I'm going to go into components and then create a new comp component go WR post. TSX and let's build the props for the component right post props it's going to accept few things so it's going to accept the session user ID which we will see later so this is the ID of the user who's logged in and we're going to have the post ID that's a string and that's it for now so I'm going to export function right post and extract the properties from right post props it's going to be a simple card component the card's going to have a card header and inside the card header we're going to have a card title and the title is going to be right post and on top of that we're going to have a card description that looks good and underneath the card header we're going to have a card content and inside the card content we're going to have a text area which is going to have a placeholder and it's going to have a value that's a title and we're going to have an on change that will set the value of the title and we're going to have a class name of mb2 we're going to save the value of the text area and a state for that we need to have a simple State let me go ahead and create it and let me import my card content and text X area components all right that looks good and we're going to have a card footer and the card footer is going to be a button on click we need to post this information to the server site let me just import the button and the post title is going to be a function this will post the information to the server site and we also need one interesting thing so that's a reason why we have the state when we type thing in our text area we want the text area to grow so that is a reason why we have the state to track if the user is typing something so for that we're going to add a simple ref and that will give us the handle of the text area ref and I'm going to pass it inside the text area inside my use effect I'm going to have a simple function that will make the text area grow as we type and I'm going to pause the title as a dependency first we're going to check if the text area ref is valid then we're going to set the style height of the text area so first we're going to set it to inherit and if there is some content inside then we're going to pass get computed style then we're going to pass the current ref and we will calculate the height based on the scroll height of the text area and we will take this height and set the height as the current height of the text area I need to mark this is pixels just it's going to be height plus pixels all right so this would ensure that as we type our text area will grow and when we delete something it will just come back to inherit all right let's see if this works so I'm going to import my right post item session user ID it's just going to be something random for now and post ID is also going to be something random for now all right that looks good let's see if this works as expected as you can see we have our post item that looks good and then if I switch to right post I need to see my right post item so it's not appearing let's see what's wrong we just have to fix this value and then if if I go back you can see that we have our view post and right post I can switch between these two tabs and if I type something you can see that this text area grows in size and that's exactly what we want cool so that looks great with this we've done building all our basic routes that we plan to build in this step and also we build all the basic components that we need for our app in step five we're going to set up super base for our app so what we will do is first we will download the superbase database types locally and we will use those types to create our super based clients so let's go ahead and do that so for that I'm going to go back to my app and I'm going to run this command called npx superbase login once you run this command it'll install the superbase library that it needs and then it will ask you to enter and it will just authorize you based on your login session that you have and if you go into the access tokens you can see that you have a personal access token that's been created for you that's all you need if this approach doesn't really work I would recommend to go to super base here I login and just simply follow the instructions there once we've logged in we're going to run this command called npx super base generate types typescripts and the ID of the superbase project that we have so this one comes from our superbase reference ID if you go into the settings on your dashboard you can see that there is a term called reference ID you simply have to copy that and use that inside this command so if I run this Command right now I'm going to have a database. types. TS file on my root folder all right so let's see if this worked if I go to my root folder I've got a database types. TSI this will enable us to have a fully typed database access with our superbase database in Step number two we're going to create two clients the superbase browser client and the superbase server client you may ask why do we need two different clients actions like login and log out which are primarily client side actions will be handled by browser client for authorizing users to access certain parts of data and to fetch data inside loaders we will use a Ser a client right before we go ahead and create our superbase browser client we have to create cre our DOT end files with the appropriate end variables inside the do end we're going to have a super base URL and we're going to have a super base and on key you can find this information under API settings um inside your dashboard I'm going to copy this information I'm going to paste it as part of the uro and I'm going to go back and I'm going to copy the unon key I'm going to come back into my app and inside the lib I'm going to have a new file called superbas dots this will be our file for our superbase browser client right before we go further I'm going to install two libraries the first one is going to be at superbase superbase JS and the next one's going to be at super base SSR so I'm going to install these two libraries once I install these two libraries I'm going to import create browser client from superbase SSR library and right underneath I'm going to import session from super base I'm going to import super base client as well on the client side I'm going to use a hook I'm going to use my own hook and I'm going to name it use super base and the type is going to be use super base and let me Define what I want inside this type the first thing that I want is going to be the envirment variables and I'm going to call it super base and and then next I'm going to need the session it's going to be either session or null the super base n is going to be fairly simple it's just our n file typed and I'm going to extract these two information inside and I'm going to create UST State hook and inside my US state hook I'm going to create my browser client and use a database type from our database types. TS file and inside here I'm going to pass and. superbase URL it's going to be nonn and super base a non key and I'm going to import use St all right so why do I have to create this inside my UST State callback function because of the fact that we want the superbase to be a single ton that means uh it'll always have one instance throughout your app life cycle once done we just have to Simply return the super base client this would work for most cases except for one case so we don't really have a mechanism to find out if the user is logged out on the client side so when the user logs out we need to kick them out of our app and navigate them back to the login page and for that we're going to have a simple method inside our use effect that will run on the superbase o State change let me just pass the super base. off inside and I'm going to get the data from the sub base off Doon or state change method and inside this method I'm going to have an event and also the session that's on the client side what I'm going to do is I'm going to check if the O token that's on the client side is not equal to the server access token then we're going to revalidate the app at the time of the revalidation the server will check if the client has some session if not it will redirect the user to the login page now we need a server access token and that we will get from the session that we passed so there's is a server session and I'm going to name it as such server session and this is going to be serve a session all right that looks good and we also have to revalidate the app for that I'm going to use a REV validator from remix and I'm going to type revalidate do revalidate this would call all the active loaders on The View and when we hit a loader then we will also check if the user logged in or not if not logged in then we redirect them back to the login page and I'm going to add server X token and rev validator as a dependencies we're going to extract the data information and from there we're going to extract the subscription that we have on this call back and we're going to return a cleanup function on our use effect unsubscribe with that our super base client is nearly complete for the server client we're going to take a look at this documentation called oo with pkce flow for SSR the first step requires us to create the superbase server client in our app we've just installed all the libraries that we need we've set the environment to variables and the third thing that we have to do is we have to create a client inside our loaders we are going to use multiple loaders so we have to have one common function that does this job for us so for that I'm going to go back to my ad app and create a new file under the lips folder and I'm going to call it superbase server. TS file if I have dot server as part of the file nameing convention then this file will be available as part of the server bundle and you can't access all the information on the client side so we want this client to be only on the server side so what I'm going to do is I'm going to create server client from superbase SSR and I'm going to import the database type from the database. types and once done I'm going to get the superbase end information from our end file and after that I'm going to export a function called get super base with headers and copy this whole information inside a file and import all the missing inputs from super base SSR and that's pretty much it so right now I just have to return the super base client and also the headers with the latest changes in this Library you have to return the headers so that means all the loaders and the actions will also return the headers and I'm going to create another function and I'm going to name it get super base with session and headers I'm going to use the client information to get the session from the super based client and I'm going to return all of them together session headers and the super based client so this is not required I've just made this API just to make my life easy I'm going to mark it a serice session cool that's pretty much it for this F and and I'm going to go back to oo with pkc flow and I'm going to do the next step so for remix we have to create an off. callback TSX route in order to do the pkc check so what I'm going to do is I'm going to create a file inside my routes and this is going to be a resource route so I'm going to call it resources. o. callback TSX file and I'm going to paste pretty much everything that I see here and you can see that we don't need this information we've already created our utility method so what I'm going to do is I'm simply going to import all the information from our super base with headers method and in inside here I'm going to redirect the users back to the login page so our super base server is complete our superbase browser client doesn't really have access to the environment variables or the server session so somehow we need to pass them on from the server all the way to the client side so for that I'm going to go to root route and I'm going to create a loader and this will access the session the server session and the headers from our method so let me import get superbase with headers from superb. server file all right so we get access to the server session and the headers I'm going to return the information in form of Json so the first one is going to be server session and the next one's going to be the headers just like how you've seen in this documentation we have to return the headers all the time not only that we also have to return the enironment variables from the server side don't worry these are safe for us to use also on the client side so what I'm going to do is I'm going to get the super base n from our server file and I'm going to return the n as well all right that looks good and now what we have to do is we have to get the session information on the client side so we're going to use the use loader data that will grab the data that the loader has returned and using this we're going to create our super base browser client now the super base needs to be available when the user tries to log in so for that we're going to create something called as context when we pass something onto the context of the outlet we have access to one hook called use Outlet context and that hook will contain this super based client value so we need this information inside our login route let's see how we can do this inside our login route we're going to have our super base returned from the context use Outlet context and we get access to the super base but the problem is the type is unknown so that's a problem so that means we need to pass a type that can keep the superbase client typed so we're going to go back to our super base. TS file and create a type super based client so for example and let me fix all the missing Imports and I'm going to export this type put a super base Outlet context type and inside I'm going to have super base type super base client and and that's pretty much it now if I go back to login and I pass this super base Outlet context type now all of a sudden I have access to all the super base types that I need so now my job is to create a login method I'm going to call it handle signin and this method is going to look like this let me just go back to the pkc flow and this method is just going to look like this and it's going to be async method and we're going to use only GitHub or for now so I'm just going to mark this as GitHub and we have to redirect back to our resources. off. callback method so I'm going to update this to Local Host 3000 and resources off. call back this is not nice cuz we've just hardcoded the value so what I'm going to do is I'm going to go to my n and I'm going to add another information called domain URL which we can change later and inside my root route so inside my loader I'm going to access my domain information and I'm going to pass it on down to my components and I'm going to pass this along with the superbase client that I have all right so we just have to to fix this type information so I'm just going to return an object and if I go back to Roots that's fixed and I also have to access domain Euro from our loader now if I go back I should have this available and I'm going to use my domain URL and use this to finish my pkc flow my job right now is to just pass this handle signin information in place of my login also have to fix this one last item I'm going to pass the domain URL to the super base Outlet context type and now typescript has stop complaining so that looks great with this our server client is ready all right so now we have come to step number three so in the step number three what we're going to do is we're going to implement the GitHub o and successfully log in a user for that I'm going to come back to my dashboard and take a look at this section called authentication inside the section I've got the configuration for all providers inside there I'm going to scroll to GitHub and then click enabled and I'm going to go back to GitHub go into settings and then developer settings I'm going to click on new orth app I'm going to give this G poster stage and then the homepage is going to be Local Host 3,000 and I wanted to land on good post and the authorization call back is going to be this one that you see and I'm going to paste it and I'm going to click register application and once I do that I should see something like this and I'm going to copy the client ID from here then go back to my authentication Tab and paste my client ID here and then come back here and then click on generate new client secret and I'm going to copy this information and then paste it here and then click save and the next thing that we have to do is we're going to go to the URL configuration and inside this URL configuration you should have the redirect URL that you have on the login function and I'm going to save it that's saved inside the login rout I have a loader inside the loader we have this method that gets the headers and the serice session from our method if the service session is already valid then we should redirect to git post and we pass in the headers if not then we just stay in the same page and use a can login and I'm going to copy the same method and put this loader inside our index route that means if the user is logged in then redirect the user back to the git post page and I'm going to come back to git post use the same approach but in this case what I'm going to do is I'm going to use the session if it's null then I'm going to redirect the user back to the login page on top of that I'm going to come inside my home route and then I'm going to access the super base and here you can see that I have a method called handle sign out this method simply calls the super base off API and signs the user out and I'm going to use this handle sign out as part of the onclick Handler of this button now we just have to test if this login logout flow works works as expected let me go back to the app and try to go to get post you can see that I'm being redirected to the login page and that's exactly what we want and if I click on GitHub ooth method then the ooth app kicks in and then I authorize and I'm taken back to the git post page that works great and if I refresh then I still stay in the good post page so that means I don't have to log in again until I log out and if I click on log out you can see that I'm taken back to the login page and if I try to enter get post page then I'm redirected back to the login page cuz I don't really have a valid session and now if I click on GitHub then I'm logged in to my gitpost route that works great and our GitHub o flow is complete all right so we've come to the step number four we're going to create a database access layer for our loader and action and we're going to get the post information from the server side and display that on our client side so for that I'm going to go to my super base dashboard and do an insert row on the post I'm going to go back to my app and inside lib I'm going to have a database. server. TS file we have this method so that we can have apis on top of our database layer so that means I'm going to export the first API to access all the posts and this is going to accept a database client database client it's going to be superbase client and the database and inside this we're going to have a post query database client. from post table we select all the information along with that we also get the author information from the profiles table and we get the likes information from the likes table but we only get the user ID from the likes table and we get the comments information where we get all the comments and we also order by the latest post to be the first and we're going to run this query and I'm going to get data and error from this query and then I'm going to do an AIT of post query and I'm going to return data and error and inside if there is an error I'm going to print the function name with the error and I'm going to use this information inside my gpst route I'm going to use the data from the API oh wait get all post with d details and we pass the database client that's the super base that we get from this method all right that looks good now we have one more job we'll have to know if the post is likeed by this user who's logged in so for that we're going to get the user information from the login session let me go back to lib and UTS and inside U I'm going to add a small function that will extract the data from the super base server session so it'll just take the server session and then get all the information that we need from the service session it will have the ID of the logged in user and the Avatar Euro and the username let me import this function and I'll have user ID username and user Avatar Euro all right so now our job is to map the post that we get from this API with session user ID so that we can find out if the post is liked by the user so for that we're going to go back to our lib .ts file and then we're going to create a new function that would combine post with likes this should accept the data obviously and the next one is going to be the session user ID that's going to be string and this data is actually the post with details data that we get from the service site and we have to define the type of this one let's go ahead and do that I'm going to create another file called types. TS inside my lip folder inside this type I'm going to import the type from the database and I'm going to extract all the types from the database row so the first thing I'm going to do is I'm going to extract the post type from database public tables post row and then I'm going to extract the profile type and then I'm going to extract the common type and then I'm going to have type for the likes and it's going to be a simple user ID and what I'm going to do is I'm going to export the type post with details and it's going to combine the post type with all the information that we are trying to access from the database this type is the data that we get from the database now let's go back to utils and then import this from our types and it's going to be an array and then we're going to check if the data is valid and map the post return post and we're going to create another flag called is liked by user so what we will do is we will check if the session user ID likes any of the posts and we get the post. likes. length so we just get the total number of likes from the post and also we get the total number of comments and we get the author from post. author if not we return an empty array and we return post all right that looks good now I'm going to use this method inside our get post and I'm going to get the post information from this method combine post with likes I'm going to pause the data and I'm going to pause a session user ID and I'm going to import information from utils I'm going to go back to this data and then pass null this could either be an array or it could be no I'm going to use these two items later and I'm going to return my post information back to the client side and I'm going to get the first post and I'm going to see if I get any information from the server side all right so now if I load this route then I should see my post being printed in the console all right let's see if this works you can see that I can see a post I've got all the information that I need in this post so I've got the author information I've got the comments information and I've got the likes information as well and is liked by user false let's see if we can map this information to our post component so I'm going to use post. author. Avatar URL and then I get the post. author. name and I get the post. author. username and I get the post. title user ID is not required but then still I'll just add it and daytime string which we will adjust later and likes is going to be post. likes. Len like by user post. is like by user comment is going to be post. comments. length and that's pretty much it if I go back you can see that I get my post information from the server side and that's great all right let's also fix the date right before we wrap this section up I'm just going to add a utility function it will just get your datetime string from the server side and then just convert it to readable string that contains the name of the month I'm going to use the format to Twitter date method inside my good post and I'm going to use the post do creat that all right right so now you can see that this post is exactly the post that we have on the serice side if I go back to my post and add a like to my post so for example let me just insert a like to my post I'm going to choose my user ID and then I'm going to choose this post and then save then if I do a refresh you can see that I've got one like on my post that's great everything is working is expected in this section we're going to build our git post routes completely so as a first step we're going to build an infinite craw for our post that means we would show a certain number of posts when the user loads the page for the first time as they scroll we will load more pages and this also means that our apis need to be paginated and for this we're going to use a Library called react virtuoso and we will take the example from this endless scrolling section in this section of the document you can see that we have a local state and as we scroll we load more items and push these items into our local state which is rendered on The View and not only that a virtual list is an optimized list that means we don't render all the items in the local state but only the items that are in The View port to have a performance virtual list we will also refer to this performance section and build a memorized post item for each of our posts there are quite a few libraries out there in the market but this one is the best in my opinion so with that being said this component that we're going to build is an interesting component for us so we're going to split this into two subc components the first part is going to be a hook that supplies all the post that we fetch from the server side and and also a flag that tells if we have more pages to load and also a call back function that fires to fetch more pages from the server side and the next part is going to be an infinite virtual list that uses the react virtual component with the output from the hook let's go ahead and build the hook first and then the component all right before we go ahead and build our hooks let's just add pagination to our database access layer so what I'm going to do is I'm going to have a page and a limit item to my get all post with details function and inside that I'm going to Mark page as a number and the limit is going to be an optional that's also going to be a number and since this is optional if you don't pass it then we're just going to assume that it's going to be 10 this means that 10 post per page and inside this method I'm going to come down and add a range so the range is going to be page minus one into limit so that's the start and then this is going to be the end all right so that's our range that we're going to get back from the service side and on top of that we still need to know the total number of posts that are are inside our table so for that I'm going to use account exact field as part of my post query I would also get the count field back from the service sign and with this count I'm going to calculate the total number of pages that we have so the total posts are going to be the count and we're going to return the limit as well and we're going to calculate the total Pages based on the count the total pages are going to be if there is Count then we're going to do a matad c and the count divided by limit if not it's going to be one so for example if we've got 70 posts and then this is 10 then that means we have seven pages now now I'm going to go back to my components and then make a new file use infinite B.S so this is going to be a hook inside this I'm going to accept two parameters the first one is going to be the incoming post and then we're going to have the total pages I also need to type this information so I'm going to use use infinite pose as my type and I need to type my post that is the incoming post and I need to type the total number of pages that's going to be a number so in order to type this incoming post what we're going to do is we're going to go back to our types. TS file and add this piece of code based on the function that we have so what we're essentially doing is that we're just trying to get the function and then we're trying to return the return type of this function and I'm going to use this information and then create my type and I'm going to import this type all right that looks good inside this method I'm going to have a local state and I'm going to pass incoming post after this I'm going to have a use fetcher this use fetcher would be used to fetch the paginated data from our database and this is is going to be the data that we get from the post loader the post loader is going to come from this route I'm going to call this post loader I'm going to fix this type information so this is going to come from remix react and then we have a state for tracking which page is our current page and below that I'm going to have my flag it has more pages this would be equal to current page if it's less than total Pages then we have more pages to load and then on top of this I'm going to have a load more method that would first check if you've got has more pages and we check if the fetcher is not fetching right now then we trigger a fetcher. Lord and then we're going to pass the page is equal to current Page Plus one so that means we need to load the next page and how do we get the output so this is not an async function this is just a trigger and the fetcher would come back with some data and that data will be available inside our fetcher. dat method so that we need to track from use effect then what I'm going to do is I'm going to set the post if the data comes back with more post then I'm going to append the new post with the post that I got from the fetcher once done I'm going to update my current page let me import my use effect all right that looks good and I return the post information load more information and has more pages information from this hook after this we're going to create our component I'm going to go to components and then create my infinite virtual list and we're also going to install the react virtuoso Library I'm going to come back to this file and export my component and it's going to accept total pages and incoming posts as props the total p is going to be a number and incoming post is going to be combined post with authon likes type let me just fix all the Imports all right that looks good and inside a method I'm going to call my use infinite post hook with incoming post and total pages and I'm going to get my post load more information and has more pages all right after this I'm going to return my virtuo component and I'm going to import that from react virtuosa and to this component we're going to pass some properties so the data is going to be the posst and it's going to use window scroll and initial topmost index should be zero for us and after the end is reached we're going to call this load mod function and initial item count is going to be five so if I place my cursor on top of this you can see that this is used for server side rendering if set the list will render the specified amount of items regardless of the container item size so we always want five items to be pre-rendered all the time and we are also going to set this over scan what this over scan does is it's going to render up to 500 pixels more than required for the viewport so that it feels a bit smoother and inside item content we're going to get the handle of index and the post and if the post is not available then we're going to return an empty div and if the post is available then we're going to return a memorized postless item and that's going to accept the post information and on top of that we're going to have a component and this component is going to return the footer this footer is going to enable us to load more items when we reach this item at the end of the page if there are no pages to load this item is going to be null if there are more pages to load we're going to return a Skeleton item all right so now we have to implement two components the first is going to be the memorized postless item and the next is going to be post skeleton let me also pass index all right so now I'm going to go back to my components and then build my first component that's going to be memorized post list item and inside this component I'm going to export const memorize post list item and this is going to use the memo method from the react library and inside this method I'm going to extract my post and index information that I've just passed and I'm I'm going to have this post and I'm going to type the post and also the index that's the number and I'm going to return the component so we have to type the post item for that I'm going to go into my types. DS file and then paste this type so this type is just going to Simply get this combined post with author and likes type and then gets the type of the first item and I'm going to go back to my mem I post this item and use this type so it's combined post not posts all right that looks good what am I going to return here so I'm going to go back to my home get post page and get this whole item that you see here pass this as the return type for the memorize list item and let me import all the missing Imports I also have one issue that I have to fix I'll have to add a display name to my memorized postless item and let me fix all the Imports all right that looks good that looks great now let me go back to my infinite virtual list post and import this item and then we have to create our post skeleton so I'm going to go back to post and Export another component called po skeleton this component is going to be a div and class name's going to be it's going to be a flex box I wanted to have some space on the x- axis and then Min height it's going to be the height of the post and margin Y3 and padding of eight and inside this item I'm going to use the skeleton component from shardene UI and I'm going to assign a class name to it Height's going to be 12 with it's going to be 12 and rounded going to be four so this is for the Avatar skeleton and underneath that I'm going to have a skeleton for the text and inside this I'm going to have two different skeletons for the text items and I'm going to go back to my infinite virtual list and I'm going to pass my skeleton component so we have done with our infinite virtual list now let me go back to my G post page and fix some of the issues that I see here I no longer need all these items so I'm going to remove all of them and our API needs to support the page Nation so I'm going to get the arguments from the request search perams page number is going to be also from the search perams so I'm going to pause the page number and it's going to be s not a number we are trying to convert the string into a number so it could also mean that we end up in Nan so I'm going to pass this method just to check if it's not a number then return one if it's a number then just use a page and it's time for us to use our infinite virtual list inside our component so I'm going to import my infinite virtual list component and I'm going to pass post information and I'm going to pass my total Pages information let me fix both of them so I'm going to return the post and I'm going to return the total Pages as well the total pages are going to come from this method and this is going to be incoming post all right so that looks good let's first uh add some more items to our database so for example I'm going to go back to my dashboard and inside my dashboard I've added about 50 random posts for my post table if our limit is going to be 10 then we should have five pages all right let's see if this works I'm going to run npm runev and then go to my pages yeah and if I scroll down everything is working so I can load all my 50 pages and if I go to the networks Tab and try to load again you can see that as I scroll down I get data for second page third page four fourth and fifth page and also sixth page and the best part about this is that if I just try to check how many lists are on the viewport you can see that only 1 2 3 4 5 6 7 eight items are rendered on the viewport so even if we have 50 items in our local state we don't render all of them we only render the items that are required for us all right so with this we are done with our first item in our step six in the next item what we're going to do is we're going to implement search on the post right now so if I go to our app and do a search for example 19th you can see that the search is happening but then nothing's coming back from the server s side so we have to fix it I'm going to go to my gpost TTS file I'm going to get the query information from line number 24 and passes on as another field to our get all post with dils method inside the database. server. file I'm going to receive that search query and I'm going to define the search query as a string or null and right underneath my PO query I'm going to check if there is a search query if there is one I'm going to call an I like function on top of my post querry and check the title information against the search query so this is going to be case in sensitive this will return the matching items of the search query with the title column let's see if that works all right so you can see that this works but if I do a search it's not updating so every time I search the good post route gives back a new post information from the loader and we also need to update that information here if you go back to the documentation of the react you can see something like this so for example there is a section in the documentation that says adjusting some State when prop changes you may be tempted to use use effect hook but instead you got to use something like this so we're going to follow the same approach I'm going to copy this information paste it right here so on the change of incoming post I want to reset my post information and set post need to have the new incoming post so I'm going to call this previous post and and this is going to be set previous post and this is going to be incoming post if the incoming posts are not equal to the previous post then I'm going to set the previous post values to the new incoming post and these two states I'm going to reset so the state and this state will be reset so the Set current page is going to be reset to one and set post is going to be reset to incoming post all right let's go and check if this would fix our problem now if I do a search everything is working if I change from 14th to 19th we see that the page is getting reloaded and if I search for something I get an Mt screen all right let's fix that all right so if there are no post items inside the infinite virtual list then I need to show a fallback component if there are no post and I'm going to return a div class name should be Flex justify Center item need to be Center and height is going to be half the viewport height and inside this I'm going to have my app logo component and I'm going to have a class name Heights going to be 10 and bit is going to be 10 and I'm going to have a text that's got a margin left of two and that says no post found all right let's see cool now if I search for something and then there are no post then I get this information cool so there is one more thing if I have a search term right here and then I do an infinite crawl you can see that this search term is not part of the fetcher do load so we have to fix that let me go back to my use infinite post and inside I'm going to have an item that keeps track of all the search query params and I'm going to import another Hook from remix it's called location hook and it's going to be use location and if location. search it's got some value in it already we have to ensure that we keep the value so location. search and then to that value we're going to append our page information current page + one if not then our page is going to be the first search query params okay that looks good now it's time for us to replace this information let's take the location the path name from the location information and then pass this information let me reload the app and have a search term called post now if I do an infinite scroll you should see that the search term should be part of our infinite Scrolls search term yeah that seems to be working and if I search for something thing that's unrelated we should see our app logo with no post font all right so with this are step number one and two of working is expected all right so we have come to our last step the last step is going to be use post action for write post component so we need to be able to submit our post to the server s side so let's go ahead and do that for this I'm going to create a resource route for our right post so I'm going to create a new file I'm going to name it resources. post. TSX and we will use this route to create the post I'm going to name this Asing function and it's going to be an action and I'm going to get the request fail from action function ARS I'm going to get all the super base information from this method and I'm going to use that here if the session is not valid I'm going to redirect back to the login page let me fix the Imports all right that looks good and I'm going to get the form data from the request and I'm going to extract the title information that would come from the form data and I'm going to convert this to two string and also I'm going to get the user ID from the form data and if the title information and the user ID information is not available then we're going to return and error and if they are available then we're going to pass the information onto an API call create post that will accept the database client user ID and title information so the database client is going to be our super base and we have to create this async method inside our database. server. file let me go and create a method that accepts all the three parameters so it's going to accept the database clients it's going to accept the user ID and it's going to accept the title and we're going to pass this information onto the database client and we're going to do an insert inside the post table and we're going to return the error if it's available all right let's go back to our post and import this information create post and and that looks good if there is an error then we're going to return a 500 and we're going to print the error and if everything is okay then we're going to return an okay true error no and we also going to return the headers keep in mind that in every request we return the headers so that's really important and this is going to be serice session let's use this action inside our right post component I'm going to use the fetcher I'm going to import use fetcher from remix and I'm also going to use the state of the fetcher to add a pending UI so I'm going to call it isos in and that would check if the state is not equal to idle so if it's not equal to idle then it's either either submitting or loading submitting in the sense the data is being submitted to the server side the state for the loading is when the whole view is being revalidated and we also have a is disabled flag that's based on is posting and if the user hasn't really typed any information in the text area in the post title function when we post some information we need to submit the data to our post action URL and the post action uro is going to be resources post inside the post title I'm going to use fetcher. submit and I'm going to pass the form data and I'm going to pass method that's going to be post and I'm going to pass the action URL that's going to be the post action URL and we need to prepare the form data form data it's going to be the title information and it's going to be the user ID that we get the user ID is going to be the session user ID and that's pretty much it and after we post the data we have to clear the field so I'm going to do set title as an empty string and let's use the is poting inside our text so for example when the user is posting then we need to show an animated icon which I'm going to import from radic UI react icon and also when a post is happening we should disable the button so that the user doesn't really click the button again all right let's see if this works as expected first title you can see that the post is happening but there is something wrong what's wrong is that the session user ID is wrong let's return the session user ID as part of the user details object and extract the information on the component and I'm going to pause the session user ID as part of the right post and we no longer need the post ID so I'm going to remove that I'm going to delete all the dummy po that I have and then try to reload my app again all right so there are no posts and I'm going to write my first post hey this is my first post and this going to be marked on and if I click on post you can see that my post happened and right after the post the root route and my git post route were revalidated so this happens through the framework after every action all the active loers will be revalidated now now if I click on view post you can see that the post that I just made immediately pops up on my git post and I'm going to post another post this is my second post and that's it so that was a pending UI and we post it and if I come back to my view post I can see my post so our right post is working is expected now let's do one final touch if I go to the react virtuoso and go to this page so there is just one interesting thing where they kind of tell you to not use margins and then replace them with padding so that is the reason why I don't really have a margin on the post component so I'm going to replace that with a padding so I'm going to go to post and add a Dev around my card component and I'm going to give a class name to it and I'm going to give padding y off one now if I go back you can see that there is a nice padding around my list with this are step six is complete in this section we're going to build the profile route for our users if you go back to the gster dodev app that's already shipped and click on the user name on the nav bar you can see that we're navigating to a profile page the profile page has a GitHub Avatar name and username combo if you click on the username you're taken to the GitHub profile and if I come back to the G post and click on the name of any of the posts then I'm also taken to the profile of that user and I can see all the posts that they post in the past and keep in mind that this list that you see here is also an infinite list just like the one in the GST route all right so now that we know how it looks let's go build our profile route I'm going to come into my route and then make a new route called uncore home. profile and this going to contain username that we pass as part of the Ural parameters all right let's build the component first and then we can build the loader next so I want this component to look like this it's going to have a main container that's going to be div class name is going to be flex and flex colum with is going to be four and Max with Excel padding X4 and margin Y2 and inside this I'm going to have another div and this div also is going to be Flex Flex column justify Center item Center and margin is going to be four and inside this div you're going to have the Avatar component from shads and UI and I'm going to have a class name with 24 and height 24 and margin bottom four and on top of that I'm going to have an avatar image and Avatar image also comes from a component folder and I'm going to give an ALT name called user Avatar and the source should be the Avatar URL we have to Define that and that's pretty much it underneath that I'm going to have the name of the user and that's going to be H1 class name it's going to be text to excel font's going to be bold and we're going to have the name and right underneath that we're going to have a link to the GitHub of the user and we're going to import the link component from remix react and we're going to pass the username to the link and then we have a P tag and I'm going to pass a class name to it and I want the text to be Sy 500 and I'm going to have at and then username all right so underneath that I'm going to have a break and a separator component from our components UI separator and I'm going to have another break and underneath that I'm going to have a simple text a say user post and I want this to be font heading class and I'm going to have another break and I'm going to have my infinite list I'm going to go back to my GI post and I'm going to get my infinite list and import my infinite list component that looks great now our job is to get some data from our loader I'm going to extract all the information right here that I need from my loader and let me build my loader so this is going to be the loader method and inside that I'm going to extract the username that we pass here as part of the URL params and it's already available as part of the load of arguments and under that I'm going to have my super base method to get the get super Bas with session and headers method this is going to be server session and we check if there is no server session then we redirect the user back to the login page if there is no valid username then I'm going to redirect the user back to 404 and after this I'm going to do the same thing that I did here I'm going to extract my search params and my page number I'm going to copy this chunk of code from git post route and then paste it here I really don't have a search implemented in my profile page so we can do this later so I'm going to take down the search query and I'm going to pass this information to super base so first I want to get the profile of the user so for that I'm going to create a method let's say data profiles it's going to give me back the profile and it's going to be evate get profile for username and I'm going to pass the database client and I'm going to pass the username and from this method I'm going to get the profile and if the profile is not found on the database then we redirect the user back to 404 that looks good and underneath that we're going to implement another method that looks same as a method from the post where we're going to get the post with the detect information so let me just copy this information and paste it the only exception is that we're going to have a user ID from the profile instead of the search query params get post for user all right that looks good and this is going to be post and we're going to do the same thing that we just did here so we're going to get the user information from the session and we're going to combine the post with the likes and the comments and I'm going to import these two methods and that's pretty much it so now our job is to implement these two functions in our super base database so first let's go ahead and do this first method I'm going to go back to my database server. TS file I'm going to have an Asing function and the function's name is going to be get profile for username and I'm passing the database client and the username let me just copy this and then just paste it here and inside that I'm going to have something called as a profile query and database client. from profile stable do select everything that matches and do equal username comma username and I'm going to return single so I'm going to return only one item back I'm going to evade this query and print the error and return the data and the error I'm going to come back here and then import this method that looks good and then I I have to build this get post for user method let's go ahead and do that let me just copy this method and then paste it down here I no longer need the search query I'm going to pass another extra parameter that's user ID user ID that's going to be string and this is pretty much going to be the same post query I'm going to copy that information and paste it right here except for the fact that we're going to have one small do equal method where we're going to check for the user ID and the rest is going to be the same so I'm going to copy the rest of the items here and then return everything we're going to evade the post query and then we're going to return the same thing so if there is an error I'm going to change it name of the function here all right so let's go back and then import this method and I'm going to rename this as raw post going I use that here R post and that's pretty much it and I'm also going to pass the profile back to the user let me fix all the Imports and let's see if this works is expected let me go back to my app and click on my username all right so it's not found because of the fact that this is not been implemented yet so let's go ahead and do that I'm going to go back to my home layout route and use actual information from the loaders so I'm going to create a loader inside my home layout route I'm going to copy this information inside my home loader and import all the missing inputs that looks good and in this method I'm going to use my get user data from session method and then extract out the user ID user Avatar and username information and then I'm going to return the data back from the loader all right that looks good now I'm going to extract the data on my client side using my use loader method let me import that and I'm going to use Avatar URL and username both I'm going to pass the username here and it's also going to be my username here and for the source yeah this one comes from the user Avatar Euro Avatar Euro let me go back and import that as well okay that looks good that looks good let me go back to my app this is the actual GitHub username and if I click on it you can see that I'm taken to my profile view and that looks great and you can see that I also have my post let me test it with another user I'm going to log in as another user and I'm going to write a post this is my post as another user and I'm going to post it you can see that we've got the post of this user and if I click on this user profile I can see that the post and the profile of that user and if I click on the username you can see that I'm taken to the GitHub that's working as expected the only thing that I have to fix is the rounded borders around the Avatar Ur let me go back to my profile and the avatar comes from radic UI so that's not what we want so we want the Avatar to be imported from our components folder yeah that looks good and let me just copy this information there all right and remove this AB image all right that seems to be working perfectly fine so with this we've implemented our profile page successfully so if I come back to my GI post and then click the name of this user I'm taking to the profile of this user with all their posts in an infinite list in this step we're going to have a post route where you can see the post and also like and comment on that specific post if you click on either view likes or view comments button on the infinite list you have a dialogue component that opens up as part of the post R and inside that that component you can see that you have post with likes and comments this comments button is only going to show the number of comments on this post but the like button is going to let you like and unlike the post if you like or unlike then remix will revalidate this whole route right below you can see a component to write the comments and if you write a comment and this comment immediately pops up on the list of comments and on the comments if you you click on username it should take you to the profile of that user this route is a complicated route so we're going to build it in steps so first we will build the like component with like and unlike action then we will build our comment action and then we will reuse the right post component to also support posting comments after that we'll build our comments list component and finally we will put all of them together to build our post route all right in first step we to create a like component with like action so I'm going to create a resource rout for the like action resource. like. TSX I'm going to treat this like like a full stack component that means it'll export an action and also a component but the component is not going to be a default export rather just a named export so this is just for the purpose of cocation so I'm going to create the component props first it's going to have liked by user likes post ID and session user ID and I'm going to do a named export function like and it's going to accept all the props and it's going to return a fetcher form and let me import my use fetcher inside the fetcher I'm going to have three hidden input Fields so the first field is going to have a name called post ID and it's going to have the post ID that we pass as part of the props and then the next one is going to have the user ID and it's going to contain the session user ID and the last one is going to check for the liked by user flag and if it's true then the new action is going to be unlike if it's false then the action is going to be like underneath need that I'm going to have a button and this button is going to be the component and it's going to have a class name for group hover and flex and items going to be Center and I don't want the focus to have an outline style and I'm going to add that style as well so inside this I'm going to have the same kind of style that I have of you like so I'm going to use this icon so I'm going to copy this information and paste it inside this component and import the star from loose side react the only difference is that I'm going to have a fill current so this is going to be a fill style and that looks good and underneath that I'm going to have a pan and that's going to going to look similar to The View likes so I'm going to use the same span all right our like button component is ready so now we have to build our action the action's going to have a standard action function and it's going to have the first few items from get super base put session headers method and we're going to have the Ser session and if the session is not active then we're going to redirect back to login page and let me fix these inputs we're going to get the form data from the request so I'm going to add this piece of code where you can see that we evade the form data and then we get the action post ID and the the user ID action here you can see that we've got the action post ID and the user ID so these will be sent as part of the form data when you use the fetcher. form for posting and I'm also going to have a simple check for post ID and user ID to see if they are valid if not then I'm going to return a 400 I'm also going to attach the headers if they have valid then I'm going to check for the type of action if it's like then I'm going to insert the like right insert like and I'm going to pass my database client super base and I'm going to pass user ID and post ID if there is something wrong then I'm going to return a 500 and the same thing for unlike as well and I'm going to call this delete like and if everything is all right then I'm going to return Json okay true error n and I'm going to return the headers along cool so we only have to implement insert like and delete like in our database. server. TS file let's go ahead and do that so let's reuse the same method insert like and we're going to have user ID and post ID post ID is also going to be string and inside this method I'm going to do this on the likes table and if there's error I'm going to print it and I'm going to return there the same thing for delete likes I'm going to copy this method and I'm going to call this delete like and instead of insert I'm going to call delete and I'm going to look for a match and that's pretty much it now I'm going to go back to resources. like. TS file and insert all the missing inputs all right our light action is ready all right so in Step number two and three we're going to have a comment action and we're going to update the right post component to support the common post as well let's go ahead and do that all right the first thing that I'm going to do is I'm going to make resource route for the comments resources. comment. TSX and this is just going to be the like action except for the fact that we won't really have this component inside and I'm going to just copy the whole thing I'm going to copy the whole action and paste it inside my comment and I'm going to import all the missing Imports and fix the Imports as well for the comments I need the title information so that's the the title The Post ID and the user ID if any of this information is missing then I'm going to return a 400 all right I'm going to rename this to title and we don't really have any action rather we just have one method to insert comments so I'm going to rename this to insert comment and fail to comment and let me go back to my database. server. file and create an insert comment just like the insert like and it's going to accept one extra parameter and that's going to be title string and of likes table we're going to enter the comments table and I'm going to enter the title so there is something wrong with my comments table so I'm going to delete the table and recreate the table again so I'm going to create a new table called comments and ID is going to be you your ID and I'm going to add a column for user ID I'm going to link that with the profile ID it's going to be Cascade and save I'm going to add another column for post ID and I'm going to link that with the post information going to save it and then I'm going to have a title information that's the actual comment and I'm going to choose a text for the same and I'm going to mark all of this s non-nullable and I'm going to save the table I'm going to refetch my type information now if I go back to my database. server. ts5 you see that this is no longer complaining that's great and I'm going to import the insert comment from server. TS file it's going to be two string and that's it all right that looks great let's go update our right post component to support our comment section I'm going to come back to write post and accept post ID that's optional it's going to be string if there is a post ID then that means we can assume that it's a comment this comment is equal to BU of post ID and if it's a comment then I'm going to post to a different action Euro also I'm going to have another field for post ID and finally I'm going to come inside and check if it's comment then I'm going to return a card component that's got a class name of margin bottom four and I'm going to have a card content inside my card and that's going to have a class name of padding four and text needs to be aligned to the right and inside that I'm going to have a text area so I'm going to copy this and then paste it inside the value is going to be title and I'm going to use this button underneath the text area and I'm going to changes to comment in and comment and everything else is the same now our right post component also supports the comment one small thing before we go further so I'm going to go back to my like action and update my action URL so there's going to be resources like and Method it's going to be post all right our like and comment action is done all right so we've done step one step two and step three now we're going to do step four and step five together so first we're going to build our show common component let me go back to the app and make a new file inside the components I'm going to call it show comment TSX file these are going to be the props it's going to accept the Avatar Euro title and the username and I'm going to return a main container that's div and I'm going to have a flex Flex column and item start and inside that I'm going to have another diff and it's going to be flex and we're going to Center the items and under that we're going to have an avatar image let's try using Avatar component from Shaden I'm going to use the Avatar and I'm going to give a class name of where width 8 and height 8 and inside that I'm going to have my avatar Ural and right under that I'm going to have a diff that's got a class name of margin left to and we're going to have a link for username and inside that we're going to have a diff to show the text and it's going to be text small and font semi bold and it's going to be username all right that looks great and we have to import the link from remix react and under that we're going to have our react marked on content we're going to import all the missing inputs all right so our show common component is ready now let's go ahead and build our actual route so I'm going to call this route home. gpst . poost id. TSX so that means we're going to pause the post ID is part of the URL params and as you can see this is part of the git post layout so I'm going to go back to git post and add a small Outlet on top so that our item can be part of this layout and let's go ahead and build our component so we're going to have a default component called current post and inside that I'm going to have a simple use state for open and close of the dialogue and I'm going to return the dialogue component from shad and UI this component is going to have few props that we're going to pass I'm going to pass the open and we also have an unchange method so if the user chooses to close the dialogue then we have to navigate back and I'm going to use the navigate Hook from remix and let me import the react state that looks good and inside this dialogue I'm going to have a dialogue content from components UI dialogue and this dialog content is going to have these class names Max birth Excel and height it's going to be 90% off the viewport height and overflow y scroll and inside that I'm going to have a dialog header and that will contain a dialog description and it's going to have this class class name margin Y is going to be two and text are left aligned and this is going to contain our post component right before we go further and build the whole view let's build a l for this component and it's going to accept post ID from the pams and we're going to get the standard information from Super BAS get super base with session and headers use that to redirect back to the login Euro so I'm going to add all the missing Imports that looks good and if there is no post ID font then we need to redirect the user back to 404 and we're going to get the session ID from the get user data from session and we're going to pass a server session and on top of that we're going to create another method to get the value from the database and we're going to call this method get posst get post with details by ID and it's going to accept the database client and it's going to contain the post site let's go ahead and implement this method before we go further I'm going to go back to my database server. file and add a new method for this specific API the query is going to be the same as this one just to get started but the common section is also going to have the author information so I'm going to add an extra profile information query and I want the profile information with username and the Avatar uro so I'm going to fetch this information I want to order the comments table based on the date and finally I'm going to check for ID equal to post ID let's await the post query to get the data and the error and we print the error if it's available and we return the data and the error all right that looks good and I'm going to take this data and then pass it on to another method that we're going to implement soon and I'm going to call it combine post with likes and comments all right let's build this method inside our utils folder I'm going to come back into my utils folder and then just copy this old method that we have and I'm going to rename this to combine post with likes and comments and it's going to accept post with common details so that means we have to type this data that we get back from the super base so let's go ahead and type our information I'm going to go back to types. TS file and create a new type comment with with author and it's going to have the comments table information and on top of that it's going to have the author information that will have the Avatar Euro and username this could also be null and I'm going to take this and I'm going to create a new type post with comment details this is going to use the post for details method and we're going to emit the comment information and use our new comments information as part of the comments array so now we have a new type I'm going to import this inside our U.S file and what I'm going to do is I'm going to create a commment with Avatar Euro method so what I've done in this method is that I've just taken the comments information and I've just flattened it and I've replaced the autho information with my own information I'm going to use this information for my comments and that's it I'm going to go back to my loader method import the information from uols and I'm going to return the post information along with the session user ID and I'm going to use my post information and the session user ID as part of the load of data now let's go ahead and build the rest of the view I'm going to go back to the memorize post list item and get the post from there use it inside this component and I'm going to import all the missing put and instead of the view likes I'm going to have the like component and that's going to accept session user ID and it's going to accept the post ID post. ID right that looks good I'm going to import the like component from resources. like let me fix all the missing inputs all right so and here the path name is going to be some random path name because this is going to be a read only under this post we're going to have a right post component that's going to accept the session user ID and the post ID and right under that we're going to Loop through the comments and check if we have any data if we don't have any data we're going to show app logo with no comments if there is comment information we're going to map through all the comments and we're going to return a card component and class name is going to be margin Y2 and Min height is going to be 24 and padding is going to be four inside that we're going to pass our show common component the show common component is going to have the title Avatar Ur and the username let me import the card component here all right our view looks pretty good let's see if this works let me go back to get post and if I I click on view post it's not found so what's the problem if I go inside my memorized postless item you can see that they don't really have the right path name so we have to fix it so what I'm going to do is I'm going to get the path name from the location from use location and if there is a search query we need to ensure that this doesn't really disturb the search query so if there is a search query then my path name is going to contain the path name of the location the post ID and the search query params of the location if not it's just going to contain my post ID with the location path name now I'm going to use this information inside here let's go back to our app and check if this works as expected let me click on the star all right you can see that I have a new dialog component that shows my post my comment and the list of comments if they are available let me see if the like is working as expected so if I just click like the like action is firing and after the like action we have this revalidation happening and if I unlike you can see that the same revalidation is happening at the time of the revalidation remix ensures that your client is in sync with the server site so that means we get the latest data from the server site all right let's test out the comments this is my first comment and I'm going to use markdown the comment is somehow failing because of the fact that we don't really have any Ro level security on our new comments table so let me go ahead and do that I'm going to add a new oress policy for our comments table it's going to be select and I'm going to create a new policy for insert now if I go back to the app and try to type a comment and post it you can see that the comment was posted successfully and if I post more and more comments you can see that the number of comments on the comment icon also increases and the comments are also ordered by the date and if I post few more comments then then you can see that the whole view is scrollable and the number is also updated to reflect the latest number of comments on the server side with this our step eight is done in this step we're going to do some final fixes let's do them one by one the first item is for us to add support for post ID route inside profile view so if I go to the profile do username route and click on view likes or comments you can see that we get a 404 this happens because we don't really have support for this route yet in that layout so I'm going to go to my routes folder and create a new route called uncore home. profile. username dopost ID and inside this route I'm going to use the loader and the default exports of home. gitpost dopost ID route and Export the same and once that's done we also have to support this component as part of our layout of the profile. username route so let's go ahead and add an outlet components inside profile. username route now let's go and check out if the app works as expected you can see that now we can see the post ID route as part of our profile routes infant list all right that looks good and when we open the post route by clicking on the links in the background the window sometimes would trigger a scroll reset during navigation and we need to prevent that so we can do that by adding a small property on top of the links component and the property is called prevent scroll reset true and we will do that on The View likes and view comments components and once done we should not face this problem anymore so with this we're done with our step one and step two in Step number three we're going to solve an interesting problem so we're going to stop the infinite list from revalidating on like and comment actions all right so first let's check out what the problem is if I go back to my G post route and I'm on my infinite scroll I've got some 50 post so I can scroll down to all the way to page number five all right so I've just scrolled down and I click on The View likes or view comments to open my post ID route now if I claim my networks Tab and do a like action you can see that after the like action all my active loaders got revalidated so that means my root loader my home my home. post loader and also this loader for this view got revalidated and on top of that theost rock loader got revalidated twice and also also the fetcher. load got revalidated so that means if I go back into my use infinite POS so this also got revalidated when this gets revalidated our local state gets updated when we don't want it to now if I close this dialogue you can see that the infinite list is off something's wrong with that because it got revalidated when it should not have to and now the data seems a bit messed up so our job in this section is to stop the infinite list from getting revalidated on like and comment action we have to use something called should revalidate from remix so what this function enables us to do is that it enables us to stop revalidating certain routes based on certain actions so we can track what the action brings us and then based on that action I'm going to skip the revalidation so I'm going to copy this function and I'm going to paste it inside my git post route and I'm going to install all the missing Imports this action is either the like or the comment action so what we have to do is we have to add a small piece of code inside our like and comment action so this is my Approach what I'm going to do is I'm going to add two route IDs I'm going to call this get post route and the other one I'm going to call Profile do user username route and I'm going to send this information in my success and failure responses so I'm going to send skip R validation in my error response and also my success response for like and also I'm going to do this for my comments as well all right that looks good now I'm going to go back to my home get post and use this information I'm going to create skip revalidation flag and I'm going to get the action result and I'm going to check for if there is Kip rev validation array found and also I'm going to check if this route is part of that array then we're going to skip the revalidation return false if not it's going to be true I'm going to copy the same code base and paste it inside another infinite list route called home. profile. username route and I'm going to change this name to profile or username and that's it now I'm going to run my app again and see if this problem exist so I'm going to scroll to the end I'm going to click on my view likes and add an action you can see that if I like or unlike the revalidation for my home. post route is is skipped so the roots getting revalidated the home's getting revalidated the home. post. poost ID is getting revalidated but my home docu post is not getting revalidated so even if I add a comment the infinite list is unaffected and our scroll hasn't really changed at all so that's exactly what we want and we can also observe the same behavior for our profile if I go and like update comment this view is getting revalidated but then the infinite list is not changing and it remains unaffected after we solve the step number three we're going to see a new problem and the new problem is that if I go to my get post route and if I make an update in my post ID you can see that this view is getting revalidated as it should but these numbers have not been changed because of the fact that this view is not revalidated and this view comes from a local state so we need to at least update these values that we update on our post ID route how do we do that so if I go and add a comment you can see that this view gets revalidated and we have access to the data of that route so we're going to use this data of this route and update our local state and how are we going to do that we're going to use a hook called use rout loader data this use rout loader data hook kind of gives us a date that we need from a specific route if we just passed the route ID so that's what we're going to do so we're going to go back to use infinite post and add this hook called use route loaded data hook and extract the data from use rout loader data and we can find the type of the loader that we are going to use I'm going to call it post loader and this comes from home. poost do Post ID loader and inside this we're going to pass the post route ID and we're going to accept that as a prop all right so this is something interesting the post route ID can be two routes it can either be home. gpost dopost ID route or it can be home. post. username dopost ID route because we show this view in two places so we show in the profile view and we also show this in the GST view so for that I'm going to go into my infinite virtuall list. TSX file and accept a new prop called is profile so this will be true if the infinite list is shown inside the profile route so I'm going to go back to home. profile and add this is profile flag this is going to be true and then I'm going to come back to infinite virtual list and get the route ID based on my profile so if is profile then our post ID view is going to have this route ID route home. profile username poost ID so this one that you see here if not it's going to have this route ID that you see here all right so that looks good so I'm going to pass this post ID route as part of my use infinite post hook and I'm going to go back to my use infinite post hook and Define a use effect this use effect is going to update our local state when we have any update in this data so what I'm going to do is I'm going to get the post information from this data first I'm going to check if there is any post and if there is any post then I'm going to update the local state with that post information so I'm I'm going to do set post post. map post and I'm going to check if the post ID is equal to update the dopost ID if it's true then I'm going to return the updated post if not I'm going to return the post all right that looks good let's see if this works as expected I'm going to go back to my app and do a refresh all right I'm going to scroll down and pick a pose so for example I'm going to update this my fourth pose so if I unlike this then this should be reflected also on this list item let me go ahead and click it unlike it come back you can see that this list is updated sweet so if I go ahead and comment something you can see that this view got revalidated and if I come back you can see that this list is also updated that's exactly what we want so we've come to the last step in this section in this step we're going to build an optimistic UI for likes and error toast first you got to ask yourself why do we need an optimistic UI so let me go to a post and change my network to slow 3G if I try to like right now you can see that the like action is fired and then the revalidation happens and the updated like kind of pops up only after the revalidation so let me unlike right now you can see that this like actions in flight and at the end you see the revalidation happening and after this you get an update the the optimistic UI is basically updating the UI expecting an optimistic outcome so that's what we're going to do right now in this step so if I go back to the app at the time of subm mission we have the value of the action in flight and you can access value using fetcher. form data and also on the same line we can get this information from fetcher do state of the submission and the revalidation is happening then the state of the fetcher will not be idle with that information we can safely say that we're loading and with this we can build our optimistic liked by user if the inflight action is not undefined we're going to check if the value is equal to like if not we're going to use the liked by user on the same lines we're going to use const optimistic likes and we're going to check for inflight action if inflight action is equal to like then it's going to be likes + one if not it's going to be likes minus one if not we're going to use the likes if the inflight action is done then we know that the revalidation is done we're going to use the likes from the actual property so now we're going to take this values and replace them with our likes and optimistic like by user all right let's check if this works as expected now I'm going to refresh I'm going to open this and I've cleared the network T you can see that the optimistic UI update happened even before the call got completed so if I UNL you can see that the UNL action happened immediately even before the revalidation is complete also at the time of this action I'm allowed to click more than once which needs to be prevented so what I'm going to do is I'm going to go back to my code base and use is loading as a means of disabling the button I'm going to go back to my app and try again now you can see that at the time of is loading we disabled the item from being clicked again so even if I try to click right now I can't click what happens in case of failure in case of failure we're going to show a toast message so that the user knows that there is something wrong so I'm going to have a use effect and inside this use effect I'm going to grab the data for the fetcher and I'm going to check for its loading State and I'm going to use a toast from our compos component UI and we have to define the toast so I'm going to use const toast is equal to use toast from component UI and I want to Define how the toast looks I'm going to check if the fetcher. data has any errors and the fetcher is Idle then I'm going to show my toast message the variant is going to be destructive and the description is going to contain the error message and we're going to type our fetcher with the action data that we get now what we're going to do is we're going to throw an error just like that so I'm going to use this and I'm going to throw an error we're also going to go back to our root. E6 file and add the toaster component in our route. t65 I'm going to go back to my app and do a refresh try to like I get a toest message if there is any failure you see that I liked the optimistic like happened but then in case of failure it reverted back to the old value so that means our UI is in sync with the server site and we also show if there is something wrong with our optimistic updates in our app that's pretty much it and let me just add one small change in the root. TSX file we don't want overscroll on our body and I'm going to mark it as none now you can see that I no longer have this overscroll in my body okay that's it our app is pretty much done this marks the end of our app we've built a fully responsive and a function functional social media platform with likes comments search write post Etc now it's time for us to deploy this app it's time to deploy our app so I've come to my viral dashboard I've also pushed my code to a GitHub repo and I'm going to click on add new projects and I'm going to search for my projects which I don't have access to so I'm going to adjust my permissions I'm going to give permission for My remix super based course you know that it just pushed and I'm going to import the project inside the environment variables I'm going to add my super base environment variables I'm going to go back to myn I'm going to add my super base URL and on top of that I'm also going to add my super base Anon key and the domain Euro I'm just going to use some random one for now and then adjust it later and I'm going to click on deploy all right so you can see that it's been deployed to remix-super base- cor. virala so we're going to copy this domain I'm going to go to environment variables and I'm going to adjust my domain Euro and this is going to be my domain Euro all right that looks good and the next step is I want to adjust my GitHub or so I'm going to go to my GitHub and go to settings and go to developer settings and oo this was our oo App instead of 3,000 I'm going to use my new domain Euro and update the app all right that's updated and I'm going to come back to my super base dashboard and go inside my G poster stage go to authentication go to uro configuration and also update my site Ur here and I'm going to save all right that looks good let's see if we can open our app and check if our app is working as expected so I'm going to go to my course app that looks good and I'm going to click on join community and I'm going to click on GitHub all right so you can see that our app is working as expected I'm able to log into our app and also let's see I'm able to scroll down do the infinite scroll I'm going to write the post hey I'm writing on my deployed app I'm going to post it and I can post it and I can also open the post view comment it and then add my comment that's working is expected that's great and also I can go search for a post you can see that if I search and my search is working as expected that's great and I go to my profile and I can also see the infinite post in my profile as well that looks good and if I click on my username I'm taking to my GitHub account and if I go back I'm right here on my get poster cool so if I click on the log out and log do and if I click here I'm taking to home cool so our app is fully deployed fully functional and working great thanks for watching this video if you like the video please leave a like And subscribe for more and I will talk to you soon
Info
Channel: Raj talks tech
Views: 4,030
Rating: undefined out of 5
Keywords: frontend, remix, supabase, tailwindcss
Id: ocWc_FFc5jE
Channel Id: undefined
Length: 216min 33sec (12993 seconds)
Published: Tue Dec 05 2023
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.