 Hello everyone! In today's video, we'll be building a full-stack social media app from scratch with React, Firebase, and Chakra UI. Our app will have all of the most robust features of a full-stack application like form validation and client-side routing. We will also implement authentication with Firebase Auth to log users in and we will also use Cloud Firestore as our database back-end and also cloud storage in Firebase for users to upload their profile pictures. You can create posts and you can like posts and write comments on posts in our application. You can also delete comments and you can delete posts as well. And you can also view all of the user profiles in our app. Finally, we'll build functionality to upload profile pictures to cloud storage. And once we're done, we can also choose to log out from our application. So this is how our back-end Firebase console looks like. We're gonna have authentication which is managed by Firebase and we have all of our user profiles here. And then we're gonna have our Firestore database to store three collections, one for the comments, one for the posts, and one for all of the user profiles. And then we're gonna have storage here to store the avatar files for every one of our users. And by the end of this video, I'm gonna show you how you can deploy your fully functioning web application to an online hosting service. And we're gonna deploy our app to Hostinger. Hostinger has very kindly offered to sponsor this video. And I've been using Hostinger for quite a while now and I have to say that I'm really impressed with Hostinger's H-Panel. So H-Panel is basically Hostinger's hosting control panel and that's what you're seeing here. And in case you haven't realized, my demo app that I was showing you earlier is hosted completely in Hostinger's H-Panel. Hostinger's premium web hosting package also comes with a free domain name which I'm using right here to deploy my demo app. And have you noticed that sweet small little lock there in the top corner that indicates we have an HTTPS connection to my demo site? That's because Hostinger has automatically installed an SSL certificate in my demo site for me and I never had to touch this. It just does it automatically and it expires at... that's right, never. That means I never have to worry about this and it just does it automatically. Just yesterday, I tried out their customer support and I was able to talk to an actual human and not just some auto reply chatbot just within minutes. So if you ever need web hosting solutions with free daily or weekly backups, go to hostingger.com slash ravenjs and you can get 10% off all deals. And if you're fast enough, Hostinger is having this special Black Friday deal that you can get on top of the 10% off if you use my Hostinger link. All you have to do is just click claim deal, just add that to current. I recommend getting the 48 months plan because you save the most with this plan. Just go all the way down here and fill in all your payment details here and remember to use the coupon code ravenjs and just hit apply. And that way you can get 10% off on top of any special deals that Hostinger is currently having. So to start building our application, I'm going to make a new folder called social media app. And I'm going to take that and drag it to terminal so I can run yarn, create, react that and then dot. So dot basically just means create a react application in this current directory. Let me hit enter and let that run for a bit here. With our app now created, I can just drag the folder to VS Code to open it in VS Code. And let's get rid of some of the files in the source directory here. So I always go and get rid of all of these files here that I don't need and just delete that. And then you have to go to our app, get rid of that and all of this. I'll just return an empty string for now. And I like to always make this in one line to export default function app. And then in index we're going to get rid of basically all of this except for app. And so that and report web vials. And now before I start the server with yarn start, I always go and make a .env.local file and set browser equals to none. The reason for that is because I have to do yarn start every time I make a big change like every time I install a new package. And I have to keep restarting the server and just keeps opening a new browser tab in my browser and I want that. So I just set browser equals to none and I can just do yarn start and it won't start a new browser tab every time I restart the server. Chakra UI is going to be the main component library that I'm going to be using for this project just because Chakra UI, I find it to be the easiest to set up and the least configuration required for the best looking components. So I'm just going to copy this line here to install Chakra from Chakra's getting started website. And just go to terminal and paste that line in. And then at the same time we have to go and add the Chakra provider to our root application in our React app. So I'm just going to import, see how they do, import Chakra provider. So let's import Chakra provider from usually it has auto complete to help me import this in VS code but because Chakra UI is installing right now. So I have to just do this manually for now. And then let's just Chakra provider here and just wrap the app component inside of Chakra provider. It's having issues resolving the package. So I'm just going to fix that. All right. And that's done installing. So I'm just going to say right off the bat, you don't have to be very well versed in Chakra UI to be able to follow along with this tutorial. But if at any point in time you run into anything that you don't understand about Chakra UI that I'm doing, you can just go to Chakra docs and just like let's say you don't understand how the button component works. Just search up button and just see how the implementation is like in the website documentation. And hopefully that'll give you a better idea of what I'm doing in the tutorial. And with Chakra UI installed, there's just two more things we have to set up. And that is React router. So if you look at our demo app here, you can see that we have different routes. I can go to all users. It will change to slash users. If I go to home, it's dashboard. And to do that, we have to handle all of the routes with this library called react router. So I'm just going to do yarn add react dash router dash dom. Just let that install with react router dom now installed. And I just realized that I have put this Chakra provider wrapper in the wrong file. I actually intended to do that in app.js. So I'm just going to move that over here real quick. I'm just going to add Chakra provider here. Just get rid of that from index.js. It actually doesn't matter, but just best practice and I just prefer to have in my app.js file and just leave the index.js file as clean as it can be. Now let's go ahead and make our very first route using react router. So first we are going to have to make our router component here. And that is going to be in our app.js file. I'm going to just do router provider and then it's a self closing tag and import that from react router dom. And then we have to pass in a prop called router. And this is our router configuration object that we're going to have to give it to react router in order to process our routes. So I'm not going to make this router config object within app.js. Instead I'm going to make a new folder in our source folder called lib and let's make this route.js file. You know what maybe routes.js. And then here we're just going to make a router object and just pass it in here. So let's go ahead and do const router and let's export this equals to an object. And then here let's just make that import statement. So import router. So here what you can do is import that from dot slash library slash routes. But I don't like the dot slash conventions here and by the time we get more and more nested routes we're going to be seeing stuff like dot dot slash dot slash dot slash and a bunch of that. So that's a problem with relative imports and that's why I'm going to just install absolute imports right from the get go. So I'm just going to go to this medium article here to install absolute imports. Then we can just do like import router from then lib slash routes without the dot dot slash. So just copy that and go to our jet.js config file here and just paste this in here. No, it's JS config dot JSON. And now we save that and can just do import router from lib slash routes. So this is very useful down the down the road when we start to have more nested routes and I just prefer to have it done from the start so we don't have to refactor our import statements later on. And if you're using TypeScript there's a different config here. You save it in tsconfig.json instead of jsconfig.json and just paste this thing inside of your tsconfig file. So now we have that. We can go to our routes.js here and make our router. So I'm just going to do create browser router here and import that from react router DOM and here is going to be our, it's going to be a list of all of the different routes and each route is going to be an object inside of this list. So let's make our very first route. I'm going to make a constant to store the URL. So export const let's call this root equals to just a slash. So that's our root and then here in this object this is our very first object in this list. I'm going to make the path call root and then we're going to have an element So this is ideally going to be a react element. We can just, I'm just going to use a string here that says public root just to show you what this does. And this has, this usually this will be like a react component like root or something because I don't have any components yet. I'm just going to use a string. So let's do that. And then I can just zr on start. And so we can start the server. Let's go over to our browser. Let's just refresh this. I'm going to just inspect and open up the console. Those words are really small. I'm just going to zoom in and let's refresh this. See if we see public route. There we go. While we're at it, let's just go and add a few more routes here to our browser router. So I'm just going to add two more and let's go in the export const. The reason I'm exporting these constants is because when we're referring to paths within our components, within our app. We don't want to hard code the path in each of the individual components and instead we'll usually want to reference the constants here that were exported from this routes.js file so that whenever we make a change to the routes, we don't have to go through each of the components and just look for all of the different references. We just update this in one place in the constants file and just work throughout the entire program. So I'm going to make a login path here slash login as well as a register. So register. Register and then here we can have our path be login and also register. Now instead of just doing public route, I'm going to make a component folder components and let's make a sub folder here called auth. And in this auth folder, I'm going to make two components login.js as well as register.js. And let's just make empty functions here first. So react functionally component. Just do that. Let's save then here instead of just using the string as the element, we can actually use the login component that we just made. So you see that now I can just import login from components slash auth slash login. I don't have to do like dot slash components or whatever. So that's like the why you would use absolute imports in a react. And here in register, I'm just register. There we go. And then now if we go to our browser and we do slash login, it says login and if we do register, it says register. Great. Now that's done, that's actually going to write some code for our login form in our login.js file. So I'm just going to close the rest of the files that we don't need here. So in login here, I'm just going to get rid of that import react because we don't need that in the newer react versions. I'm just going to return. So there's this running joke in the web development community. The hardest thing you're ever going to do is center div as a web developer. But that's not true now with Chakra UI because Chakra has this component called center that we can just use to center our components. I'm going to give the center 100% width and I'm also going to give it 100% view height. And then in this center component, I'm going to make a box. So if you don't know what any of these components are, you can just as always just go to the Chakra UI documentation and just look at the component specifications in the documentation. So like if I go to box here, you can see what you can do with the box. You can change the background color of the box and you can add the padding. You can set the color. So we're just going to go back to our code here and let's continue with our box. I'm going to give our box a margin X of one. I'm going to give it a max width of MD. So this will kind of act as our container. It will not let the width go more than the medium break point. So in Chakra UI, there are these things called break points and there's like XS and then there's small and then there's medium large and then extra large. And then there's the second level of extra large. All right. I'm also going to give this box a padding of nine units and I'm going to give the border one pixel in thickness. And also I'm going to make this border slightly rounded by giving it a large radius. And then here inside of this box, I'm going to add a heading here from Chakra UI. Just say call it log in. So we are going to give our heading a margin bottom of four. This is for the input fields below this heading. We want to add some space between the heading and the input fields below. I'm going to set the size of this heading to be large. And then let's do text align. Let's center this heading as well. So align equals to center. Let's see how this looks like in the browser. So if I go to slash log in, we should see our box. You can see the border is of one pixel and it's slightly rounded. And then we have our heading and then our padding, our margin of four below the heading. And next for the input fields, we can always go and like make an input like that and then just like an input like that. And then it'll show up like that. But I'm not going to do that and I'm instead going to wrap all of this in a form here. So the reason for doing that is because if we're using forms, we can just use the enter. Just fill up these fields here and just hit enter and it's going to log in. Without the form component, you'll have to explicitly click on the login button and trigger the on click listener in React to be able to submit the form. So hitting enter won't submit the form. And that's why we have to use our form element here instead of just using react control inputs. And also using, I'm going to use a library called react hooked form which will help us write form validation. So like if I get rid of this back part and just hit enter now, you can see that we have like form validation tells you that email address is not valid. And just having react hook form simplifies that process. But before we install and implement react hook form, let's just go and build out the basic UI components first in the form. I'm just going to leave the on submit call here empty. I'm just going to like on submit equals to like a blank function, a blank arrow function here. So it does nothing right now. The form doesn't submit. So the way you make forms in Chakra UI is you can have form control components like form control and this component we have like form label. And then we have our inputs here. All right. So I have to get up to install and just just prompts me the right thing to fill up in the code. So I have a type of email here and the placeholder is email. And then here in the form of the form control, I'm going to just going to demonstrate how to use form validation. So there's a prop in Chakra UI form control component called is invalid. And I'm just going to hard code this to true just to show you how it looks like if there is there is indeed an error. I'm going to give I'm going to add some spacing. I'm just going to add to two units of padding in the wide direction which means top and bottom the form label. You can just make this email. And here we have our placeholder. You can have like email like user at email.com. And then here I'm going to just add a form. You know what? Before I do anything, let me just show you how this looks like in the browser. So it looks like this for now. And you can see that the email input is highlighted in red. And that's because I have my is invalid equals to true here. And that's why it's red. If I have this false, it's not going to be red. So let's leave this true. And I'm also going to get like I'm going to put form error message here from Chakra UI. And we can say this is an error message. Let's see how that looks like. So that's how you make errors in our Chakra UI form. Come back here and just take this and do the same thing. But for password, so let's change this to password and type. Let's change this to password. And then we can change this to password 123. Let's see how this looks like. And here password. All right. So that's nice. And lastly, let's go ahead and work on our submit button. So I'm going to go below the form control for the password field and add a button from Chakra UI. And this button is going to say log in. And let's let's style this a bit. Let's give it some spacing on the top. Let's give it a margin top of four. This type has to be submit and the color scheme. I'm just going to give it like teal. Maybe I'll just change the entire project's color scheme to teal instead of the purple in our example. You can give this size of medium and then the width. So I'm just going to see how that looks like. So you can see it's like on the left. So we can give it a width of full and it'll just take up the entire space. And the Chakra UI's button has a few useful props like is loading. And we can just set this to true just to see what it looks like. And then you can see it's spinning and loading. And we can also give it a loading text logging in. And that looks good. If we add three dots, it'll look not without the three dots. All right. What we can do next is add that register link. Don't have an account register instead link here down at the bottom. So if I go back to log in and you can see that line of text don't have an account registering instead. So let's add that. So how we're going to do it is we're going to use this component from React Router called link. So the link from React Router DOM and we can just go and add that under our form component link and just register. So link register. And then that's a clickable link and we can add the prop for two. And we can always just do two slash register. But as I mentioned previously, we don't want to hard code the routes in our components because sometimes you want to change the routes a bit. And we don't want to have to go through every single one of the nested files to look for the links. So instead, I'm just going to import this register constant to register. And here it's going to import from the library. So you can see the benefits of using absolute imports very clearly now. Instead of using dot dot slash slash library routes, we can just do library slash routes because the absolute location would be in the source directory. So we don't have to do any dot slashes and relative imports. So now I'm going to link to register. If I save that, I can just click on register. It's going to bring us to the slash register page. Well, one thing you're going to notice is that we can't style this very well. Like we could always style it with the React style prop using style, just like text or font weight, make it bold. And we can style it like that. But we already have check UI and our UI components. So instead of just using the style props from React, I'm going to import the link component from check UI. And here you can see we have our link component that has some of the styles that we can use. And of course we can pass in more styles of our own into the check UI component like color. Because if you do color here on the link component that's provided to us by React water, you can't do just color equals to blue. It doesn't work, see? So now let's go and import the link component from check UI. And now it's going to clash because we have two different link imports. So let's just change the link from React water to router link. So link as router link. And here this link component is going to be the check UI link component. And now if I add color equals to teal, you can see that actually works. I can change the color weight to 800 and then make it darker. And then we can also go and do font weight to medium. Then we can add our underline. So let's do text decor equals to underline. And they'll make it underlined. And then so now the thing I'm going to do next is make it so that there's a highlight. So we can see it highlights the background when I have hover. So I'm going to do hover. This is a check UI thing. I can change the background to not purple teal. So hover, it gets highlighted. All right, that's nice. I'm also going to just add some margin on the top. Margin top equals to six. So it doesn't stick to the button. You know what? No, let's add the margin to a parent component called text, which we're going to import from check UI. Because I want to be able to type other things that aren't the link here. For instance, I want to write like, don't have an account space. And then here we can have font size of extra large. Align this to be center and give it a margin top here in the text instead. So here we have our don't have an account prompt register instead. Let's write instead here instead. There we go. And there's it's going to be stuck together. So we have to add some space like that. There we go. But now one thing you're going to notice is that clicking this register link doesn't do anything anymore. Because it's just we have to use the router link component from React router DOM to be able to actually make a functional link. So the chakras link is just for styles. And then for functionality, we're going to make this link render as a router link. And then Chakra is going to style this link. And then it's going to implement the functionality of the Chakra, the React router link. So save that. If I click on register instead, you can see it brings us to register page and it works. But before we make our login form work with functionality, I'm first going to want to make our dashboard component in our dashboard route. Because if we don't have our dashboard, the login form doesn't know where to go next. We're just going to log in and we're not going to be able to tell if we're logged in or not. So I'm going to go to our components and make another dashboard folder here and make an index.js and make a React functional component. And then here, let's name that dashboard. And here, let's just call it dashboard. And then here, we're going to add that to our routes. We have to make export const dashboard equals to... We can always just do slash dashboard. But the thing is, I only want the dashboard to be accessible by users who are already authorized and logged in. I don't want just anyone to be able to go to dashboard because I don't want users that are not signed in to be able to go to dashboard. So I'm just going to make it protected and add slash protected to it. So basically how this works is if you go to our... Let's go to our demo page here. So let's say I have protected slash dashboard and if we go there, it's going to kick me back to the login page. See, because I'm not logged in. If I go to protected slash profile slash whatever the profile is that hit enter, it's going to kick us back. It's always going to bring us back to the login page because we're not logged in. But if we are logged in, for instance, in this tab, I can hit enter. It's not going to kick us back to the login page because although we're in the protected path, we're logged in. So we just have to make the logic to check for the protected routes. For that, let's make a protected route. And then the way we implement that is we go to ReactRouter here to make another path. We call this protected. The layout element here is going to be our layout element which we're going to have to make in our components folder. And then we can pass in children to this path of protected. This is going to be another list of objects. These objects in here are going to be the protected slash whatever path. So protected slash dashboard or protected slash posts, users, profiles, whatever. Let's just put this as a string for now. So this is a string. And then children, let's just leave that empty. And then I'm going to go to my app here. We can go and look at protected. And then you can see this is a string. I can do slash protected slash whatever. It's not going to exist because we don't have that route. If I do protect dashboard, we're not going to have that route. So let's go and make that here. We can make our path. Let's make it dashboard. And then the element is going to be our dashboard element. And then if I go to protected slash dashboard, you can see that it doesn't give us an error anymore. But you're going to notice that it's not going to say dashboard. Instead, it still says this is a string. And that's because in order to make a layout component, we need to go and like we need to use this outlet component from the react router library to be able to render children elements. Because right now, this is essentially it's just a react component that's just a string that just returns a string. It doesn't have a way to return the children components. So let us actually go to our components folder and make that layout component. So layout and let's do index.js react functional component. Hello, react functional component. There we go. Layout. And then here, if we do index, it's just going to keep returning index regardless of what the children is and what the sub path of it is. So instead of just doing that, I'm going to use the outlet component from react router DOM. So basically the outlet component is how react router knows where to insert the children. If I say that this is the child and just do outlet here. And then I go to routes instead of the string and I do layout. Right. And then dashboard is here. Right. So if I go back, you can see this is the child. This is the child dashboard. And then I can make more children routes. I can make like a test, test, like just test. All right. And then test one. So if I go to slash test, you can see this is the child test one. And then if I make another child and let's do test two. And then the element test two, then you can go to test two. And you can see the child is test two. All right. Now that we're familiar with that concept, we can come and get rid of all of those children components there. And then what we're going to do is go to the layout component and actually implement the functionality here instead of just writing, this is the child. So what we have to do is we have to go to our path name and check if the path name starts with slash protected. And if it does, then we want to check if the user object exists, meaning if the user is logged in or not. And if they are just rendered whatever child component there is in the outlet component. But if the user isn't logged in, then we have to redirect them back to the login page. So how are we going to do this? So let's go first check if the path name starts with protected. And how we're going to do that is by using a hook that's provided to us by the React Router DOM library called useLocation. So useLocation, that's a hook and it returns a location to us. And this location object has a path name property in that object. So instead of doing this, so what we can do is we could just take that path name and just destructure inside of the first line itself. And that way we can save on one line. But if you don't want to do that, you can just do like location. And you could always just console.log the location object to see whatever it's in that object. So let's refresh this. And now we're out here, so let's just go to dashboard. And you can see that we have an object here and it has a hash, a key, a path name, search and state. So what we're interested in is the path name property. So let's just go and do path, path name. And then now if I refresh this, you can see the path name is a string that's giving us the path slash protected slash dashboard. All right, now that we have the path name, we can use effect here from react. And then let's just get rid of the console.log. And here it's going to take in an arrow function. And then here, let's just say if path name changes, then we want to run all of the code here and use effect. So let's do if path name dot starts with. So starts with is a JavaScript method that we can run on strings. And if it starts with protected, let's just console.log protected route for now. And let's go and refresh this. So we are in protected route. If I go to slash login, for example, then it won't say protected route. But if I do slash protected slash dashboard, then it says protected route. And instead of just logging this to the console, let's go the extra mile and let's just redirect the user to the login page whether if they're logged in or not for now. So here we're going to use another hook that we can import from react router DOM and it's called navigate. So I'm going to do console navigate equals to use navigate. And using this navigate function here, we can navigate the user to our whatever the desired route is. So here it's going to be login. And we have to import that from our library, our constants. So library slash routes and login is a constant and we're just going to navigate the user back there. So let's see if this works. So I can already see that it works because I'm back in the login page despite me leaving this on the protected dashboard route. So let's go back to dashboard. You can see it just kicks us back to the login page. Let's try it. Let's try it again. All right, protected slash dashboard. Okay, look, it just doesn't let us go to the dashboard just kicks us back to the login. So everything's working as intended. To check if the user is actually logged in or not, we have to get our hands dirty now with some actual Firebase stuff. So to do that, we have to go and create our Firebase project in console.firebase.google.com. So we're going to go and add a new project and we can call this whatever we want to just make it social media app. We can hit continue. We don't need Google Analytics. Let's just give it some time to buffer. And now that it's done, we can hit continue. And it's going to bring us to our dashboard. And the first thing we want to do is make a web application by clicking on the web. And then we can call this React app. And then we don't need Firebase hosting. We can just register the app. And it's going to make our very first web app connection. And then we have to add the Firebase SDK to our React application here. It's going to tell us to install Firebase, which I am going to do. Let's go to our console and just stop the server, yarn, add Firebase. So hit enter. And then while that's installing, I'm just going to come here and copy all of the code here. And we're going to go to our library folder and add a new file called Firebase and just paste everything here. So ideally, you don't want to have all of your API keys and your project ID and whatever in your source code. So usually what you want to do is go to your env.local file and just put all of your things like React, underscore app, underscore Firebase, underscore app ID, whatever. And equals to your app ID. But for simplicity's sake, I'm not going to do that in this video. If you want to do that, you can just do what I showed you in the env file. And then just do like app ID and just process dot env dot react underscore app underscore whatever the name of your environment variable is. And now that my Firebase package is done installing, I can restart the server. And then here I'm just going to get rid of all of the comments. And then we're going to import the SDKs for the Firebase products that we want to use in this project. There are three products that we're going to use. One is the Firestore database we're going to use for our posts and our users, our user profile information and our comments and everything like that. And I'm going to use authentication for user authentication and storage for the files, the avatar files that users are going to upload to our app. So we can import and get Firestore like that. And we also have to import get auth from Firebase slash auth and then import get storage. And then once we have all of those SDKs imported, we can go and make our handles here. So I'm going to export this because we might have to use all of our handles for our Firestore auth and storage SDKs. So I'm going to do that three times. This is just going to be a constant DB and then instead of initialize app here, I'm going to do get Firestore app. That's right. And then here let's do auth and then here just get auth app. We also need one more. I'm just going to clone that and we need one more for the storage bucket. So let's do storage and then equals to get storage app. And now I'm going to go to our Firebase console back here and let's go to build and then authentication. Here we have to initialize. We have to get started here and go and activate our email and password signing provider. So let's go and click on this and you have to just enable that just toggle that and then just hit save. And now we have that enabled. And I'm going to go back to our layout component here. So in the layout component, we want the path name to start with protected and also and if the user object is not defined, then we want to navigate the user back to login. So the user object we have to get from our own hook. Ideally what we want to do is we want to make a hook here and the hook is going to be like use auth. It doesn't exist yet. I'm just going to write this here so you get the idea of what we want to achieve here. So this use auth hook is going to be our custom react hook that we're going to write. That's going to return us a user object and also an is loading prop that tells us if the user is being fetched because fetching the user object from Firebase is an asynchronous task, which means that there's going to be a loading state. And while this is loading, we want to return early. If it's loading, we just want to return loading and we don't want to render the children component. So we just want to return loading and we just want to cut everything off. And this use effect hook is complaining to me and giving me some warnings and I think that's because I'm returning too early here. I'm returning before the use effect hook has time to declare. So I'm just going to move the return statements down and save that. And obviously not going to run yet because we don't have this use auth hook that we have to make. So let's go ahead and make that. I'm going to go to my source folder and make a hooks folder and let's just do auth. So we're going to have hooks for posts, hooks for users, hooks for comments and likes and stuff. And I'm just going to make a file called auth and we're going to have all of our auth related hooks. And this is just export those hooks. So export function use auth and return. Let's just return an empty user and then it's loading is false for now. And I'm going to just import use auth from hooks. While I'm back here, I'm also just going to add the user object in the use effects list here just to watch for the changes in the user object as well to run this code. And then I'm going to go and see how this looks like in the browser. So if I go and refresh this and I go to protected slash dashboard, it's going to kick us back to login, right? But if I go to the hook here and I change user to be true, let's just make it a true object. Let's go back to protected dashboard. And you see it doesn't kick us back immediately back to the login screen because now user is defined and the program thinks we're logged in. That's why it's not kicking us back and surrendering the child component of dashboard with the layout component. Now instead of just hard coding the user value to be true here, I actually want to make this so that it fetches the user object from Firebase. So how do we do that? I'm going to use this library called react firebase hooks, which has just gives us a bunch of pre-built Firebase hooks that we can just use to get the user objects and to use stuff like Firebase storage and Firestore, Cloud Firestore. So let's go and install that library, stop the server, yarn at react dash firebase dash hooks. And a while that's adding, I'm going to come here and this is the react firebase hooks documentation. We can see that tells us to import the use auth state hook, which we can just import. So let's import use auth state. And then here, I'm going to just copy this line and put it in our own hook here. And instead of the user, I'm just going to change the name. You can change name here because it's not an object. If it were to be like that, then we wouldn't be able to change. We would have to do something like rename user, the user property to auth the user because it's just a list. So the only thing that differentiates the objects is the positioning, the order. You can just name this auth user. And then loading is loading and then error is just error. All right, and then here are options. If you look at the documentation, you can see options here. Options is optional. We don't need to specify for the options. So I'm just going to get rid of that. The auth is going to be our auth instance, which is basically the Firebase auth handle that we exported from the Firebase file earlier. You see this auth object. So it's just import auth from library slash Firebase and pass it into the use auth state hook. Then here, let's just return the user to be auth user for now. And then is loading can just be is loading. Basically, it's just is loading is is loading. But because of ES6 syntax conventions, we can just do is loading and then it will work the same way. I'm also going to just pass error in to the return object. And now if I save this, go to our React app, we go to our dashboard and we first have to start our server. So you aren't start and just wait for that to compile. And now it's running. It can go back to our website and let's try going to protect the dashboard again. And it's going to kick us out again. So that's great. And now that is actually going to write the functionality for the login form. So ideally, let's go to the form. Let's just close everything. And in the components in our auth and login form here. So we are yet again going to write another one of our custom hooks to handle the function, the login functionality. So it's going to look something like this. We're going to destructure a login function as well as an is loading indicator from our use login hook that we're going to make. And we're going to import that use login from hooks slash auth. And then I'm going to go to hooks slash auth and let's export our function called use login. So it's going to return. It's going to have a loading stage for one because we have to return. Remember, we have to return. We're expecting a login function as well as an is loading state. So let's go and make is loading set loading. And we're going to use state for that false. And then we have to import use date. Can I just use auto import here? Yes, I can. And here I'm going to make a function. It's going to be asynchronous function login. And so here, let's take in some props. We're going to take in an email, a password. And then the third one is just going to be redirect to redirect to is going to be a prop that we can specify where to redirect the user to after the user is successfully logged in. And I'm just going to give it a default value of dashboard. So if we don't provide a redirect to property, it's still going to work. It's not going to crash. Just going to do the default behavior, which is just to redirect to the dashboard. And then here, let's just start by setting a loading state to true. And at the end, we're going to just set loading to false after we're done with everything. And then Firebase has this function called await. It's called sign in with email and password. We can import. So sign in with email and password. It takes in auth, the auth handle, which we import from library slash Firebase. And then the email, which is from here in the password. And you have to import that. So I guess we have to import sign in with email and password from Firebase slash auth. This is an API call, which means we have to await that. And then that's sometimes we're going to run into errors because what if the user tried signing in with a password that's wrong or a user tried signing in and he doesn't have an account yet. So we have to use try catch to catch all of the errors. So we're going to try that, right? And then we are going to catch an error. And then here, if there's any errors, we're going to notify the user. And if we succeed in our login, we're also going to go and notify the user. So how we're going to do that is by using Chakra UI's toast. So I'm going to go to Chakra UI's documentation here and just to show you the toast component. So the toast component, if I hit show toast, you can see that we have these alerts that we can just open up with Chakra UI. And I'm just going to go back to the code here. To use toasts, we will get a toast handle and use the use toast hook from Chakra UI. And then here, if we succeed in signing in, we're going to toast the user. And the title just says, you are logged in and then the status success. So they'll give us a green toast. We can set it's closable to true. And then we set the position to top. And all of these problems, by the way, you can just look at the documentation in Chakra UI to find. Like the variance and the status, the closing toasts, you can just go and look at the documentation. I'm just going to speed here and just give it a duration of 5,000 milliseconds. So we're going to toast. And then after we're done toasting that, we can navigate our user. So navigate, remember how to navigate users. We have to use navigate. So const navigate equals to use navigate import that from a React router DOM. And then once we toast them that you're logged in, we're going to navigate them to the redirect to property. So by default, that's going to be dashboard. And then if we fail, let's just toast them and tell them that logging in failed. And then the status can be error. They'll give us a red toast. And then we can give it a description of error.message. So that's error, the error object that we're going to catch. It's going to give us a message property and it'll give us a more descriptive description of what happened. Like if the user failed, the password's wrong or the user doesn't exist. And we can give it's closable to be true, position top again, and then duration is to 5,000. And then after all of that, we're going to set log in to false. And then, okay, so that should be pretty much everything that we have to do for our login function. Another thing I'm going to do is I'm going to come here and return false. So let's say return false if login failed. And then if it succeeded, we're going to return true. And then return true if login succeeded. All right, so now we can go to our login component here and implement the functionality for this login hook. So remember earlier when I was talking about React hook form, and now we're actually going to have to install that library and use the React hook form's hooks to implement the functionality for a form and handle the unsubmit functions. So I'm going to stop the server and let's install. So you aren't at React hook form. So we're going to add React hook form. And then in React hook form, there's this hook called use form. We're going to import use form from React hook form. And then use form returns a bunch of things that we can use. And it's going to return a register function, which we're going to register. We're going to use register the individual fields in this form. So usually how you would deal with forms in React is you would make a value and then you set it to some state variable and then you do on change equals to whatever set text and stuff. But using React hook form, we don't have to manually type out all of the set state and whatever other props that we have to manually pass into the inputs. And we can just use the spread operator and just do register. And this register is coming from the use form hook and register takes in the name of the field which we use to identify the specific input field. In this case, it's email. So we can just do email. And then we can also do the same for passwords. So I'm going to just use the spread operator again on register, which will return an object. And then we can just do password in here. And we can save that. Another argument that we can pass into our register functions is the validation functions. So in here, I'm just going to do email and validate. And that's going to be a function that we're going to write in our utils folder, which I'm going to create in a second. And password is going to have the password validate function as well. So I'm going to go and go to our source folder and make a folder called util, utils. And then let's do forum-validate.js. And I'm going to paste in a bunch of validation functions. So here you can see we have email validate and we have password validate. And this is basically just a regular expression, just regex. And you can very easily find regex patterns from the internet or just Google it. Or you can just use like GitHub co-pilots, regex suggestions if you have GitHub co-pilot, which makes it very useful. And then basically you can just pass this validation, validating function to the React co-form register function argument. We have to import that, of course. So let's import and I have to save this. And then here we can import, import the email validate as well as password validate. And if you want to customize these validation functions, you can just go and take a look at the validating functions yourself and you can change, you can customize the message, you can change this message to something else and you can add more patterns or just modify the regex patterns if you know how to do regex in here. So with that, we can also get handle submit from the use from hook and they'll just handle our submit first. So handle submit because usually when you submit a form in React, you'll have to do something like e.prevent default to prevent the default behavior from form submissions, which is to refresh the page, but you don't have to worry about all of those details when you're using use form hook from React hook form. So handle submit will take in a function, which you can just call it handle sign in and you have to make this function up here. So a sync function handle log in. Actually you can just call it handle log in to make things easier first. So handle log in, it's going to be a function and usually what it looks like is this handle submit function is going to take in an arrow function and it's going to have like a data object that you can then pass into your handle log in function which you can receive the data object from here, but because there's a shorter way of writing it you can just omit that part and it'll still work the same, but pretty much that's just for your understanding. We're going to receive this data object from the handle submit function from the use form hook, which is from React hook form. So in the data object we're going to have all of the properties that we specified. So we're going to have our email and we're going to have our password. So before we do anything we can just console log the data object for your understanding. So let's go and it's going to it can't resolve React hook form. Let's refresh this and we're not going to be able to submit this form because the button is perpetually loading. So let's go and fix that and let's just comment that out. So test and then test here enter it's going to tell us please include it. So to do that then you can see here when I hit enter it's going to give us this in the console that says email. This is an object that has the email property and also the password. So basically that's how the React hook form works. So we can use this data object and we can call the await login function which we're getting from our use login hook. So remember that in our login hook we're expecting an email and password and redirect to is optional. So let us go and give this login an object that has an email and emails basically just data.email and then password just data.password and then we can just do redirect to equals to dashboard. It's not going to do anything extra because we already have the default behavior here but this is just for us to simplify our future development so when we want to come in and add this redirect to functionality in the future it will be reminded that we can just do it straight from here this handle login function and we don't have to go and modify the login hook. So we can do that and then we can use reset. Basically reset is something we can get from the use form as well. We can reset it's just a function. We can call the reset function here which will reset the entire form basically it will clear the form's input fields. So if I come here type that and we'll hit login. You can see it resets and the login toast appears and that's all working and that's really cool. The thing you're going to notice down the line is if we fill this up and there's an error it's going to reset and we don't really want that to happen we only want the form to reset if it succeeded in logging in. So remember that in our login hook we return false if the login failed and we return true if the login succeeded so we can just const succeeded equals to await login and then if succeeded then we reset the form if not we don't so let's try this again and we hit login we hit login the dot is we use the dot two times right it's add whatever okay hit enter and you can see there's an error and it doesn't reset the form. The next thing I'm going to fix is this perpetual error state that the form is in right now and that's because I have the is invalid here hard coded to be true in the form so how are we going to get the errors from the use form hook? So basically after the new React hook form update we have this new variable called form state that we can destructure to get the errors variable here from use form and I can just console out log the errors variable to show you what it is so if I come here you can see right now the errors is an empty object because we haven't submitted the form yet but if I give it an invalid email address so this is an invalid email address because I don't have to dot right and if I give a password that's too sharp and click login you can see that it prints out an object that has the property of email and password so email and email says email address is not valid and password must at least be six characters long so the email object is basically referring to the email field or passwords referring to the password field and how do we differentiate and set the names of these properties and that's why we had our register our register function and the argument name there email so we've changed this to like email and I refresh that and you can see that it's going to have to refresh this page and let's give it an invalid email an invalid password you can see that the address is to email so basically the how you know the names of the properties of the objects is by looking at the register functions that you have in your form I'm going to change this back to email and I realize that the email address is not valid message has spelled has the word address spelled wrong so I'm going to go to our form validate function here and fix that and change this to email address with two D's and we can refresh this and see that the email address is not valid it's fixed and if we fix this you can see that now the email property is gone from the error object and we just have the password that's left and fix the password by giving it more than six characters you can now see that the object is now empty because we don't have any errors left so let's go and change our error messages here so instead of giving this just error we'll do errors.email and then here this is errors.password and then we'll do the same for is invalid so I'm going to take this and put it here I'm going to take this and put it here alright so if I go back to my browser I should be able to refresh this and see that there's no errors right now if I give it a wrong email and wrong password I hit login it's going to give me this error which is the reason it's giving me this error is because if we look at the email object here you can see that this message is under the property with the name of the message and basically what I'm doing is I'm doing errors.password and errors.password is an entire object in and of itself so this entire thing this is the error.password object so I don't want to be putting that into my children this position here as a react object because that's not a react object that's an actual JavaScript object so I can make it a string by referencing the dot message property and let's go and do the same for email and that should fix it so if I refresh this so the message property is undefined and that's because the message property isn't defined from the start it's only defined if we submit the form and there are errors so we can fix that by saying if errors.email if that exists then we're going to render out the message if not it's just going to not render anything so errors.password and then let's refresh this and fingers crossed it's going to work this time so let's do that let's hit login and it works alright so our login form should be fully functional at this point if I do test user at dream.com and give it a render password and try to login it's going to load for a bit and it should give me an error here that says error.auth.usr not found and when you realize that the login button keeps loading and that's we have to fix that because we wouldn't be able to try again if that happens so it's going to be in the hooks here and it's going to be set loading to fall it's going to be okay so we have to clone this before the return statement because this return statement cuts this function off from running that line which is set loading false okay so let's refresh and test user at dream.com some password and there we go but I want to see the form in action before I actually complete the functionality for the register form so I can go over to my dashboard here and just manually create a user so let's add a user test user at dream.com and give it a password that's really strong so password one two three just add the user here and let's just try to login it's going to tell us wrong password which is a better error in the sense that this error now exists we just don't know the password so I'm going to put the password in here and click login and we're going to what is this error okay it's going to tell us that we're logged in which is nice and it's going to bring us to the dashboard so the login form is now actually fully functional with that done now let's go and build our functionality to log out the user because we're logged in and there's no way to log out at the moment so to do that I'm going to build a nav bar at the top of the screen here and add it to our layout component so go to components and let's make a navbar folder I'm going to collapse everything so that everything is neat again just collapse and just reopen our navbar just add index.js and it's going to be a react functional component name we can call that navbar right so at this point I'm just going to start taking some snippets from my code so that we can speed up the development process and I'm going to just paste in some container some flex containers in here and just we have to import the flex containers from Chakra and then I'm going to show you how this looks like in the browser but I have to first go to my layout component and instead of just saying this is the child and we're going to add our navbar there in our layout component so ideally we want this navbar component to show up in every one of the protected routes pages and that's why we're putting that in the layout component because the layout component is the component that renders in every route that's in the children of the protected route component alright so we see, okay so that's what we see we see our navbar here and you can see it's basically just a small bar here with some tint on the top and dashboard just there because dashboard is from our dashboard component here this dashboard component to show you better what this flex container does I can just change the background to something like purple and can go back to our browser and just refresh you can see that okay let's go back to the dashboard I have to login to the test user I forgot the password oh it's password 123 alright so we're logged in and then that's purple and then if we go back to the code we take this background and we put it here red so that's the red section so the red section is where we're going to be putting our buttons and the purple section is just there for the layout and I am going to take this link component and let's put it in this flex container and of course we have to import the link from Chaco UI and then the router link we have to import that manually so import link react router DOM link as router link and we have to import dashboard as well so let's import that and now we see our home link over there and then I'm going to copy a button component just put it down here as well so under link I'm going to have button and we have to import that from Chaco UI and then we're just going to comment the onclick and is loading so we have our button and our link and we can take out the colors so just like our login function we are going to make a hook for the logout function as well and we're going to go to auth and export function use logout and okay so what we're going to do in here is we're going to have a loading state and we're going to return okay first we're going to return an object that has logout as well as is loading and is loading is just going to be from react state so is loading and set loading is use state false and we need an asynchronous function called logout because it's an API call so to actually call the logout function I'm going to go back to their react firebase react hooks documentation and there's this function called use signout that I can just copy it's a hook that I can just put in here and actually the loading state is going to come from this hook which means we don't even have to implement our own is loading hook I'm just going to change that is loading and we have to import that use signout so where do we get that from it's going to be from let's see here it's going to be from react firebase hook slash auth alright so this it's already up here use auth state and use signout and here we're just going to do await signout go to documentation and for use this use signout the use signout hook you can see that it returns a return is true if this function was successful or false if there was an error so we can just say if await signout if that's true if we succeeded then we're going to toast the user and tell them that they have successfully logged out and we're going to navigate them back to the login screen so we're going to need two hooks for that we just see we're going to need const navigate equals to use navigate that's to redirect the user back to the login screen after we're done with the logout we're also going to need to use toast to toast the user let's just toast first we're going to toast them I prefer to just have it successfully logged out there then we have status to be success we have isclosable true position top duration 5000 getup co-pilot is a life saver by the way and we can navigate them back to the dashboard or more like the login page after they're logged out because the dashboard is just going to send them to the login page anyway and then we can have like an else here like else show error because signout returns false if filled and we should be able to just go to our navbar now and do const logout equals to use logout I think oh did we name we named it use logout alright and it's going to be is loading here we have to import the use logout hook from hooks I don't know why the autocomplete isn't working here hooks slash auth did we export this function yes we did okay and then unclick should be logout is loading is just is loading let's see if that works so I'm going to go back to my app let's hit logout and it works and then we can log it back in at test user at gmail.com there we go logout again just to make sure it works and it does work sweet so now we're finally going to work on the register forum and then we're going to do the actual posts and the users and user profiles and comments and stuff so I'm going to go to my auth hooks file here hooks auth and then under use login I'm going to make a function here a hook called use register and I'm going to write all of the functionality for this use register hook before I go and actually do the UI for the register component which is here right now it's just an empty component let's finish up the functionality first okay so use register let's start with our return value so we know what our target is so our target is going to be to return two items which is the register function and then the is loading function alright so it's loading variable sorry it's loading state variable so let's make the register function first that's going to be asynchronous function so async function register so the register function is going to take in a user name an email password and redirect to again to redirect to the page that basically this is just the page to redirect to after the registration is successful I'm going to give it a default value of dashboard and then let's close this tag here so basically the way we're going to deal with user profiles in this project is by show you my existing sample project here we can see that we have our authentication tab in the console in Firebase console and basically this is managed by Firebase we don't have access to we don't really change the table here because this is basically managed by Firebase and we just tell Firebase to create a user and the user just shows up in this table but we're going to make another table inside of our firestore database and I'm going to make a collection called users and here we're going to store all of the user profile information for our users and I'm going to what I did was I basically just made the ID and here the ID field and document ID the same as the ID in this user's table so if you look at Hostinger you can see it sakes something to for VCN if you go to firestore database you can see that the ID is pretty much it's basically the same and that's how I match the ID, the users from this table and the users in the authentication table and here we can basically store whatever values we want to store about this user, we can store their avatar URL, we can store the date that the user has created the ID of the user and the user's username so basically when we call this register function here we're going to do two things we're going to have to add the user to the fire basis authentication table and we have to also make our own user profile entry in our firestore database so let's go and make our loading state here and so it's loading and settling, we start with false and we're going to set loading to true when we start our register function the next thing we're going to do is we're going to add the user to the databases but before we do that we want to check if the username that the user enter which is from this variable here and we want to check if the username already exists because we don't want two users with the same user names now if we try to put the email like the same email and the email is already registered firebase has built in error handler to tell us that this is an error the email already exists because the username is our own value our own property that we have in our called firestore database it's not in the authentication tab you don't see a username here you just see user ID you see their email but you don't see a username here so we have to manually handle our username our user to prevent duplicate values in our user names so I'm going to call an arbitrary function called as username exists here this is a function that we're going to have to add ourselves later but I'm just going to ignore that and just treat it as a black box for now and just say if const username exists equals to is username exist and we're going to pass in the username here and it's going to be an API call because if username exists has to go and check in the firebase firestore database to see if the username already exists so we have to be careful and remember to add a weight in here and we say if username exists then we're going to toast them that username already exists and tell them that's an error and then we're going to set loading to false and it's very good to get a co-pilot has already written all of that for me so that saves some time but I still have to make the toast handle here and then then we can just do an else here and then we can return actually I don't think we should return false here at all because then set loading false will never run because the return statement cuts this function off before we can reach that point so I'm going to do a try catch block here because the user might be trying to register with a user name register with an email that already exists and firebase will throw as an error so we have to catch the error here so try create user with email and password so this is something we have to import from a firebase as well so up here we import create user with email and password just go all the way back down look for that line and we do that so basically this function expects an auth handle which remember we're importing from the library auth handle object there so we're going to pass that as a first argument and then it's going to take in an email and password so once this is done basically we already have our user created in the firebase authentication table but we have to now go and manually add this user to our user profiles table in firestore so I'm going to do await set doc and now this is actually the first operation that we're going to be doing with firestore so I have to go to our firestore go to build, go to firestore actually I have to change back to my current firebase project and because this is the first time we're using firestore we have to go and create the database for the first time or it's going to fail so I'm going to start in test mode and let's see I'm going to just enable this it's going to take a while to enable I'm going to come back here let's just import set doc so import set doc from firebase slash firestore and then let's come down here so we're going to set the document and then we're going to have to get a document reference here so I don't know why it's not we have to manually import that let's import the document and then we have to also import the database handle because now we're working with the database and not just the authentication featured so here I'm going to just make a document reference the first argument of the document reference is going to be the database handle which is db and now this is going to be the name of the collection that we want to set this document to which is just users and then here I'm just going to put the id of the document and what the id of the document is going to be is just res.user.uid so we're getting the id from the response from the response here the response is just giving us back the user object that has been created by firebase and then it's going to generate an id and it's going to be in the response so we can just do the uid then here we're going to have to pass in the data object here the actual document so the document is going to have an id of the document id which is already here but the reason I'm going to make an id here and just pass in an id here anyway is because the way firestore works is that it doesn't return you the id when you try to get the doc later on and you have to manually parse the id which is why I'm just going to skip through all of the hassle of doing that and just manually passing in the id field here as well usually you don't have to do this you can just get the document's id to know what the id of the document is but I'm just taking a small shortcut here and also that's why I'm using set doc and not using add doc because I want to set the document's id and I want to make that equal to the user's id in the firebase authentication table and I don't want to just generate a different id for me here and I hope that was all understood easily understood and if you have any questions about it you can just ask in the comments I'll do my best to explain in detail so the id will just be the response dot user dot uid and the username will be the username of course but we can do dot to lowercase just to make everyone's username in lowercase and the avatar let's just give it an empty URL for now until the user updates their profile their avatar is just going to be the default avatar and the date basically just the date of creation when this user leaves the platform so we can do date dot now that's going to return a time step and basically the recap of what this entire line does is that we're calling set doc which set doc is typically used to update documents in firestore but if the document doesn't already exist firestore won't throw you an error it'll just automatically create the document and so the first argument is going to be the position of the document basically the document reference where in firestore are we going to put this document at so I'm just specifying it to be I'm just giving it the database handle the name of the collection and the id of the document so yeah and then the second object the second argument is just the document object itself which is the data that I want to put in this document and so after this is done we should now have the user's profile in the firebase authentication table and we also have the user in our own firestore custom user profiles table and then once that's done we can finally toast the user and we can tell them that I'm just going to copy this entire toast and put it in here to save some time and then we're also going to navigate them to the next page and I have to define what navigate is right so const navigate equals to use navigate there and it's going to navigate them to the dashboard by default as default behavior and then and then we're going to catch the error here and we're just going to tell the user what went wrong just basically a toast that says sign up failed and then I'm going to add another block here that says finally and I'm just set loading to false here in the finally in the finally block and just save that okay and so now basically the only thing I have to fix in here is I have to fix the user name exist utility function which I'm going to just make here newTills we can do is user yeah this is your name exist.js and I'm just going to take the code and put it here export default function is your name exist I have to spell function write and this is going to be an asynchronous function because it's a it's a call it's a checked it's a call to the API to check if the username exists or not so I'm going to make a query object here and I have to import the query function from Firebase slash firestore and basically it's we're going to take in the username here and basically we're going to query the collection and we have to pass in the database handle of course and the name of the collection which is users and then we're going to use where the where clause to check where the username of the user is equals to username and I have to import that too so collection and where right so now that we have this query it this hasn't done anything yet it's just a it's just a query specification it hasn't made an API call to the firebase firestore backend yet so I have to now actually make the call to the firebase backend using this query object so I can use the get docs function which I have to import as well so we get docs and we put in queue which is the query object and then we have to await this and it returns a query snapshot that we can get in this variable and then here we can just return query snapshot size grand zero so if the size is grand zero it means that this get docs function found documents that matches this query and that means the username already exists otherwise it's going to return a false which means the username doesn't exist yet so I can save that and we're pretty much done with our use register hook and here we just have to implement the functionality in the UI component in register.js so here in the register form I'm just going to cheat a little and just go and copy and paste my entire login form it's pretty much the same just with an extra field and I'm just going to rename this to register and then here const register is loading equals to use register use register here and so I'm going to have to rename this to something else because it clashes with the register function from the use form hook maybe just sign up that way it doesn't clash and save we're just going to handle register or handle register that works then register or more like sign up and sign up it's going to expect data it's going to expect your username too username is data.username username okay so here I'm going to have to add another form control field for username and type type is just text so we can omit that placeholder is username the name is username errors.username and then errors.username here pretty much shit and we can just change this to register and then we want the loading text to be signing up and then here instead of don't have an account we can change this to already have an account and then log in instead and here we're going to link them to the login page so I can get rid of the register constant and I'm not sure if this is going to work but we can try and open up the console that's pretty much everything we had to do for the registration form we have to change the text from login to register so the heading is here register alright let's just do like test user test user at gmail.com password 123 register the email address is not valid okay so I forgot to change the validation function here so for username instead of email validate we do username validate import that so let's try and refresh this form test user test user at gmail.com password 123 register gives us insufficient permissions I'm going to go over to my fightbase console to see what's happening here it seems like it failed creating that firestore initialization when I was first doing it we have our firestore database here I'm going to try refreshing this page and doing it again so test user test user at gmail.com password 123 again so I'm going to go to my rules and see if we have to read write to be false so this is in production mode which means the security rules it's not allowing us to write to this table if we're not logged in I'm just going to temporarily change this to true and just deploy this in development mode for now because it's not that secure because we're allowing read and write access to the public but that's an issue that we'll deal with later on okay so let's try this register page is working password 123 register and it says email already in use so I feel like that messed up our first attempt messed up the database I'm just going to delete this account here check if there's anything in my firestore database nothing okay so let's try registering again it's going to give us insufficient permissions 123 setting up okay alright so it worked in the end so there was just a weird firebase glitch I think and if I go to my firestore and console and just refresh it I should see a user here in my firebase authentication table and also in my users collection and you see the details here now before I move on and make any of these elements here in the main dashboard like the posts and the profile and stuff I'm going to go back to our code one last time here in the auth section and fix fix up this use auth hook so you notice that previously we just used the auth user and we just got it from the use auth state hook and basically all we're getting in this auth user is all the information that is available inside of this authentication table so we have the email we have the password and then we have the ID that's pretty much it we don't have access to the avatar and the avatar links and the user name and our own custom values that we put in our users firestore collection so let's fix that by implementing some function calls in this use auth hook to make it more robust so what I'm going to do here is I'm instead of just returning the auth user object straight away to the user we're already in the return object I'm going to do some processing I'm going to return a separate different user object that has both the information from the authentication table and from our users firestore user profile table so to do that I'm going to make a different variable a different state variable called user and then here I'm going to do use effect I'm going to change this as loading to a different name because we're going to have two different loading states I'm going to change this to auth loading so every time auth loading changes every time auth loading changes I want to run the code inside of this use effect hook and I'm going to make a function here that's asynchronous and we're going to call this fetch data and in this function I'm going to take the auth user's user ID I'm going to take that ID and I'm going to fetch the data from the firestore database with the ID that I now have and I'm going to return I'm going to make a new user that new user object that has both the user's ID and the email and everything else the avatar and username so here the first thing I'm going to do is set loading to true and I'm going to have my own loading state to fetch the to handle the loading state for fetching the firestore so is loading and set loading use state so here I'm going to set the loading state to begin at true because every time we use this use auth hook we're going to assume that we want to reach a point where we can either determine the user's user profile or the user isn't signed in yet so it'll always be true we'll never start use auth with false other hooks like the use login hook where the loading state begins with false because in the login page nothing's loading in the beginning so it's just it starts to load when you click the button to login but here in use auth it always loads on the page load so we start use state and true and use login is false so here after we set the loading to true we're going to get a reference from the document reference of DB users and an auth user at UID so basically we're passing in the database as the first argument to this document reference and then we're going to reference the user's collection and then this is the ID of the document and in this case the ID of the document is equal to the user's ID the user's UID and now that we have our reference this doesn't do anything yet this is just a reference to a document in a potential document so we haven't sent out an API call to fetch the data yet so now we can use the actual function to call the API and it's called getDoc and then getDoc takes in the reference so I have to import this too so let's import getDoc and then because this is an API call we have to await that and then we can get a snapshot back in the response and then once we're done with all of that we'll go ahead and set loading to be false the problem with this is we're just putting the document snapshot into this constant we're not doing anything with it so what we have to do is we have to take the user and then we have to set it into the state variable user and then we're going to return the state variable user instead of just author user so we're just going to do const const to user no actually let's just do setUser setDocSnap so this is just a snapshot it's just a snapshot containing all of the information and it has a whole bunch of other extra information like headers and other details about the API call that we don't need so we need to call the dotData method on this docSnap this document snapshot to get the data and that data is the data of the document and then we're going to take that data and set it into the user state variable and then we're going to return that user state variable so it's better I guess give it a default value of null because the user is going to start off at null and then this loading is going to be true so what I'm going to do next is because right now we just have an asynchronous function here inside this use effect called fetchData it's not actually doing anything because it's just a function definition it's not the function is not being called so we have to call this function but only if author user is not null and when author loading is false because if author loading is still true that means that author user is still being fetched it's still in a process of being fetched so we don't want to run this function yet so if not author loading meaning if author loading is done and if author loading is done we're going to check if there's a user because if there's a user it means we're signed in we have to spell it right and then if there's no user that means that we're signed out we're not logged in so we just set loading to false and we do nothing here we just say it's we're not signed in if author user is true that means the author loading is completed it's done fetching the user and there is a user then it will run fetchData let's go to the browser and try it out so I'm going to try login with testuser at g.com we do pass password 123 hit login and it says we're logged in but it doesn't redirect this back to the dashboard so I'm thinking what's happening here is if I go to if I go to layout layoutindex.js here and we look at the redirect code it says that if path name starts with protected and not user but the issue is that the user is loading at the beginning and when the user is loading the user is undefined and yes it does return loading auth user if it's loading but this use effect is before this return statement which means that it still runs and it still sees that all users undefined even though it's the user is still loading so one way to fix this is that we can if we can say it's if it's not loading and and then everything the rest so if it's not loading only should you check if the path name is protected and only should you check if the user the user doesn't exist so if it's still loading this entire this entire thing won't run because that's false and then we won't navigate until until is loading is true that means I can just add is loading here I guess in the use effect array so I'm going to refresh this test user at testuser com password 123 login fingers crossed and we are logged in and it doesn't immediately kick us back to the login page which is good work on the sidebar component so you can see here that we have our sidebar and no matter which page I'm on if I'm on the profile page or if I'm in the comments or the home page you can see that the sidebar is always just there and it's constant so that just means that we have to make our sidebar inside of this layout folder and just include it in this layout this layout index.js file here so let us go and make our sidebar .js and just looking the structure of our folders and files I think I'm just going to take the navbar just move it just move it into our layout folder just to ease organization I'm going to update the imports so we have to now import the navbar from component slash layout slash navbar instead of components slash navbar and you can get rid of that folder and just to make sure everything is still working I haven't included the sidebar in the layout component yet I just moved the navbar and that's pretty much all that I did so it should still work and now let's make our component our navbar our sidebar component so we have a sidebar here and I'm going to take some code and put it in here it's going to be a box that's pretty much just the container for our sidebar we can import that from Chakra UI and just to show you what this looks like I'm going to make a red background and it's not going to show up here in the browser just yet because this sidebar is just a component that isn't included yet so I have to go and import the sidebar in my layout component there we go and then here let's just let's see let's just put our navbar somewhere here just sidebar slash alright so that's our sidebar it's on the left side which is on the wrong side and we can fix that with a few styling options here we can just make a flex a flex container and we're going to put our outlet in this flex container or sidebar in our flex container as well and I'm going to wrap our outlet in a box that has a width of 900 pixels and let's move the outlet inside of this box have to import the box as well and then we can style the flex we can give that like a padding top of 16, padding bottom we can give that 12, margin axe auto I give it a full width a max width of 1200 pixels so that's how sidebar looks like and I'm just going to take out the red because we don't need that anymore and now I'm going to go back to my sidebar component and let's go and add some components in here I'm going to have an active user component and that's basically this avatar and this entire component here above the purple bar and that's going to be our active user component so I'm just going to leave that as a comment and then we're going to have to make that purple bar and then the all users button so for the bar I'm just going to use the chakra box as a bar, as a divider and see if that shows up alright so it shows up there and then it's of course going to be pushed down when we have our active user component afterwards, then we're also going to add a button here that says all users so I'm going to put the button here so the button just has like an outline a variant of outline, there are different button types if you look at chakra UI so there's the variants we have button, outline solid outline ghost and link so I'm just using the outline variant, let's comment that out first and yeah so we have all users I just want to find a way to center this if I do the line equals center, see if that works and of course it doesn't work because I'm adding the align center property to the divider box which is basically just like green bar that green bar up there, that's the box I'm adding the center to so it's not going to work instead let me just make another box to wrap everything and put center on this parent box instead it's not going to do anything but if I do align equals the center it should align everything to the center so that's nice next let us actually make this button link the user to the profile the all users page so to do that let's go to our routes folder our routes file here and let's just do export const users equals to protected slash users and we can add that as another child path here, the path would be users element we're not going to have any elements here first so we can just do users a string as an element then here we can link to users and then we can import the users from our routes folder, our routes file and then as link now this link is going to be our react router link which means we have to import that and now if I go back to the browser and click on all users it will redirect us to protected slash users which is just basically a string right now and it is now time to work on the active user component I'm just going to make that this very same file in sidebar so I'm just doing function active user and then it's going to return stack and then we can just active user and then this active user stack I'm going to have code code is just a way to format our username so you see this let's go to browser here that's basically just Chakra UI's code component it's basically for you to write code and show code in your website using that component to display the username in a cooler way so we can simply just type at username for now and you can see that shows up and another thing I want to do is I just want to give a line equals the center here into the stack so that's the center and we can give spacing equals to 5 as well as margin y equals to 8 so it has space above and below the stack as well as between each elements in the stack so like if I had multiple code elements here you can see that it has that spacing without spacing 5 it's just all stuck together so let's reintroduce the spacing and here I'm going to add a button edit profile color scheme let's make it teal and then we have a profile so we have our button there and then I want to make the button have a full width so you see how this is like a long button and this is short so we can always just add width is full and it makes it long and then we can also add a functionality so that it brings our user to the actual edit profile page and as usual to make this button function as a button that will link us to the user's profile page we can do what we have always done which is to add the as equals to link prop so we're rendering this button with the check UI button styles but the actual functional component behind this button is the link that we're getting from React router and then we also need this to prop to know where to go the thing is so the way our URL is going to work is we're going to have something like slash protected slash no alright let me just do it in a string so it doesn't all complete so it's going to be in slash something slash protected slash users or more like protected slash profile slash the user the user ID so if I go to my demo site if I do edit profile you can see protected slash profile slash whatever that user ID is so this is dynamic this is like a dynamic link and we have to go and add that to our router here so I'm going to make a profile profile path export cons profile equals to slash protected slash user slash profile and then here it's going to be an ID so that's basically how you tell React router that this last part of the path here is a dynamic dynamic dynamic parameter which we have to get so that's the ID and then here we'll do profile and then here the element just user profile for a specific ID and then let's do two equals to and then we're going to use a formatted string and we can do protected the protected route slash profile slash and then here we have to add the user's ID and we don't know what it is right so we have to get the current user's ID as well as the current user's username to be able to make this update and not just hard-coded so how do we do that now remember the hook that we made in our hooks and auth here we have our use auth so right here use auth it will return as a user object and it's loading state so here within the active user component I can do user and it's loading equals to use auth and then here instead of just hard-coding username we can do user.id and the problem with this is when the user is loading user is going to be undefined and undefined doesn't have a property of ID doesn't have .id so I'm going to do like if it's loading then we return just loading I guess and then here we can do user.id and then we have to add a question mark behind the user variable because just in case the user doesn't have the user is still undefined it's going to throw an error like if I just ignore oh meet that and let's just see if it gives us an error I'm going to go back to our app here and refresh this so it's I mistyped that it should be user. username not user.id and it should be test user so it works and if I click edit profile it's going to bring us to slash protected slash profile slash whatever the user ID is so I was wrong I don't we don't need the question mark there then this works because this this early return state modes will stop us from rendering this component that doesn't when the user isn't defined yet and now finally let's add the avatar above the user name here I'm going to use the avatar component from Chaco UI and the good thing about the avatar component is that if I if I add so usually you would have to do a source and then give it like slash give it like slash images slash avatar.png or whatever the source is right but for Chaco UI for Chaco UI is the avatar component if you just provide it with like right now if I don't provide provided with anything it's going to just fall back to that default avatar picture if I give it a name of like um tl you'll see t show up the initials will show up like I believe if I do tl space a it's going to do ta let's do like c does it show three characters no so I guess two characters is the limit the max limit because one for the first name and one for the last name so if I did like Donald Duck it should it should do dd all right so instead of just giving it hard coding the name to be Donald Duck we can change the name to do user dot user name and then if our user is that's if our user is test user and then it should be a t that shows up here and I'm going to increase the size of this avatar to make it something like XL so it makes it bigger and also the one more thing we're going to do is instead of just using the letters as the I just want the letters to show up as a fallback like just in case the user doesn't have a profile picture but if the user does have a profile picture and if I go to my fire base here and I just add a profile picture this to this avatar field I want it to be I wanted to show up so I can just go and go for a user profile picture image and that's we're going to we're going to implement fire base storage to enable users to upload their own images by the end of the tutorial but for now for demonstration purposes I'm just going to just google just like right click copy image address just put it in here in our avatar let's put it let's update that and then right now you don't see the avatar show up so you're going to make the source here equal to the user dot avatar and it should show up and it gives us that updated avatar one final touch up I want to do this avatar is that look when I hover it doesn't do anything to my mouse and I can't click it and when I click it nothing happens but if you look at the app here in my demo I can click on the avatar it will bring me to the profile so if I just click on it you can see it brings me to the profile so I'm going to just add a few styles here you can use the hover the on hover target in Chakra UI to specify styles to add when the mouse is hovering over the object so I'm going to make the cursor a pointer that basically just means the cursor becomes like a clickable cursor I'm also going to give it like background opacity maybe opacity of 0.8 percent like 80 percent and it's going to reduce the opacity so it makes it more it makes it feel like it's a clickable object and right now it's not going to if I go home I click on my avatar it does nothing so you can always just do as equals to link link and then do 2 equals to whatever destination you want it to be but I already have that in my button down here and I don't want to just keep adding as equals to link to everything so another way of implementing the link functionality to all of these components I just want to wrap this entire section in the link component so I can just take this out and just do a link and then we just wrap the entire thing and get rid of the ass prop just take the 2 and just put it here in the link and just save this so no matter what I click inside of this link component no matter if I click the button, the code or the avatar I should be able to just go to the link so if I click on the avatar it brings me to the link and that's great now I noticed that that messed up our styles a little bit here so that's because we're messing with the stack component we can just move the link outside of the stack component and now that should fix the styles and we can click anywhere in the stack and link us to the profile so I can do that and link us to the profile maybe this isn't that optimal because if I go home and I click on this empty space and not on the avatar itself it will still link me to the profile so we might want to fix that so the way I'm going to fix this is I'm just going to revert back to what we had originally and make the button a link just have that here in this avatar we're going to make this avatar as a link and then we're going to send it to somewhere and I'm going to take the avatar and put it out in its own separate component because if you look at our demo project here we have avatars in a lot of places in our project we have avatars here in the sidebar we have avatars in our posts and we have avatars here in our all users page and in our let's see in our comments here as well so we have avatars in a lot of different places and just to have our one avatar component in a different file we can just reuse the avatar component in multiple places so I'm going to make a new folder here called profile profile because we're going to have a profile around later on anyway we're going to have profile slash whatever and we're going to have an index.js in this profile at some point but right now I'm just going to put the avatar in here and let's do a react functional component and then we can return this avatar and then we have to import the avatar. I'm just going to comment this too and then we have to import the link as well so let's import our link and let's import avatar because the avatar this component has the same name as the function so that's not going to work. I'm going to do chakra avatar here and then we can do import avatar as chakra avatar and then here we're going to have to get the user from the props so we have that and then let's just say user if not user and then we're going to return loading because the user could be loading here and we don't have access to the is loading prop here so we just detect if the user is null then you just return the loading and then here instead of doing that here I'm just going to take the user and put it in the avatar component so avatar and then the user equals the user you have to import the avatar not from chakra UI but from our own component so we import avatar and then let's fix this too so to equals to our formatted string the base is going to be the profile slash profile slash and then the user ID there we go so can't read properties of null so I'm going to have to add the question marks I was talking about earlier to the user variable now if I refresh this it should it still says null let me check let's just add the question marks everywhere and fix the problem and you see that our mouse we can't click the empty spaces out here anymore and we can click the button or the avatar if I click on the avatar it's going to bring us to the profile wait a minute this this link isn't the right link that we want because we don't want a colon ID appearing in our URL that's just for react router the parse in the backend so I feel like we've done something here yeah this shouldn't be profile here it should be protected so the reason why I was doing that is because if we look at our routes here and we have our profile this string right here is just to be put here for react router to parse that and just understand that the last part of the URL is the ID and we're not supposed to actually put that in our link so I'm going to save this and if we go back and just click on the avatar it brings us to profile and another thing I want to do is I want to get rid of all of these question marks because it isn't a good practice to be adding question marks everywhere just because you never know it's unpredictable and you never know if the user is going to be undefined or not so just doing question marks everywhere I'll just get back this loading state here if it's loading and then we just return the loading and just get that back and see if that works and refresh that let's hope it works, I'm going to edit profile it works and then if I click on the avatar that works as well so that's much better that's better practice I'm going to take a break from all of our user profile editing and profile pages and go and backtrack to our dashboard component so our dashboard component is going to have a section to add new posts and then we're going to have a list of all of the posts just on the feed in our dashboard so I'm going to make this new post component first and let's go back to the code here in the dashboard in x.js and let's replace this so the first thing I'm going to do is edit box and then I'm going to give it a max of 600 pixels give it some margin and padding and here I'm going to have a form and that form is going to be for this input here alright and then in here I'm going to have just a horizontal stack and then I'm going to have a heading that says new post and then I have to import that from chakra and let's refresh this doesn't do anything right now so I'm going to go and look at our routes maybe it's because that's because we have the string here as our dashboard element we're going to do dashboard instead and we should see our new post and that's nice but I feel like I want to downsize the heading let's change the size to something a bit smaller and that's algae and that looks perfect and the next thing I'm going to do is I'm going to add a button here at the end of the new post like to the right of this new post title that's the post button that you see there so let's go ahead and make that button it says post and import this then let's see so we have our post button I'm going to have to make it all the way to the right though so I'm just going to give this h stack justify space between and that should make it all the way to the right and let's give this button a color scheme teal and we're going to do size of medium margin top you know what before we do all of that let's see if that looks yeah it looks pretty much the same so I don't have to add any of the other props but one very important thing I forgot to add here is the type of submit to this button because I'm going to be using react hook form again to deal with the form here and to run the handle submit function and stuff like that and if we don't put this type of submit here to this button react hook form doesn't know that this button is the submit button and it won't probably won't handle submit if we click on this button and the next thing I'm going to do is I'm going to give I'm going to make a text area so chakra UI has this text area that we can we can resize like that for some reason stacked next to you yeah it because that's because it's in the H stack I have to take it out of the H stack and then make itself closing and should be below and we want to get rid of that that adjustment handle there so what we can do is we can do resize equals to none and then what I'm going to do is give it margin top let's give it margin top of five and that's going to space it nicely and I'm going to just refresh refresh this so what you're going to notice is that when I when I go down it's going to it's going to scroll and that's not what we want because like if you look at our demo app here you can see that it automatically resizes and that looks aesthetically much more better and it looks much better so we're going to use this package from npm called react text area auto size which uses jQuery to automatically resize our text area input and some it's just a fancy little detail that we can just add with yarn and then while that's running I'm just going to add some I'm going to add a placeholder here create a new post a new post there and then let's restart the server and to use that component in here I'm just going to use the ask prop again so ask text area auto size and you have to import this so let's import there and then if I refresh this now if we type stuff it automatically resizes which is perfect another prop that you can pass to jQuery UI's text area is the min rows prop it'll just make the minimum number of rows larger so if I do 10 you can see the minimum rows is it's going to be 10 but I'm just going to leave it at 3 the next thing we're going to do is just implement the react hook form hook called use form like we have done for the previous forms so we're going to do const and object equals to use form and then we can get register from here we're going to register this text area as an input so use the spread operator on the register and here we can just do text and then here we can have some validation which is pretty much just do require equals to true and that's all of the validation we're going to do for our text in our post we're not going to make another validation function for this one and let's import use form so let's see if the auto import works it does we can also get handle submit as well as reset from our use form hook and here the form can do on submit equals to handle submit let's say handle post and then we have to make that function async function do we actually we can just do a function handle post to add async if we have to make an api call afterwards and here let's just do reset we're going to get the data object and we can console log and see what it looks like again so basically this is pretty much like our previous example the data object is going to come from the handle submit function from the use form hook and reset just basically going to wipe the form to its original state let's go to our browser and just try it out so I'm going to do whatever here let's hit post and here you can see the data object that's console that's logged into the console and basically we have a text prop that has the text yeah so instead of just logging that to the console we have to actually post this and upload this to the database and for that I'm going to use another one of our custom hooks and I'm going to make a new file in our hooks directory called posts.js and here we can finally make our first react hook that doesn't have anything to do with authentication unlike use auth use logging or use register and then we call use post use add post so export default export function use add post and we're going to return a function here to add post as well as the is loading state and then we have to make our async function add post and this function is going to take in a post object and so we're going to set loading to true here when we first start to add the post and that means we're going to have to make our state variables so we have to import use state and then we're going to just set loading to true then we're going to we're going to make our post inside of this add post and just like our register use register hook where we are making we're adding new documents to our users file table in Firestore we're going to use set doc instead of add doc because I don't want to rely on Firebase to create random document IDs for me and I want to be able to make my own IDs and have more control over it that way I can create documents and add the ID field into the documents themselves so I don't have to worry about getting into the document ID later on so set doc is an async function so I have to await that I'm using the doc reference here in the first argument and it takes in the document object as the second argument so document reference I'm going to make using the doc function this is basically version 9 syntax for Firestore Firebase and the first argument for the document reference is going to be our database handle so db and the next is going to be our collection name I'm going to name it posts just like our sample database if I go to the test social media app test down here so that's the name of our collection it's called posts and it's going to have all of the different documents and here you can see we have the ID field in our document so usually if you use add doc it will just generate a random document ID and you won't have access to the ID field inside of the document itself and you have to use like snapshot.id to get the ID and you can't just there's more there's extra steps involved so I just prefer to add the ID into the document itself so I'm going to do ID here and then the ID I'm going to make using this library called uidv4 from I guess we can just try the Firebase util uidv4 function to see if it works because usually we have to install this other library to make our UIDs worse and if this works then we don't have to install that so in this object I'm going to use the spread operator on the posts on the post object there and then I'm going to just inject the ID there I'm going to inject the date which is the date created as well inject a likes field so likes is going to be an array and this array is going to store all of the user IDs of all of the users that liked this post so when we do a new post it's not going to have any likes yet so it's just going to be empty and date can be date.now and I'm going to see if this works let's go to our handlePost you know I'm going to rename this to handleAddPost so it's less ambiguous and then I'm going to make our hook here let's do addPost equals and then we also need isLoadingUseAddPost and then let's go to our button here and change isLoading equals to our addPost one thing I forgot is when I'm using the addPost function here I have to pass the UID the user ID and as well as the text the text I can always just get from that other text which is coming from the react hook form data object but UID this so basically this ID that you see here is the ID for this document this post the ID for this specific post but here we're going to have to add a UID UID for the owner of the post the poster so the user ID of the person who posted this post so for that I have to go and get the current user object so I guess we'll just use our user or use auth hook that we've made and I can call isLoadingToAddPost add addingPost yeah and then here authLoading so we have two different loading states and here the UID is just going to be the user.ID and one thing to notice is that when the user is initializing is loading the authLoading will be true and the user will be undefined and if we try to add the post here it's going to give us UID of undefined which we never want so we want to limit the user to only be able to post and submit this form once the user is done initializing so here I can use the isLoadingProp to say if if authLoading is loading which means the button should be loading in the loading state which means it's disabled and it's spinning and then I'm going to do addingPost here as well so let's do loading text equals to just loading so we have to import use auth as well let's import that so technically this should work if I go over to our React application and try to add a post it's not going to show up here in the space below just yet because we haven't made the component for the post list but we should be able to go to our Firebase console and see the collection here appear and the collections don't exist in Firestore like the posts collection doesn't exist yet and you try to add a document to that collection Firestore will automatically create that collection and add the document inside of that collection so let's make new post hit post it's going to be loading and then I'm going to go and see if it updates the Firebase console so we do see our posts collection appear and we do see a new document here and the form field is reset and that's how we know that the reset function has ran but the button is still in the loading state and that's because if I go to my posts hook you can see that I set loading to true but I never set loading to false which means I'm going to have to set loading to false here and then I'm just going to add a toast I'm going to go and grab a toast and just put it here just put the toast here and then just to notify the user that they added the post successfully and I'm going to const toast equals to use toast there if we refresh this and we do this is another post and I hit post there we go the post added successfully and then the field is reset and we can go to our console and we can see another post showed up so now let's go and make our post list so we can actually see the post that we've added to the firestore database I'm going to go to our files here and just make a new folder here called posts or just post and in this post I'm going to make index.js and this is going to be one post it's going to be the component of a single post and then we're going to have another component for the post list so I'm going to make that post list.js so this is one post and this is all of the posts I'm first going to make one post and the import react can do post so I'm going to have a box and padding let's have padding of 2 I'm going to have max width of 600 pixels and then in this I'm going to have to import this box from chapter UI as well and then here I'm going to have I'm just going to find a way to render this post to the browser first we're going to make our react function component this post list a post list is going to take in all of the posts and then we're going to return post.map now actually let's just return a box here and then I'm going to give this box padding x of 4 units and then we're going to do posts.length equals to 0 so if the post length is 0 then we're just going to print something out here just say like no posts or something and if the post length is not 0 which means that it's greater than 0 then we're going to do posts.map and we're going to map for every post we're going to return a post and then of course we have to give it a key value here or else react is going to complain because we need every time we use .map we have to add a key to each individual post then we're going to pass the post data object because remember that our post is expecting some data and we can destructure this post data here and just we're going to get some information from this post object and we're going to worry about that later so the key let's just do key is post.id because each post is going to have an id and we can import this post from .slash index right and then here we have to import the box as well let's do that let's go back to our dashboard and below our new our new post component we're going to make another component here for the post list what I could do is also just take this and make it in its own own component so function new post just take this entire thing just return this here just return that and then we're going to have to take all of that and bring it there in this new post component and I'm also just going to just do new post and then here post list so right now it's not going to show anything yet even though we have this if post length equals to zero it's going to show no posts and that's because posts itself is undefined and undefined is not equals to zero so I guess we just have to go to our dashboard here and just give it a mock data of an empty array now we should see no posts we're going to make this a little bit more aesthetic by giving this by making this a text component from Chakra UI post yet dot dot feeling a little lonely here and we are going to give the font a size XL looks better and then we're just going to do text align center so now I'm going to go back to the code and just go to the post component and let's add something into this box I'm going to add this I'm just going to paste this code in and basically what I've done is I've added a header a box section and actions so I'm just going to comment out the header and actions we don't have to worry about that yet basically what that's going to be is this upper part here that's going to be the header we're going to have like the date the time posted and the user who posted this post and then the actions is just bottom bar with the like button and comment and if you make it new post it's going to also have that delete button if you are the owner if you're the original poster so import the text from Chakra UI and just see how this looks so basically what this is is it's basically just breakpoints and we can just we can just set the font size to medium for now and if I go back to my React app and it's still not going to show a single post because if I go to my dashboard you can see I provided posts to be an empty array and when it goes to the post list it's going to just look and see that the post length is zero just return this and instead of just mapping through the post I can just give it like mock data and just do one two three blah right and it's going to give us any number of posts there so what I'm going to do now is I'm going to change the text here to reflect the text that's in our post here so this text value from our Firestore database so to do that let's go ahead and write another one of our custom hooks inside of posts.js so just under add post just under use add post here I'm going to export another function called use posts and here I am going to just make a query to the database all of the posts and just list them out so as usual I'm going to make a constant queue which is the query for our posts and the collection I have to import the query don't I I have to import this from Firestore there from Firestore then we're going to query in the first argument is always the database handle and then the next is going to be our actually I'm just going to do collection here and the database handle inside of the collection and the name of the collection is going to be posts and then I'm going to use reactfire base hooks again for this I'm going to go and look for the top use okay this is not the right this is this is for auth I have to go look for cloud Firestore hooks just look for use collection data just copy so we can just use collection data so const we get values loading error snapshot so posts is loading and then equals to use collection data and we have to import that from we have to import use collection data from reactfire base hooks slash Firestore and then here we have to pass the query in so I'm going to put Q and then let's also just get error because we can get the use collection use collection data we can get values loading error in snapshot so let's just get the error and if there's an error we can just throw the error so we can see the error in the console and just fix it and we can return the posts and is loading so if I go to my post if I go to my dashboard here and I do const posts is loading posts loading equals to use this is not even the right place to do it I'm sorry this is in the new post component I'm going to be doing this in the dashboard component use posts and I have to import this so instead of just hard coding the posts I'm going to do posts here and just go to our post list and see if everything works and we're getting the posts here and we're putting the posts to our individual posts and here we are going to just do the text and we're going to destructure the text from the post object so if I go back to Chrome and I go to my app you can see that it shows this is another post and new post and that's because in our firestore database we have this is another post and we have new post and if I go and add another post here let's do that it goes to the bottom of the list I can add more and sometimes it goes like in between the posts and that's just weird we don't want that we want the newest post to be on top and the oldest post on the bottom so we can modify or hook in our vscode posts use posts hook and here in this query I can go and modify this and we can add the order by clause and you have to import that from firestore so we can order by the date this is why we created that date that time stamp that date field and we can order by ending so now you can see the newest one always goes to the top this is the newest post it should go to the top there we go and I'm going to come back to my dashboard component here and I realized I'm not using this post loading state so I can just do if is loading then we'll return loading loading posts and the next thing I'm going to do is I'm going to fix this thing so I'm going to go and make this header.js component and let's go and close out the rest of the things that we don't need so this is going to be a react functional component and that's I'm just going to put the container in here it's going to be a flex and we have to import that from the chat and like if I go to the browser right now we don't see anything new because we have to import we have to import and use the header so import header and let's use that so we do see that small header appear and I'm going to add a few things into this header first of all if we look at our demo app we're going to have our avatar and remember that we already made that avatar component inside of our profile so this avatar here we can just import that and use that here so import avatar and just use the avatar here and one thing to note is that this is going to give us an error because avatar here it expects us to give it a user object and we can't just get the current user with use of the use of in here and just put the current users into the avatar here because as because you can see that the user depends on the owner of the post the user ID of the poster and not the current user itself so we have to somehow get the post to get the user ID in here in the header and then just use that to get the details from Firestore like the user's avatar and the user username so let's just do UID and here we're going to go to the post and provide the UID here so this is the ID of the poster and then the UID is something that we can get from the post we can destructure that from the post object because if you look at the Firestore database in the post we have the UID field which contains the ID of the owner so let's save that save this and right now we don't really have a way to get the user's information that's why we're going to have to make another hook here I'm going to make a new file called users and here we're going to have all of the user related hooks so let's make this user user let's take in the user ID this hook is going to be pretty simple because all we have to do is read the document in the user's Firestore collection that contains all of the user information that we need so let's react firebase hooks for this we're going to use the use collection data hook again so we can do const we're going to get the user and the user basically just is loading and then error we don't even need error I don't think we can just omit that and we just use document data and then here it's going to take in the query and we can just make that query constant so query it's going to be a document reference and in here I'm going to have to close that and in this document we're going to pass in the database handle and then we're going to pass in the collection name which is users and then the ID and see the formatting is right do I need one more right doc is just not imported so I have to import that and then we can just return the user and is loading that should pretty much be our use user hook that's all we need so I can go back to my header component here and we're getting the UID from our from our post because we're passing in the UID to our header so now that we have access to our user ID we can just get the rest of the user information and just pass it into this avatar here so we're going to pass user here and we're expecting to get that user object so we can just do const user and then is loading equals to use user and then pass the UID I have to import this and let's say if loading return loading that should work if I go to my browser and you see that we have our user and we can always just look for sample a dog image and just like just get one of these images let's just go and copy image address and just like change the avatar and you can see it changes the dog picture to the avatar and one thing is that this avatar is a bit too huge and if I go and do size equals md here it's not going to do anything because this avatar is not the checker UI avatar component it's our own custom avatar component it's just a profile here which means I'm going to have to manually take the size in let's have the default size to be XL instead and just put size equals to size here so if we don't specify a size in the props for our avatar component it's just going to default to XL but if we do so choose to override the size with our own custom size then that will work you can still see that this is XL and this is M I believe the next section I'm going to add is this user profile and then the date like how long ago was this post added so let me go and take that piece of code and just put it down right beside the avatar it's going to be a box that has a button for the username and the text for how long ago this post was created so I'm going to import that let's import that and import the text as well so if I save this you can see that we have our sample username I don't even need the ELISA it's just a sample username and then nothing happens when I click on it right now and it's just giving me the sample username and not the actual username yet so let's go ahead and make this print out the actual username so remember we have access to this user object I can just do user.username so there we go we have our test user it's just hard coded right now so what I'm going to do is I'm going to get the date from the post so here instead of just passing just the UID I'm going to be passing the date as well and the date is something we can destructure from the post as well that's the date created it's going to be a number, it's going to be a time step and you can get that from our post document you can see that each post is going to have a date and that's coming from our post use add post hook and remember when we made new posts we always add the date property and it's going to have date.now in it so that's going to be the time stamp of when the post was created so now that we have access to that date I'm going to install a new library it's called datefns so datefnsnpm so this is the library that we're going to use to format the date because we have to somehow convert this time stamp number into a string, a readable date for humans to read and so we're going to just npmi datefns or we could just use yarn so let's stop the server yarn add date-fns and once that's done installing I'm just going to go here import format distance to now from datefns just yarn start and then here I'm just format distance to now and then date and you can see that it's working it's 18 minutes ago, 19 minutes ago, 42 minutes ago if I make a new post right away and I hit post you can see less than a minute ago and that's just going to automatically format and update so the thing is with our current setup if I hover over the avatar and click on avatar it's going to redirect us to the profile because we are just reusing the logic from our avatar component and the avatar component takes the user object and since the user object has all of the different user information in it it has the avatar link it has the user's id so it can just automatically link us to the user the user profile page but if I click on the user button here in the name of the user it's not going to do anything because our button doesn't have it doesn't know what to do it doesn't have an on click it doesn't have a link component so what I can do is I can just go to profile here let's just make user name button as a component make a react functional component here and then I'm going to go to my header and I'm going to take the entire button thing and put it here so we can reuse this and I'm thinking to update this code as well to make it so that I can click on that um I'm going to just have to get the user the user information here whoops to get the user information here and then we do user.username and then we can do as equals to link and we have to import the link from react router and then we have to add it to so let's use an fstring protected profile user.id and then here I'm just going to reuse that user name button and we can pass in the user like that like just like how we do it to the avatar see if that works oh that's just because I I have to put this user in a prop in an object because what I was doing is I was just like it's supposed to be prop and then like const user equals to prop I was just doing user here which wouldn't work you could just destructure it like that and then it should work now if I go and yeah it works if I click on it brings us the profile that works we can reuse that later on and maybe I'll get rid of this view profile here and just make the user's username clickable that way you can save you can just reuse code I just noticed that our post list is aligned slightly to the left and I want to make that center so what I can do is I can fix that by going to post list and let's just do align center so that looks much better but you're going to notice that our text is and our buttons are aligned more to the center as well we want that to be aligned left so what I can do is I can go to the individual posts which is in the post index.js here let's just do text align and center that and we should be able to did I spell that wrong I did all right so let's refresh this refresh this oh yeah it should be aligned to the left there we go much better and the next thing I want to fix is if I scroll down here you can see that the new post it like goes behind the nav bar but we can still see that so I don't want that to be transparent can you go to our nav bar in our layout here nav bar just add background of white and then that should cover it up and the next thing I'm going to do is work on the actions side the actions section of the posts so you can see this bar down at the bottom with the like comment and delete that's the actions component that I'm going to include here in our post so let's go ahead and make actions .js make it a react functional component and let's just uncomment that import actions and then let's save that so in this actions component I'm first going to make the container let's do flex and give it some padding I'm going to give it a padding of 2 and I'm going to make an intersection here let's first make the like section so the like button at the bottom left I'm going to wrap it in a nested flex container and then I'm going to align items just going to center it from the get go and here what I'm going to do is I'm going to use this component from track review icon button so icon button it's a self closing tag and you can see that's going to be our like button and let's make it slightly smaller and let's add the color scheme to be red because hearts are always red and if I save you can see that becomes red and then we can make the variant equals to ghost there we go and now we actually have to get the shape of the heart like the like the like icon so we need to do react icons it's a package that has all of the popular icons that we can just include in our project if we look at like or like f a heart so that's our heart that we can use and then that's like if we click on the like and it's light then we'll use this icon it isn't light then we're going to just use that regular heart that isn't filled so first I'm going to start the server yarn add react dash icons and that's how we install up the icon packages I'm just going to click that to copy this to the clipboard import this as well as f a heart we're not going to use f a heart for the time being because we have to implement the functionality to detect if the current user has liked this post or not but we're not going to do that yet we're just going to focus on the UI and then restart the server then let's add the icon here so what I'm going to do is do icon equals to f a regular heart backslash so if we go back we can see that there's our that's our demo app if we go back to our applications the browser and refresh that there, there our heart that's our heart right there and I'm going to make this rounded like if I click on this button you can see it's kind of rounded the highlight which means we can just do is round and that's rounded and what I'm going to do next is I'm going to add a number here just below the icon button and this number is going to represent the number of users who has liked this post I go to the browser and you can see that number appears just beside the like button and I'm going to implement the functionality to read the number and whether or not the users like this post and I'm going to get the post information from the props so let's get the post and we can destructure the information from the post object now before we do that I'm going to have to go back to our individual post cards and you can see that action components being called here I'm just going to pass the post the post data object in here so we can have access to that in actions and while I'm doing that I'm also just going to modify the header here because you can see we're manually passing in the UID and the date to the header what I could also just do is just pass in the entire post that way we don't have to destructure the post here or we still do we just don't need to take out the date and the UID we just have to get the text and put the text here and in the header I'm going to receive instead of receiving UID and date I'm going to receive a post item that we can destructure in here taking the UID and the date and I'm just going to get rid of that import and then here in our actions I'm going to destructure the post to get the like information so if I go to Firestore database console you can see that each post document is going to have this field called likes and it's going to be an array containing a list of all of the user IDs of the users that like this post so I can get the likes from this post and then here I'm just going to do likes.length and if I refresh and go back to the browser you can see right now there's no likes so the likes.length is 0 and then I'm also going to do isLight let's just set this to true for now and in the icon button if isLight is true then we want to display this fahart icon if not we're just going to display this regular heart so isLight, I'm going to use this ternary operator to say if that's true we rendered fahart otherwise we rendered regular heart so you should see that it's like right now if I change this to false that would be false so instead of heart coding if isLight variable is true or false let's actually go and write functionality to detect if the current user has liked this post or not so what I'm going to do is I'm going to make a new post here I want to like this post and let's post that and then we should see that appear in our console there so I want to like this post and right now the likes array is empty but we can add a field here and let's just go and grab our current user id and I'm just going to take that and copy and I'm going to simulate a like by going to our post and just adding this to our array of likes so I'm going to add that and you can see now the likes arrays has a length of one so technically we see one here and that's what we do see we see one in this post and zero in the rest so we note for a fact that our counter is working but now we have to just highlight this like button so that it shows that we have liked so to do that we first have to figure out what our current user id is within this action component and then we check if our current user's id the logged in user's id is within this likes array so if I console log likes and refresh this you should be able to see that we have like an array and it has the length of one and that's our current user's id but we need a way to check if our current user's id is inside this likes array so what I'm going to do is is like is equal to so likes.includes so this is a javascript built in method for arrays we can use this includes method to check if something is inside of this array so I'm going to just hard code this our current user's id here for now you can see that works these posts don't have likes but this one does have like and we can just try it on another post like new post let's just add our user id into the likes array and we should be able to see that there are two posts here there are likes so that one here that one there alright so let's go and use our use auth hook so use auth this is our custom custom hook that we made and it's going to return user object and is loading let's just rename this to user auth or just user loading and I'm going to do user user.id and we refresh that and we can see it still works and so now it's time to add our on click to our icon button here so ideally what on click will do is just call a function like toggle like and then we're going to get toggle like from our custom hook called use toggle like which we'll write just in just a second and then we're going to destructure two things we're going to destructure the toggle like function and also the is loading which will just be just is liking I guess or like like loading I feel like I prefer that more okay then here we can add is loading equals to just I'm going to show you what a loading icon button looks like so I'm going to just set that to true and you can see when it's loading it looks like that which is very nice and that's from Chakra UI so let's just uncomment that again and another thing I want to do is I'm going to have to pass in I'm going to somehow have to tell this use toggle like hook what the current user's id is because then this toggle like hook is going to return a function that toggles the like on and off and it has to know what our current user's id is in order to be able to like or unlike the likes array in the post document so let's just preemptively just give it the post I give it an object that has the id which is basically what we're going to get from the post and that's going to be the post id and it's going to give it is like we are also going to give it the current user's id in that way we can I'm just going to just change the name to user.id it's going to have user is going to be undefined when user's loading so I have to add a question right there and another thing I want to do is I want to change this as loading to like loading and I'm not going to stop just there because I want to also make it loading if the user is loading because I don't want the user to be able to like the post if the user object isn't initialized there and the use toggle like won't be able to know what the user's id is so I'm just going to block the user if either one of the hooks are still loading and it's like it has to go above like that and then now let's go and define this function this use toggle like hook I'm going to go and make that in posts of course let's go and close out all of the other tabs here just import use toggle like from hooks slash posts we have to export that here so export function use toggle like and remember that use toggle like takes in this entire object here this configuration object which is just a uid here and what it's going to do is we're going to have to return so what do we promise to return again if we look at our code we can see that promise to return a toggle like and is loading so I'm going to have to return a toggle like function and then is loading state so it's loading state I can just make that very easily use state false and then we also have to do an asynchronous function called toggle like and the first thing I'm going to do in this asynchronous function is just set loading to true and the next thing I want to do is I want to get the document reference to the post document which I want to modify so if I'm liking this post then I want to get a reference to this posts document so remember that recall that I'm getting the posts id in the use toggle like configuration object which means I can just make a document reference to the Firebase document and I'm going to pass in a database handle as the first argument and the collection name is post and then that's the post document id which is what we're getting from there and then there's this function from Firebase called update doc which we can use to update specific fields in this case we just want to update the singular field of the likes array we're not going to touch the date the id the text and anything else so we're just going to use update doc it's an api call which means we're going to await that and then the first argument is the document reference basically we're telling update doc which document we're intending to update and then finally this is the configuration object the data object they're going to pass in so remember I was just talking about how we're going to update this one singular field called likes so let's just do likes and then recall that we're getting is liked from the configuration object so if it's like this true we're going to remove the our users id we're going to remove our users id the uid from the documents like array but if it's like this false then we're going to add the uid to the array so in Firebase we have this function called array remove and array union and these are both functions that we can use remove is to remove stuff from the array and union is to add stuff to the array basically you can add arrays to an array and just union the two arrays but in this case use a ternary operator again if it's liked is true that's true then I'm going to remove otherwise I'm going to add so this is what it looks like and then in the array remove I'm going to pass in the users id so the same goes for array union and then once I'm done with updating the document I'm going to set loading to false so pretty much pretty much done with the use toggle like hook if you don't like how this looks you can always just take the object out and you can just do like const config object equals to that then you can just do config object or just name it config I guess there and then I'm going to come here and refresh this let's go ahead and see if this works so if I like that it should become zero and unlike that works and if I like that it should be one it should be one and it basically just works and you can see that it automatically adds the likes and the likes to the likes array so if I were to go and log out here and register with a different user let's do user2 and then user2 at gmail.com pass oh it must be six characters long user2 1 2 1 all right let's sign up and we're logged in so what I'm going to do you can see here that this like count is one but the like is not liked because that's liked by another user so if I click on this it's going to become 2 and that's going to like so that looks very nice and we can just go ahead and like if I log out and just go back in with user test user at gmail.com you can see the same yeah you can see the same thing here you can just unlike that it becomes one so that's sweet so next I'm just going to work on the comments button and I'm just going to take this and just steal the code for the like button and just change some things to make it the comment one so first things first let's see how this looks in the browser and we see that that's the comment button looks pretty similar and we just have to change the color and the icon to make it match just what I'm going to do I'm going to change the color scheme to teal and I'm going to give this comment flex some margins so let's give it margin left equals to 2 that way it's further away from the like button and I'm going to change this just comment out as loading and on click functionality and the icon instead of doing is liked you want to show two different types of comment icons so if there's no comment it's going to be that empty comment if there are comments it's going to be the failed comments so I'm just going to import some icons here so FA comment and FA regular comment so you can see it's pretty much just like the format of how we import the hearts icons and here let's just do this for now just do like FA comment and I'm going to change the text here so we don't want to do likes dot length anymore we're going to do something like comment comments dot length but right now we don't have access to this comments object so I'm just going to hard code this for the time being just add like five now let's make it so that the button when you click on it brings you to the comments page so I'm going to do on click here and instead of doing on click I'm just going to do as equals to the link from react router and we have to add this to and then we can just add the format string to so our rods going to look like something like protected slash comment slash the post ID so that's going to be the post ID which means we can just do protected here slash comment slash and then this is going to be the post ID if I save that and I click on this you can see it brings us to comments and the post ID and this route doesn't exist so let's go and create that I'm going to make I'm going to go to library routes let's do comments and then here let's do protected protected comments and then the ID and here comments and the element will be post comment all comments for a specific post ID and I'm also just going to just make that folder here for now comments and then here we can have index and react functional component here we can just call this comments if I go to routes instead of putting string here I'll just put like comments so it's going to be from component slash comments and here I'm going to save this and just put the string here and we don't need to react import so if I go back to my home here I click on this it brings us to comments and then the comment the post ID but before I go down the rabbit hole of making the post page for all the comments I'm going to go ahead and finish up our actions bar here first by adding that delete button to the right side of the post so let's close that first and close the routes just close and everything that's not needed alright let's just get rid of that and we can leave this loading in alright I'm going to take the icon button here just clone it and just put it here and instead of this icon we can just go back to react icons and just search for a trash so I'm going to take this trash icon or we can just use that it doesn't matter then we can import that too and then here I'm going to use that icon instead and let's change the color scheme back to red because I want the red trash can to look dangerous right if I just did like teal just showed you what this does it's going to be it's going to be there it's not going to look very threatening so let's go and make it red that looks a bit more dangerous to click on and then I'm going to make it all the way to the right side which means we can just do margin left equals to auto and they'll automatically give us all the margin that we need or you can always just use a chakra separator but I'm just going to use margin left because it's easier this way and we have to take away this as link and this too and then we have to change the is loading and then we have to make the functionality to delete post so let's just say that we have a function called delete post and we can just call on click to call the delete post function which of course doesn't exist yet but we can always make another hook for that so we're going to have so many hooks by the end of this tutorial we're going to make const delete post and then we're going to get is loading so is loading let's just rename this to delete loading and here down I'm going to just do is loading is delete loading equals to use delete post and then here we have to pass in the ability of the post that we're going to delete which is going to come from here and just check if everything is alright and you can save that it's going to give us an error because use delete post doesn't exist yet we can import that here use delete post and then we have to go ahead and actually write that function export function use delete post that takes in an ID that's right expect us to return is loading state as well as delete a delete post function to call before I actually write this delete post function I'm just I'm just going to finish up my comments page first and you'll understand why later because when we delete a post there's going to be a comments collection if I just go and show you the collection in my test firebase console you can see that we have our comment section and if we have comments in the specific post and we delete the post document without deleting the comments documents that are linked to that post the comments are just going to exist forever in our cloud firestore and it's not going to be linked to any valid post because the ID of that post that was supposedly that the comments supposedly belong to is now gone because that post is not deleted but the comments just floating out here so I'm going to just finish up this use delete post hook later on after I'm done with the comments page for now I'm just going to make this an empty function and I'm going to just make the is loading state use loading false there we go sorry use state so let's go ahead and start working on our comments page so here instead of just returning this div I'm going to return a box and we're going to just align everything here in this box to be centered and then we give it a padding top of 50 and then the first thing I'm going to do is if I go to my example here so in a comments page the first thing we're going to have is this post card on a very top of the comments page so let's go ahead and add that post and it's going to give us an error most likely because if I go here it's going to give us an error post is undefined because the post expects a post data object which then destructures the text so we have to go ahead and fetch our post but how do you fetch our post because right now we don't have a post we can't pass in any props here that says like post ID because this is a top this is a top level component that React router is rendering you're going to see that the post ID is actually in the parameters in the URL and if we go back to our library and routes you can see the route for our comments has this back part that's a dynamic URL dynamic parameter and that's the ID so we're going to use the React routers use params hook inside of here const params equals to use params right and you can just console the params to the we can just log the params to the console to show you what that is I'm going to just comment this out because it's giving us issues and just refresh this just look at the console you can see that we have our params here we have the ID which is whatever if I change the route here and we call this post ID and refresh this you can see it becomes post ID here in this object instead of ID the meaning of this property in this object is dependent on what name you put in the configuration string here for the path in the routes so I'm just going to leave it as ID and now we can destructure the ID for the params and that's going to be our post ID but because our post component here is expecting a entire post data object and not just the ID we're going to have to somehow get we have to fetch our post based on this ID yet another hook for this export function usePost and here that's going to be the post ID and just do const so I'm just going to be using the useCollectionData hook from React hookform again we can just do that and we're going to have to make our query variable here so constQ equals to and I'm going to just take out an error Q equals to let's see we're going to have to use again a document reference and we're going to pass in the database handle the name of the collection and the ID of the document then we can just return our posts and is loading then let's go and use post constPost and then is loading so this is supposed to be singular because it's a singular post it's just one document it's not a collection it's not a bunch of documents so it's just one we have a different we have a different hook for the plural of posts to fetch all of the posts and this is just one post so two different hooks for two different purposes and then I'm going to just go and pass in post equals to post and then just say if is loading then return loading dot dot dot and then we have to pass in the ID to the use post arguments because it remember it's expecting us to give it an ID for the document so let's refresh this it loads for quite a bit and we'll see if it works we're getting an infinite loop here alright so this counter was going crazy and I think we're having an infinite loop somewhere in our code which means something is not quite right just going to do a quick check here and there we go this is the issue so we're using the use collection data hook inside of use post but remember we're just trying to get a singular document from a singular document reference so we should be using use document data not use collection data so that should fix it and if I go back and refresh hopefully we'll get that infinite loop error thing again so if I go to comments we get our post and that's working very nicely so the next things that we're going to add is the add new comment section like this add new comment section and also the list of all of the existing comments so back here in the comments page component I'm going to just add a new component here under the post card component and we're going to call this new comment and of course we have to make that new comment.js file so here I have to import new comment and because the tutorial is already going it's been going on for quite a while I'm just going to paste all of the UI code here so I have to go through step by step on how to make the UI but the functionality is not here yet so I can just save this we have like a box and we have just like containers inside of containers to style them and we have a form that doesn't have it on submit we have an input here and a button and that's all that we have if I go back to the browser and refresh you see that we now have that additional comment section it doesn't do anything though if I just hit enter it's going to do the default form action and it's going to refresh the page and then add that question mark in the back of the URL path that's just the default form behavior in html so what I'm going to do is I'm first going to add an avatar so if you see if you look at our demo here we have an avatar here that we can click on and go to the profile and that's we have to add that to the left side of our comment bar and recall that in our profile folder we have that avatar component that we can just reuse here so avatar then we can save that it's going to give us an error because avatar expects you to give it a user at the very least you can omit giving it the size but just default in XL we might just make it small or extra s so we have to give it a user and we're going to get the current user so const user we've done this a million times so I'm just going to just speed run through this so use auth and then so is loading is just there let's just put the user object in here I'm just thinking if we should return loading if the user is loading let's just say if loading just return loading because we don't want to be able to write new comments if we don't know who the current user is so refresh and you can see that it's kind of huge right now that's why we have our size override property there the size equals to like an X s and that's a bit too small maybe SM now again if you are following along with this tutorial and you just saw me paste a bunch of code in here you can always go to the link in the description there's going to be a GitHub repository where you can just go and look at the completed code and just copy all the UI components in and I'm going to just explain why we have this autocomplete equals to off thing here because this is a form and this is a text input inside of the form and if we don't have autocomplete equals to off it's just treated as any other input just like the input for the user name or email and the browser is going to try the autocomplete whatever it has in its history so if you've typed user at gmail.com in this in the text input before it's going to try to autocomplete that email which we don't want because this is a comment section and we're not intending to put like email addresses and passwords in here so just that's just for it to turn off the autocomplete and the next thing I'm going to do is I'm going to use react hook form to register this form and override the default unsubmit function so as usual you can do register equals to use form and import that and we're going to register this input field by using a spread operator and just text and the only validation I'm going to do is just make it required and let's just do handle submit here and import that so handle submit and also reset because we want to reset this text after we made a new comment we want to make it blank again after we submit handle submit and it's going to take in a function here and let's just call this add comment and we have to make that add comment that add comment thing in the function so let's just function let's just call it handle add comment instead there and then let's handle add comment oh yeah we have to put the parentheses just reset so to add a comment we're going to have to call something like use add post but it's for comments so it's going to be something like add comment like that and then it's going to come from a function from a hook so we have to make our own custom hook to add comments so I'm going to go to the hooks folder and make a new file for comments .js export function use add comment and then here as usual we can have a loading state then we are going to return this loading as well as an add comment function that we have to make so async function add comment first thing I'm going to do in this add comment function is of course as usual we're going to set the loading to true the next thing I'm going to do is I'm going to use this set doc function from firestore to add a document to our comments collection so if I go to my comments here you can see that in my example firebase console you can see a comment it's going to have date which is the date added the date of the comment and then the id of the comment which is going to be the same as the document's id and then we have the post id so which post does this comment belong to and then we have the text which is pretty much the content of the comment and then uid is the person the user who posted this comment so let's first make that id and we have to do id equals to uid v4 we can import that from firebase slash util and that's just going to let us generate an id and this is going to be an api call so we have to await this and then let's have a doc ref here and then we have to make that doc ref later and this is going to be the object the data that we're going to enter into this document so I'm going to get the data from the arguments here the parameter comment and then we're going to use this spread operator to spread the comment and then we're going to inject the id into this document so now it's going to ask us to make this document reference which we're just going to do with doc and then the first argument is always the database handle we have to import that and then the comments collection and the id of the document and then once we're done with that we're going to have to toast which we have to import so const toast equals to use toast and let's do toast I'm just going to add the title comment add it and then let's have the status of success is closable true and then position to be top and then let's have the duration be 5000 and then once we're done with all of that we can finally set loading to false okay so if I go back here I can come and add the argument that this add comment function is expecting so we can add the text and we're going to get the text from the data object which is going to be passed to us from the handle submit function which is going to pass the data object into the handle add comment so we can do data of the text and we also have to include the post id and we're going to get the post id from the props here and have to go back to the comments here and let's add you know what let's just pass in the post as a whole object and then here we can just get the post object and then we can destructure the id let's rename this to post id from the post object to post id so let us check what other information do we need to put into our comment document so we're going to have to add the date and then the uid so I'm going to just do that so date is date.now and then uid will be user.id and I have to of course get the add comment function and then the is loading become comment loading use add comment import that come here and save this import use add comment so the thing is this text field is the only thing that has to be in this handle add comment function because it's the only thing that's reading from the data object this post id we can actually just put it here can just put post id and then just go to the hook and just get the post id um and we can just get rid of that here the date.now doesn't even have to be here because we can just inject the date from this use add comment hook so let's inject our post id and then our date let's do const date equals to date.now and then the user id we can also just put that here so uid is user dot id and so the only thing that's left in here is the text and we don't even need um that object in that case you can just add comment and just give it the text and then here in this add comment function we no longer have to use the spread operator like that we just do text um there text we save that and hopefully this is not too confusing I'm going to go and make this loading state uh apply to the button so comment loading and also I don't want the user to be able to comment before the user is the off user is done loading because then the comment won't have an owner user id so I don't block them from doing that and let's see if this works of course it's not going to show up here because we don't have the comments list component yet but if I type this is a new comment here and add comment we should get the toast saying comment and the toast is coming from our use add comment hook you can see the toast there and then if I go to my fire base console I should be able to go and look at the comments collection here and see that this is a new comment appear if I have another comment and hit enter you should see another comment appear and so our add comment functionality is now completed alright next we're going to go and write code to fetch the actual comments from the firestore database and make it show up here right underneath our add new comment component so to do that I'm going to make this comment list.js and it's going to be a react functional component and then we're going to include this comment list here inside just right below our new comment component we don't need to include the post in there just yet alright here and in this component I'm just going to pull this up in the browser and see what it looks like so here comment list and just in here we're going to fetch all of the comments from our firestore database and as usual we're going to make our own custom hook for this and we're going to use the use collection data hook from react hook form in the comments.js file here I'm going to export the export function called use comments and in here we're going to fetch all of the comments but first we have to know the ID of the post that we're fetching the comments for because we don't want to just get every single comment and we just want to find comments that belong to this post this specific post with this specific post ID so we can construct our query variable here and then the first I have to import that the first argument is going to be the collection reference and then we can pass it in the database handle the name of the collection which is going to be comments and then we can use the where clause as you can see the way get up co-pilot as recommended me to do this is there right away we're just going to use where and we're going to check where the post ID matches up so we want to check where this post ID field matches the post ID that was provided to us in the arguments of the use comments hook and then we're just going to save that and let's do const comments I'm just going to check if there's another hook like that in here posts maybe we're just going to take this line just pasted in our comments hook so then we have to import use collection data if you guys have been looking through the documentation and you see this use collection hook I don't really use this hook I use the use collection data hook more and that's because the use collection hook just returns to you the snapshot and then use collection data helps us convert that snapshot into a variable before returns the value to us and using the use collection data hook you still have access to the snapshot variable it's just at the last position of the list and you can also use collection once basically use collection data it's real time so every time you update the information in this database in the console it will automatically show up in the front end and we don't have to do anything to manually refresh the data it's why I like to use this hook so much and I'm going to change this to comments it's loading error I'm just going to take out the error variable because I never used that anyway if you're building an app and you want the best practices ideally you would take the error variable and like detect if there's an error and you want to warn the user if there's an error because you don't want in the actual production environment you don't want your program just crashing on actual users who are using the production ready application so now I'm just going to return comments and is loading so I gave that whole speech about handling errors I'm just going to for best practices sake I'm just going to do if error then you just want to throw that error so now that we have this use comments hook built we can go back to comments list and we can just implement the hook here so comments comments is loading equals use comments and then we have to somehow passing the ID here which we can just get the post and just destructure the ID from the props so ID equals to post and then we have to give the post to the comments list have to import use comments and then we can just do a simple dot map here so we can just comments dot map and then here we're going to take each individual I actually don't need the curly braces just comments dot map and we're going to take each individual comment and we will just return a string and then we can just do the text or maybe I'll just comment dot text and then we have to just change this to ID and let's see how it looks like in our front end I'm going to refresh this and it's going to do undefined so if it's loading return loading and let's refresh that so that works and then I just want to make this on a new line so I'll just do a line break here let's make a react fragment and here we can just wrap that and just do like a simple br so this is a new comment another comment if I add another comment yet another comment so you can see that that unpredictable behavior shows up again where I add a comment it's going to randomly go into random positions in the comments list and that's why we want to order comments here by the date because when we're making comments remember that we're adding the date the timestamp for when this comment was created and that is from our comments hook so you can see that we inject the date property here inside of our use add comment hook and it's going to just inject the date so we can just make use of that date property so I'm going to go to the comments hook here and then in the query instead of just fetching all of the comments where this clause is met we can also just order by so order by and that's going to come from firestore so order by here from firebase slash firestore we can import that and then let's order by date and then recall that in the posts here we have we have ordered by and I was ordering the date by sending order meaning the newest post will be on the top of the list but now for comments I'm going to build this kind of like the youtube comment section so basically I'm going to have the newest comment at the bottom and then the oldest comment on the top so instead of just doing date descending I'm going to do date ascending here and we can save that and then now if I go back here and I refresh and this is exactly why we had we have to always catch errors like I just didn't catch this error here in our comment use comments hook and I just refresh the page it's going to crash in our face and we don't know what the error is because it just says cannot read properties of undefined reading map and you it's really hard to debug because you don't know where the problem is but if I had this if error throw error it's going to throw the error and we can catch that and gives us a more comprehensive description of the error and tells you that this query requires an index I can create the index here so I'm just going to copy that and the reason why this error is happening is because I'm using this order by and the where the where clause and the order by clause which requires an index and I just have to make the index by going to the link and we're going to see how it looks like in just a second so I have to make a composite index that has a post idea of ascending and date of ascending I can just create index and sometimes indexes can help you save money and in terms of pricing when you're deploying your app and you're trying to scale your app in Firebase so I'm just going to let this run for a second and then I'll resume the recording man this is done building and the building is now done and the index is enabled so that means that if I come to my app and refresh this theoretically it should work and it should always add the newest comment to the bottom so newest comment goes to the bottom newest 2, newest 3 and that works perfectly because our order by clause is now fully functioning so now instead of just putting all the comments as text here I'm going to actually make that comment component here so in our comments I'm going to have to increase the size of this so I can read the name of the files so comment.js now this is a singular comment and we can make a React functional component for that and let's export default function comment and we can return something here so what we're going to return is a bunch of containers with boxes and flexes from Chakra UI which I'm just going to paste in here don't get intimidated by seeing the amount of code here it's just mostly just styling code you can just take this from the link in the description I have my GitHub repository you can just copy and paste it just import all of these things so basically it's just padding and margins and max widths and text align and border color and yeah you don't have to worry too much about the styles you can always just re-style this yourself so I'm going to save this and in the comment here we're going to take in a comment data object and then we can destructure some information from this comment object so I'm first going to go to our comments list let's just close this I'm going to close that and I'm just going to go to my comments list here instead of just returning this comments.map I'm going to actually return like a box from Chakra UI and inside of the box we're going to have comments.map and for each comment we're going to return a component in here and that component is basically the comment component we just made so comment.slash comment and then so let's make this comment component so comment and self-closing and we're going to pass in the comment in here like that and still giving me this error because I think I formatted this wrong somehow I might just redo the entire map so oh no actually I figured out what went wrong so we need to wrap this in curly braces for it to read it as JavaScript then if I do this we're going to get an error here from React.js because React doesn't like it we don't give each child a unique keyprop when we're doing map so we have to pass the key we can just do comment.id because we know that each comment is going to have an id and there we go if I count the number of comments here 1, 2, 3, 4, 5, 6, 7 and I go back in here and just count this as well 1, 2, 3, 4, 5, 6, 7 so we do have 7 and 7 although the text doesn't match up but that's because we have to come and fix all of the hard coding here so first of all I'm going to change this text from this is a comment because that's just going to make it hard coded I'm just going to get the text and destructure the text from the comment object that we're being passed from the props so I'm going to refresh that and you can see that the text updates so this is a new comment, another comment and I'm just going to reset this let's reset this collection delete the entire comment section it's going to be gone if I refresh this, it should be gone and then we can just comment 1 there's our comment and here instead of just hard coding the test user what I want to do here is I want to do the username and I can't get the username from the comment object because the comment object doesn't have a field that stores the username of the user that made this comment instead it stores the user's ID and I'm just starting to realize we may have just forgotten completely forgotten about that part because I don't see a user ID here so if I go back to my comments hook the use add comment hook and so basically in our new comment section we're passing the user ID to this use add comment this use add comment hooks configuration object but we forgot to receive that here we're just taking the post ID we completely forgot about the UID so let's just get that and just inject it here as well so now if I go and delete this comment and I go make a new comment here new comment we should be able to go to the back end and just see our user ID show up there we go and finally now that we have our user ID we can go ahead and get the UID from the comment object here and then we're going to convert this user ID to our useful the useful information that we want which is the user the user object to pass into our avatar because we're going to put our avatar here and then remember that the avatar object is expecting a user data object because just let's just go back to our avatar it's going to be a profile avatar and it expects that user object so let's go ahead and give it that by using our use user hook so the use user hook exists in this file so the use user hook can help us convert the user ID into the user object by fetching the data from the fire store database so const user is user just is loading use user UID and then we're going to import this alright and then if is loading return loading alright and then we're going to pass in the user object here and here in this box we can't forget to include the user user name so there we go our comment is now functioning and what we can do is just use our avatars size override to override the size let's make it something like SM there we go but if I I'll just want to try MD and see if that works alright maybe SM is better we can get the date object from the comment just like how we did it for much like how we did it for the post here we got the header here we used format distance to now and we imported that from date FNS and then we just format distance distance to now so let's just copy that and just paste it here and then we have to import format distance to now and date has to be destructured from the comment object can save that and you can see 3 minutes ago and I'm going to make a new comment that's going to be less than a minute ago and then there we go less than a minute ago that works now before we move on to the next section I just want to have a little bit of fun with our the app that we currently have I'm going to try to go to the dashboard and just use a different window it's going to kick us back to login screen because we're not logged in in this window and we can just make a new user new user too and then new user at gmail.com test password and we are going to see all of the posts that we made I'm going to go to this newest post and here we can type this is a comment from a different user and if I go back to my test users account and you can see that a new user too appears but one thing I'm going to change about this is I don't want that to be just a username I want it to be the button that we can click on and link us to the user profile which means I'm just going to come here and just do user name button and username button expects a user a user object let's give the user equals to user and then now we can click on the users and it will link us to the profile which doesn't exist yet and we're going to make that in just a second another thing you're going to notice is that I'm just going to close this here another thing you're going to notice is that all of the comments have all five as the indicator of how many comments there are and we have to just fix that so let's go to the comment here no actually it's going to be in the post in the header so let me find for that action sorry my bad so actions and five so that's where we hot coded in the number of comments so in here I'm going to go ahead and use the comments the use comments hook that we just made here to count the number of comments let's go and do constant comments is loading will be comments loading equals to use comments and then here is we're going to get the ID from the post which is just destructuring from the post object and that's all good so we can import that and it's loading button here we don't have to care about that alright so let's just return the comments dot length so this works and we can just make sure it works here if we go back to our dashboard you can see that there's four comments here there's zero zero here if I added one comment here it becomes a one and that works but if you are scaling this project up and you want to do this more efficiently and reduce the cost that it will charge for your base project you can create an index just like how we created that index for our order by for ordering the date you can make another index and use the dot count method on firebase just to count the length of an array inside of firestore without actually having to fetch all of the comments because we don't have to know the contents or the text of the comments we don't need to know the users who made these comments we just need to know the length so there's a better way to do that I'm just going to leave it like that for now and here let's see I'm going to change the icon here because we don't want to just hard-code this icon we want to see if the comments.length is zero then we are going to do this regular one if not we're going to make a filled comment icon show up to indicate that there's their comments so let's see if that works so there we have a filled comment and for the zero comments it's just a blank comment icon and that's great I want to give the users the ability to delete their own comments but not other people's comments so I'm longing as test user right now I want to be able to delete my own comments here so let's go to our comment.js and just add that icon button here and then we're going to give this size to be sm we should see our icon button show up and we want the icon button to be all the way to the right so we can just margin left equals to auto and it should push it all the way to the right and then I want I want this to have the trash icon let's just get that from the post here so in our post we have sorry it's in the actions file again so here there FA trash let's just import that import FA trash from react icon slash FA so I believe FA stands for font awesome or something but I'm not sure about that and we can go to our icon button and just give icon equals to FA trash and we should see the trash I want that to be red and I don't want the I don't want a button to have a background like that so first of all color scheme red so that's red and then we shall give the variant equal to ghost and it becomes a ghost and I want this to be rounded so we can do is round and there we go so that's much nicer and the next thing we can do is just check if this is in the right position test alright so that's pretty much just like our example and then what we can do is we can go ahead and add on click so in the on click I'm just going to make it run make it execute a delete comment function and we have to make that function I'm not going to make that function within this comment component instead I'm going to put it in the hook and just call the function from the hook so let's just export a function use delete comment ID that's the ID of the comment and then we're going to return delete comment and is loading so here we can just delete comment and is loading to be delete loading I just want to name this better so I'm going to see if we can change this is loading so is loading will be user loading just copy that so delete loading equals to use use delete comment and then here it expects us to give it an ID the ID of the comment that we want to delete and we're going to just destructure that here from the comment object as well so there's an ID there and let's give this button here and is loading to be delete loading and then here in the comments hook let's go and make this function here so async function delete comment and then we need the is loading state is loading set loading and here the first thing we're going to do of course is set loading to true and then we want to make our document reference to delete that comment so const q equals to document and then it takes in database comments ID and then we can just await um delete delete doc I believe and we can delete q or rather doc ref instead of just a query so is just a doc ref to a single document and then once we're done we can set loading to false I also want to just toast the user to let them know that their comment was deleted successfully um so let me go ahead and look for this snippet so I can paste it use delete comment here and just put the toast here there we go and then we can just const toast equals to use toast and then that should be pretty much it so we can go back to our web app so that's not right because we shouldn't be allowed to delete the other user's comments so I'm just going to make a check here and we have to check for if the current user is the owner of this comment so how do we get the current user we have to go ahead and use the use auth hook so const user let's call this auth user is loading to be auth loading and then equals to use auth we have to import that and then we let's just say if user loading or auth loading or we don't even have to do that we can just do it down here if not auth loading and the auth user dot id is uid because the uid is the id of the user who owns this comment if that's the case then we are going to just return this icon button and there we go we see that the icon button for new user 2 disappears because that's not our comment so let's try to see if this works I can delete that and says comment delete it though we would probably want to do a confirmation alert to see if the user actually meant to click on that button you could always use a check or ui modal you can just make a modal like that and say oh sure you want to delete that and say yes and actually delete but I'm just for the sake of saving time for this video I don't want to make this so long we can just do a simple javascript prompt like that and that saves us so much time so I'm going to go to the comments hook here and delete comment so here just right under delete comment and set loading to true what I'm going to do is just do const res equals to window.confirm and we can confirm are you sure you want to delete this comment and then we'll just say if res is true if the response is true then we do all of this otherwise we just don't do anything there we go and then we can just click on that it says are you sure you want to delete this comment and click cancel and it doesn't do anything fix that loading thing though so I guess we can just set loading to true inside of this if statement and just refresh this so if I click on that I say cancel it doesn't go and delete the comment but if I say okay then they'll delete the comment now before we move on to the very very last section of this I don't know how many hour long tutorial I just want to take a moment to thank all of you who are still with me here and stuck to the very end and it's been a long journey making all of these features and I just I'm very excited to finish off this last part and that is the user profile page so if I go and edit my profile we can see that it says user profile for a specific ID and that's just because we have that hardcoded here in our user profile route in the route.js file let's go and make our index.js file in our profile component and then we can make a react functional component and then I'm just going to replace this with their profile component that we just made and import that so profile and let's make this profile and then we should see index and that's our index.js file here so I'm going to take a bunch of code and just put it in here as well. It's just a bunch of containers you can see stacks and flexes and horizontal stacks and just text and I'm just going to save this and show you what this looks like. It's just a basic skeleton of the UI. It doesn't have any functionality yet so we're going to have to insert our avatar there. We're going to have to count the number of posts this user made and then the likes here we can obviously go and make functionality to count the number of likes this user has received on their posts but I'm just going to leave this as a big to-do here and leave that up to you I'm not going to do that in this tutorial just because it will drag on forever and then we're going to just show them when this user joined and also we're going to include a list of all of the posts that this specific user made and we're going to make a button here that allows the user to change their avatar and upload their own file for their avatars. So here the very first thing I'm going to do is I'm going to grab this ID from the back part of the URL. This is going to be the user's ID and we can do that using the use params hook from react router so we can destructure ID from the use params hook and we can log that to the console and we have to import use params so I can refresh that and you can see that the ID should show up here in the console and that's because in our routes file we have our profiled route to accept a dynamic ID at the end of the path so now that we have the ID we can first do our posts list so in posts and posts list you can see that the posts list accepts a posts prop and that's basically just an array of posts that we're going to render in our posts list so posts list and then we can just add the posts here and do already have an existing hook in our posts file here called look for it let's look for it use posts but this is not what we want to be using as it is right now because it's going to return to us every single post that's in the firestore database and that's not what we want because we want to filter this posts down to only show us the posts of this specific user so what we can do here is we can just add an optional argument an optional perimeter here called UID and this is going to be the this is going to default to null and so if we don't provide any arguments to the use posts hook it's just going to assume that you want every single post and if you do provide a user ID and you're just going to give you the posts that this particular user made so here you can just modify the query variable and make that dependent on the UID so if UID exists and we're going to use a ternary operator so if the UID exists we're going to do a query here if not we're just going to fall back to this default query so I'm just going to copy this entire query and just put it here and the only difference it's going to make we're still going to order it by date descending and we're still going to get that from the posts collection but the only difference is that I'm going to add a rare clause to specify that I want all the documents inside of this posts collection where the UID field is equal to the UID that's passed to us here so if I go back to my Firestore database and I just show you our data here in the posts you can see that the post documents are going to have this field this UID field or the user who posted this specific post so here what we can do is we can do cons posts and then is loading equals to use posts and then we can pass in so we're not going to pass in UID like that because we don't have a UID variable in this function but what we do have is the ID and the ID is basically the user ID the UID that we're getting from the top part of the path here so I'm just doing I'm just going to do ID let's import that and then let's just do let me think about this I'll just do it like that alright if it's loading let's just call this post loading and if post loading is true then we're going to just return text loading posts are loading if not we're just going to if the loading is false then we can just return our posts to this see it throws us an error again that's because we're using the order by and where clauses together and that's why we have to make an index so let's just do this thing again and let's just go and create this composite index and just let it build in the background for a bit before I continue to recording I've been sitting here waiting for a while and it seems to be done and enabled so I should be able to just go back to my app just how to refresh this and there we go that's our post list right there so if I go to the home page you can see that there's going to be oh so apparently that's not I'm going to just make a post that is not posted by this test user and we won't see that show up in the test user's profile so let's just login with a different user I don't remember the credentials of any of the users I've made so I'm just going to make a user every single time let's call this user2 at jmail.com password 123 what we already have user2 I guess we can just login password 123 there we go so if I go to my user to one's profile we should see no posts because we haven't made a single post yet if I did new user2's post and just posted that and we go to our profile we should now see that appear and if we go to our test user's profile we will not see the new user's post despite it being present here in the main dashboard so let us go back to our profile and what I'm going to do here is now insert the avatar so the avatar doesn't exist yet but if we just import and reuse our avatar component I'll have to import that so import avatar and avatar expects a user object right here takes in a user and a size so I'm going to just make this size 2xl and I'm going to give it the user and we don't currently have that user object yet so we can always just get the user object with our user hook very handy so const user is loading to be user loading equals to use user and then the id which is going to be the user's id which we're just getting from the parameters of react router so let's import that and then we can just pass in the user like that and if the user is loading just let's just do like um if user loading then just return loading and there we have our avatar and next up I'm going to make that user name show up right beside the avatar there by replacing this add user name here text we can make our chakra UI text component and just do username so user dot username and we can see how it looks like so it looks pretty small you can change the font size to 2xl and there we go and finally let's work on the posts and the joined so I am going to go over here and replace the 10 with um posts dot length and let's go ahead and refresh this so we do see that we have six posts here and if I try to delete a post which I obviously can't because we completely forgot to implement the delete post function here we just made the hook and just left it and forgot about it so uh we'll do that before we end the tutorial and then so we have six posts here if I went and deleted another post I just want to make sure this post belongs to this user here um okay let's just look for this new post and just delete that should be somewhere here new post all right there it is let's go ahead and delete this document so if I do that you can see that the posts the post count drops to 5 and then now let's deal with this joined field so instead of just hard coding when the user joined we can just do user dot date and that's basically the date that we're getting from this user object and then it's going to give us that time step which is not very useful to know because humans can't read that very well so let's just conveniently we have our date FNS library that we've installed which provides us with a format function from date FNS so we can just use the format function here so we can format the user dot date and give it a format string to tell it how we want the date to look like so I'm just going to do month and then year and so we indeed see our month and year show up so that's great and we can now finalize our user profiles by adding that edit avatar button on the top right corner if this is our profile if we're looking at someone else's profile for example if we look at the new user's profile it shouldn't show us that button much like this change avatar thing we're going to click on that and should give us a modal to let us select image files to change we're going to go back to the code for the profile component here and let's go and add that button right here I'm just going to paste the button in and just import this let's do that save that I'm going to show you how that looks like so it's going to appear up here in the top right corner it's basically positioned absolutely it's going to be positioned in the top right corner and we have the teal color theme but this button isn't going to do anything we can click on that but it doesn't have an on click prop so let's go ahead and make that on click prop so on click so what this button should do when you click it it should open up the modal much like this button over here this open modal button it should open up a modal that we can use to set our profile picture so basically we can just use this disclosure hook that Chakra UI provides us and just do const is open on close and on open as well so on open equals to use this disclosure and then here we just do on open so basically you can just use this on open function just call that to open the modal and then this open variable is going to update and that's what we're going to be passing into our modal component so let's make that modal component with an edit profile.js and we are going to receive is open here just pass is open to our modal so edit profile is open equals to is open and that's basically the variable that we're getting from this use this disclosure hook and then I'm going to go to my edit profile here and paste in a bunch of code and just ignoring the import statements if I can find a way to collapse that so no apparently not so basically in this modal or all I'm doing is I'm going to check if the modal is open not and just get that from the prop I also have to get on close from the props which means I have to pass that in here too so in the edit profile we need on close so why this edit profile modal requires this on close function is because the modal is going to have a close button and that's the close button and to be able to close the modal it requires access to that on close function which this close button will trigger and so we have our header here that says edit profile and then we have our avatar which we have to insert and then we have our label and we have our input which does nothing at the moment because we have an anti arrow function on change prop so I'm just going to change this to teal and let's go and try clicking on a button so we click on that we get this edit profile and we don't have an avatar right now we don't have that preview avatar so we're going to have to fix that let's take that and replace it with our avatar component and the avatar component takes in the user object so we have to pass in the user which we're going to have to write the hook for so user is loading equals to use user actually it should be use auth and let's import use auth if we can and so let's just return false to return loading if it's loading and so I should be able to go back to our application change avatar and we do see the avatar there and let's let's try to select a file here it's not going to do anything right now but I'm just going to show you what it does so we have all of these pictures you can just click on that it does nothing just tells just sets the input but isn't controlled by react state so react doesn't know that you've selected a file here so in order to make this input a controlled input we can just change the on change function to be handle change by the way notice that I have this accept equals image slash asterisk here so that we're only accepting image files in this input and users can't just upload text files they can always just change their html and their browser and just override this and just hack through it so at the end of the day you might want to implement a secure checking system to check and like a serverless function to check the file types as well as compress and optimize images before like resize the images before you upload them to your storage but I'm not going to do that in this video because I'm just going to make this as basic as possible and I'm just going to make a function here called handle change it's going to take in an event and we can just log to the console the event of target of files so if I go and choose a file here that's just you can see that it outputs a file list type object it's going to have a file and all of the properties of the file alright so I'm not going to log this to the console instead I'm going to set this file to a state variable and I can call like a set function called set file or something and like do e.target.files zero now this function doesn't exist yet but we're going to know that we have to make we're going to have to make a hook to update the user's avatar so let's just go to our user's hook here and just export a function called use update avatar and here we can return a function to set file and then here we are just going to do const set file equals to use update avatar and here the set file function is going to set the file in a state variable within this hook so we can just do const file set file equals to use state and we have to import use state so now we have the file inside of this use update avatar hook and what we can do is we can go ahead and make another function here called update avatar so this is a function that will actually update avatar and make the API call and we can get the update avatar function here and just do on submit and just like when this button is clicked then we're going to run this update avatar function so let's go to the button here and do on click equals to update avatar so a quick recap is whenever you change the file in this input it's just going to set the file into this file state variable and once you're actually done and you've changed and you're ready to submit and you click this button it's going to call this update avatar function which we have to write and it's going to take this file and push that to the firebase storage and then we're going to have a loading state from this because uploading file takes some time it's an API call and that's why we have a loading state I realize that it clashes with an existing is loading so let's just rename this auth loading better and then here we'll just do file loading and just get the loading state from the use update avatar and we have to make that loading state loading state here as well so it's loading then we have to const is loading set is loading and then now finally let's make this asynchronous function called update avatar I'm actually just going to change this to set loading instead of set is loading so that's shorter and the next thing to do is of course as always we're going to start the API call by setting loading to true and then false afterwards and then we're going to call a function called upload bytes and that's going to be imported from Firebase storage and since it's an API call we can await that so upload bytes takes in two arguments the first one is going to be the file reference which is basically the position the location in which we want to upload the file and contains information about the file's name and the file's folder and stuff like that and then the next is the actual file to upload and this data object who can just call it file because we have a state variable to store the file I'm just going to put it there and then all we have to do now is make that file reference and basically we're going to use this reference function that we can import from a Firebase storage so let's go ahead and make the reference the first argument is going to be the storage handle which we're going to import from our library slash Firebase and note that we're no longer just doing db here we're doing storage this time because we aren't we're not dealing with cloudfire store anymore this time we actually want to use the Firebase storage API SDK for Firebase storage and not just cloudfire store anymore is because we're uploading files and we're not storing data so what I'm going to do is I'm going to name this file avatars slash whatever so this is going to be the file name and this is going to be the folder if I just set the file name to the user's ID and that way we'll have a unique file name for every file in the cloud storage if you try to upload a file to a reference to a file name that already exists firebase is going to re it's going to overwrite that file so you can't just store the file name here as it is because if two users upload dog.png they're both the dog.png is going to overwrite the file name inside of storage so let's just use the user ID as the file name I'm going to have to import I'm going to have to get the user ID from the arguments of the use update avatar hook so we have to come here and pass in the user dot ID there and that way we can just set the user ID as the file name and once the uploading is done I'm just going to toast the user just to let them know that it succeeded so we have to do const toast equals to toast I'm just going to copy the toast and just put it here profile updated and set the link to false so let's go ahead and refresh this now before we try to actually upload our file I just want to remind you guys that if this is the first time you're using the storage SDK within your firebase console you want to go to build and go to storage and there's going to be a get start button there that you have to click I already did that off screen which means if you don't do that and you don't initialize the storage for the first time you're using it you're going to run into an error and it's going to crash in the JavaScript console so I'm going to select a file let's do any file and just click save it's going to tell us profile updated and if we go to our storage console just refresh that we should be able to see that avatar's folder because we were trying to upload the file to a folder that doesn't exist firebasers automatically create that folder for us just click on that and there we see our image that's the image that we uploaded and the file name is going to be the equal to the user's ID and another thing I want to do is I want to come here and try to upload an empty file so if I don't select any file it's going to say no file chosen and we can hit save and still going to work it's going to let us through and it's going to say profile updated and I just want to see what happens here in the database console so we're going to get a file that's corrupted that isn't actually a file it's going to be like a null file and then just don't want to allow the user to upload files if the file is empty so let's go ahead and make a check for that so when you click on update avatar before I even do anything I'm going to check if the file is null so if not file we are going to toast the user and tell them to select a file so I have to copy the toast and put that here and then to cut off all of these code all of the other code from running I'm going to return make an early return statement which then stops this function and then this code here won't run so refresh this change the avatar if I just select an empty file and just hit save it's going to tell us no file selected and it's going to force us to select the file so we can select just any file just save that and it's going to say profile updated and we go to our console and refresh that we should be able to see the updated picture we have yes we have uploaded the file to the firebase storage bucket but we're not actually changing the so if I go to the firestore database we're not actually updating the user profile in here to reflect the change into the avatar field it's still an empty string so let's go ahead and do that within our hook so before we set before we toast profile updated and set the loading to false I'm first going to have to update doc and I'm going to update doc and this is we're going to have to make a doc reference to the user the user profile of that specific user we're trying to update and we're going to do a wait and let's make that document reference so it's going to be a doc database handle users and then the UID and then we're going to update the document and we are just going to give a an object that has avatar equal to the avatars URL so we can't just do avatar avatar avatars slash ID whatever because this isn't a valid link if you try to put this link into the source of an image element in html it's going to give it's not going to give you the image that you want so we have to somehow get that download URL from Firebase now if you just went to the console and just went to the storage and just like click on the file that you want to download and just look at the file location you can just like just click on that and then this gives you the URL is firebase storage.googleapis.com slash whatever whatever that whole string there so how do we get this string from within our app so there's this there's this this function from firebase called get download URL it's going to be an API call which means we have to await that and inside of this argument we're going to pass in the file reference and then we can set this to file URL like download URL or just avatar URL I guess and then we can just pass that here avatar URL and then now if I try to change the avatar and I just like set the star here and hit save it's going to update our avatar and you can just hard refresh the page and you should see that the avatar is updated now I don't want to have to refresh the page to see it the manually refresh the page so if you wanted to refresh the page using react router you can use the use navigate hook so use navigate and then we just go to the bottom here and we just use navigate just navigate navigate 0 and 0 basically just refreshes the page so I can change the avatar again just change it to something else just hit save it's going to save and refresh the page and you can see everything is updated and this feature I'm going to add to this change avatar model is that I want to be able to preview the file that I'm uploading so right now if I choose I chose a different file it's not going to show up here in the avatar and that's because in our avatar component we have hard coded the source to come from the user's avatar property so what I can do here is when we have set the file to this react file variable I can just give back a file URL and it's going to be URL dot create object URL and that's basically built in function inside of javascript that will allow us to create a URL that we can put into this source prop as we are previewing the avatar picture and we're going to pass in the file binary in there let's go to edit profile here and just take the file URL from the hook and let's pass it into our avatar here so we can do like source equals whatever there but it's not going to work because this is our custom avatar component it's not listening for a different prop so we can manually set a default avatar to be default avatar prop to equal null and say that if the avatar is not null then we render then we render this avatar override source otherwise we'll just render the default behavior which is just the user avatar so if we have an override we'll just use the override or you can just name this override avatar just put that there then we can just override the avatar with our source here file URL and if I come back to this web page you can see that create object URL on URL is it failed basically that's because in here we started off by setting null to our file object and url.create object URL can't create a URL for null type files so we have to just check if file is null then we'll just return null otherwise we're going to process it using this create object URL method so we can refresh that it's not going to give us the error anymore we can change the avatar and just select this and you can see that updates and it shows us a small preview before we even upload this to the firestore database so we can hit save sorry I mean I meant cloud storage so storage and firestore are totally different things storage is used to store files and firestore is more like a database so we can refresh this and just see the changes so we're going to see our file uploaded here in this avatar and pretty much we're done with our avatar changing feature here if I go to another user if I go to user 2-1-2-1 and click on that you want to change avatar button here because we shouldn't be allowed to change someone else's avatar so let me see what went wrong here it's in index and in this button so this change avatar button should only appear if we are looking at the profile of our current user so let's get our current user here using user let's rename that to all of the user and this loading would be auth loading so I'm going to use auth and then if user loading alright so it doesn't matter let's just wrap this in curly braces and say if not auth loading so if auth is loading then we just won't render anything here and if auth is not loading we're going to check if the user the auth user dot id is equal to the user dot id that user id is coming from that variable there and if that's true then you just render the button so the change avatar button should disappear but if we added our own profile we should see that button appear finally let's work on this all users page so right now this page just returns an empty user string which is right here this users path if we look at example this is what we want to get we want to get we want to map through each of the user and just make a grid and show all of the users so let's go ahead and make a component here called users then we have index.js here and I'm just going to copy and paste some code here so basically this is a very simple component it's just a list of all of the users we're going to have to make a hook called users it's going to return to us all of the users that exists in this user profile this user profile table here so it's going to just simply read every single one of the documents in this users collection and just return that to us and then it's going to map through each of the individual user and return a user card so one box is one user card and we have to map through the entire users array to create all of the user boxes so let's go ahead and make that use users hook but before I do that I want to go at the route and replace this users string with the users component which we have to import so import users from components slash users and then let's go to our hooks and the users hook here I'm going to export function use users plural we don't need an ID here unlike use user because we're getting an entire collection which means we won't be requiring an ID because we're just fetching everyone anyway so I'm going to just const users is loading equals to use collection data and then we're going to have to reference the collection here and give the db and name the collection which is these users and then let's just return users and is loading so hopefully that works and we can just save this and for now I'm just going to comment this out and we're just going to print out the user object and see if that works probably not because we can't resolve dot slash user to comment that out as well so that's not a valid react child it's not a react child because this user is an object and we can that's not a string so we can just use json on stringify and just stringify it and see this list so we're going to list through every single of the users and we're going to get the ID and the username and whatever so that works and you can just make this user card in dot slash user so user.js and I'm just going to take this and put it here so basically what we're going to be doing in this component is we're going to get the user prop which is this user here and then we're going to just I don't know why I put this loading here because we're not looking for this loading prop so I'm just going to get rid of that we're going to have a vertical stack that takes in the user object and then we're going to have the username and then we're going to make the profile the view profile link link it to the protected slash profile slash user ID and then we can change this to teal and then let's save that and there we go that's our functional users page refresh that and we can just go to anyone's profile here and view the profile of the user too and user2 doesn't have any posts yet so we're going to go and all the users and look at user2121 we see that post we can view our own profile and we're going to see all of our posts so we now have the users route completed and the final thing we have to do right now is just go and implement the delete functionality I was talking I don't know how long ago in the video so we can finally get to that and finish up the project so I'm going to head over to hooks and go to the file and use delete post hook I'm just going to revisit this hook and just write the functionality for this delete post function so the first thing I'm going to do here is I'm going to confirm if the user if they actually want to delete this post so I'm going to make a window that confirm are you sure you want to delete this post so if I go and click on that it says are you sure you want to delete this post and if I click cancel it's going to be false if I clicked on ok this res available is going to be true so let's just say if it's true if the user really wanted to delete this post first we set loading to true and then at some point of future we're going to set it to false once we're done with all of the API calls so there's two things we're going to want to do here we're going to delete the post document in the post collection so we're going to delete the post document itself and the next thing we're going to do is delete all of the associated comments for that post so we're going to delete comments here as well so deleting the post document is relatively easy we can just await delete doc and if the import delete doc from firebase-firestore takes in the document reference and the database handle the collection name and the ID which we're getting from the arguments of the parameter of the use delete post hook and that's very easy because now we don't have to post document anymore and we just have to delete all of the comments now this is going to be slightly trickier because it requires us to make an advanced query to search where the post ID equals to this ID variable so we're going to just we have to like get all of the comment documents that are associated to the post first before we can delete them so I'm going to make a query here and I'm going to query for the collection in the comments collection and then I'm going to do where the post ID is equal to the ID because in our comments and in the documents of the comments collection we're going to have this post ID field so we're just saying oh where the post ID field equals to this ID up here and once we have the query we're going to get all the documents and we have to get the documents before we can delete the documents because there's no way to directly delete documents using the rare clause in the firestore so we're going to make a query snapshot equals to await get docs and then the query and the import get docs so this is going to give us a snapshot of all of the documents and to delete we're just going to use for each so we're going to do query snapshot for each and then we're going to a function here to delete each document what I can do is just come up here and make a function async function delete delete comment then let's take in a doc a reference a document reference and then here we're just going to run delete doc document the reference so let's just call this doc ref and then we can just for each document reference for each document we're going to return we're going to run delete comment and doc.reference because when we're doing query.forEach we're looping through each of the individual documents in the query and we're not actually looping through the documents reference so we have to do document.reference and pass that into this delete comments this delete comment function or we could also just we could also just do like make this asynchronous function and make it an arrow function instead and just like delete doc and doc.reference and that way we don't have to create an additional function there just all in one line and so after we're done with all of that I'm going to toast the user and tell them that their post has been deleted using checker UIs checker UIs toast so const toast equals to use toast import use toast from checker UIs just toast that post deleted so if I come here and I just refresh the page it's going to tell us use toast is neither okay we have to import that let's import yeah we have that import and for some reason it's not oh yeah I have to take this and put it up here in outside of this delete post function refresh that and let's delete if we say cancel it's not going to do anything if you say okay it's going to delete the post post deleted and the comment should all be here on so I'm going to delete all of these posts and check our firestore database so we no longer have a single comment and we still have we're still going to have one post and that post is going to belong to that post is going to belong to user 2-1-2-1 but it doesn't have any comments see zero comments so so right now we can see that we've successfully managed to remove all of the comments and all of the posts using our new and updated post use delete post function one last thing that I caught before we finished the entire project is I still notice a delete button here for someone else's post that isn't our test users post and we don't want to display the button if that post is not our post so we only want to be able to delete our own posts and not other people's posts so all you have to do to fix that is going to post in the actions component in the post and this is the delete this is the delete button right so we have to we have to get our current user using use path which we already do and then we can get the uid from the post data object so this uid prop will tell us if what the user id of the owner of this post is and we're going to check if this uid equals to the current user's user dot id so what we can do is just wrap this in curly braces just do like if we have to check if if if user is loading or not so if user is loading if not user loading as well as if the user dot id is equal to the uid in that case we're just going to render this icon button so we see the button disappear it's pretty much the it's pretty much the same logic as the change the change avatar button logic that we had in our profile should be profile and then index of js so it's the same logic as this one here and to render the change button the change avatar button conditionally we're just rendering the delete button conditionally here as well so if we went and made a new post here you can see that our post is going to have a delete icon but not somebody else's post and I'm just going to check if the comments have delete buttons yes they do and if I were to log out and login with user2 at gmail.com just click on this test user's post here and look at the comments and yeah we don't have the delete button someone else's comments only our own comments so everything's working great and I'm just going to do one last check to see if everything works now that we have our fully functioning react app I'm going to show you how to deploy this app to an online hosting provider so I'm going to go to vs code and just stop the server and just do yarn build and that's going to generate a directory here in just a second there we go that's our build directory and once this script is done we'll just upload the files to hosting looks like our app is done building so I'm going to go to my hosting dashboard here and click on manage just right beside your premium web hosting package so I'm going to manage this package and just click on the file manager button there and we're going to wait for that to load and then upload our files to the public HTML directory so there we have our default public HTML directory we don't need that we're going to replace that with our own files so we can delete that and then we can just upload our folder and we're going to look for our build folder which is right here just upload that and click upload and it is really really fast look it's just done uploading and I can just rename this to public HTML underscore HTML go to my domain and slash login we're going to see that it's going to give us the pages lost and that's because of the way React Router is configured to work we have to add like a .htaccess file inside of our public HTML folder here to make React Router work with Apache so I'm going to make a new file here called .htaccess and I'm just going to take the snippet and just paste it in here the snippet is going to be in the video description as well as the GitHub repository just going to hit save and this is where hosting really shines because we don't have to worry about restarting any servers or refreshing the server after making a change and we can just go to the page and just refresh that and you can see that it's instantly live and instantly refresh it we have a fully functional app that we can just sign it into so test user just password and it works and we have our SSL certificate we have this entire thing working in our domain and our console just works I can just go and inspect and just see there are no errors in the console and you can edit the profile you can go click on all the users and just do whatever we want here just log out again and guys that's it from me I really hope you enjoyed it and I will see you in the next one