Transcript for:
Notes on Enterprise Video Calling Integration

have you ever wanted to integrate video calling into your app but thought it was too complex or timec consuming what if I told you that you could build an Enterprise ready video and audio experience and add it to any app in just a few hours not weeks or days hours and not just any experience but one that scales to millions of users and thousands of call participants hi there and welcome back to JavaScript Mastery where I not only show you how to build real world applications but also dive deep into the why behind every line of code today we're taking on the challenge of cloning a video calling app that handles more than 300 million meetings every freaking day before we dive into the code let's take a quick look at how our app works at the start we must create an account we'll do that through Clerk's beautiful sign-in component which is completely free and allows for multiple social sign-ins or just your typical email and password login once we're in we can quickly initiate a new meeting redirecting us to set up our camera and microphone as soon as you join you'll see a familiar video setup where you have full control over every aspect of the meeting experience from recording the meetings to sending Emoji reactions sharing your screen adjusting audio and video devices changing the grid layout viewing the list of all the participants and individually managing each participant we have the option to exit the meeting or end it for everyone one returning to the homepage if you'd like to schedule a future meeting simply put the details date time and voila you'll find it ready on the upcoming meetings page where you can either copy the meeting link to share it with others or start it early you can also view all the meetings you've held in the past with a previous meetings list including their recordings on this page you'll also have your own personal room with some details and a unique meeting link which you can share with anyone for an instant meeting let's not Overlook that you can also join meetings created by others by simply providing a link here and just like that you're ready for the meeting rest assured all of this is incredibly secure real time and responsive that was a lot wasn't it we'll use the best Technologies out there including typescript and next js14 as well as tailn CSS and chaten for Sleek modern styling and there will be no code to blindly copy and paste instead I'll ensure you understand every aspect of the development process and the reasons why we do things a certain way plus we'll follow a figma design and write every single line of CSS together this course isn't just about building an app it's about understanding how to apply these Technologies in the real world so you can beat Devon and become an expert developer this app is made possible thanks to wait hold on a second another thirdparty tool to handle this why not build it ourselves from scratch as developers we know the importance of not Reinventing the wheel third party tools enable us to focus on developing unique apps rather than getting slowed down by the specifics like in this case complexities of video streaming this approach not only saves time but significantly improves our skills making us better developers but do the big companies you'd want to get hired in care and the answer is yes the software you learn today is being used by Adobe SoundCloud Gumtree crunch base and many more the name stream and it allows you to implement skill able Enterprise ready video audio and conferencing features quickly and easily absolutely free of charge without even requiring a credit card they have pricing plans but these are mostly for Enterprises and the cost for hosting and managing the entire infrastructure on our own is lower than something like AWS learn to embrace these tools to make you a more powerful developer finally let's dive in and create something incredible and make sure to stick around to see how I approach what one of the trickiest features I've quoted in a video so far to start developing our Zoom clone application we'll begin from bare Beginnings by creating a new empty folder on our desktop let's call it JSM uncore Zoom unor clone once you have it drag and drop that empty folder onto your empty Visual Studio code window as you can notice we're starting slow from beer Beginnings but we'll quickly ramp up the complexity and the importance of concept you'll be learning about and the difficulty of Concepts I'll teach you and as I said at the start I'll teach you every single little part of this application with no copy pasted code and completely free tools starting off with the biggest of them all next GS the react framework for the web of course we'll be using next for its built-in optimizations Dynamic HTML streaming and server components alongside next GS will also be using Tailwind CSS a utility first CSS framework packed with utility classes that make our styling easier and building just on top of it we'll use shat CN as our component library of choice that allow us to easily pick and choose of some of the pre-built components but then apply our own complete styling to them using Tailwind so let's initialize our application by going to shat CN UI and then click get started here we can navigate over to installation and choose nextjs we can copy the first command open up our terminal and paste it there's just one tiny change we'll make to this command and that is remove this my app and replace it with Slash which will initialize the project right within our existing directory we just created let's press y to install the create next app CLI and let's answer a couple of questions would you like to use the source directory no need for most of these questions you'll just press enter to speed run your way through it so yes yes yes just enter enter enter and the dependencies are getting installed while they're getting installed let's also run the Shad CN UI init command to set up our project so we can copy it and back within our editor simply paste it and press enter once again here we'll just press enter enter enter and we're going to install everything now what just happened well if you look at our file explorer you can see that we got a few new files such as the global. CSS where we're importing Tailwind we also got a lip folder with the utils where Tailwind CSS just added some stuff for us so we can easily consume all of its class names and finally it also added the Tailwind CSS config which we can later on modify to make make our styling life a bit easier now going back to the docs the next step is to install the font and some further customizations which you can do if you want to in this case we'll be using our own font later on and there we go you have your own new app structure and that's it so we can test out if shaten works by adding a new shaten component to our project so let's copy this command from Step six and paste it right here MPX shaten UI latest add button what I love about shaten is that it allows you to add specific components to your project and it adds them directly within components UI right here so you can even modify the source code if you want to not that we need to but you can if you want to so you can choose exactly how many components you're adding and you can only add the ones that you actually use so going back we can copy this homepage to see if the button appears we can navigate over to page app and by the way if you're wondering how I just did this without opening the file explorer and then clicking on page I used a command or control P shortcut which allows you to search files by name and then quickly traversing over to them this is just one of the many pro tips I'll scatter throughout this video so now we can remove this entire homepage and paste what we copied over from chaten finally let's run mpm runev to run our application on Local Host 3000 command or control click it to open it up and here we are I'm going to zoom it in so you can see it a bit better but if you have a button that looks like this that says click me it means that you have successfully installed Tailwind CSS and shaten and that we're good to start creating the file and folder structure of our application so going back to our codebase we can open it up and see what we have here we have have the app folder which is the most important folder right here especially considering that nextjs uses a system called file based routing so depending on which folders you add here and which Pages within those folders it will expose different endpoints so that you can then move across different pages within your application first things first we're going to remove this favicon because soon enough we're going to use our custom one and we'll start tidying up some things first of all I'm going to create two new folders and both of them will be wrapped within parentheses o and we're going to also include root like this now why did they use parentheses right here well I just used a nextjs concept known as route groups in the app directory nested folders are normally mapped to URL paths however you can Mark a folder as a route group to prevent the folder from being included in the routes URL path what this allows you to do is organize your route segments and project files into logical groups without affecting the URL path structure so it's useful for organization and for nested layouts both of which I'll teach you in this application and you can see right here that the convention is to Simply wrap a folder's name into parenthesis so in this example they tell you that you can organize ize your routes by marketing shop and so on and then each one of these route groups can have additional pages and it can also have additional layouts which will be very important later on within our application so going back within our app with an oth we can now safely put the sign in folder as well as a sign up folder now we have logically divided sign in and sign up from the rest of our Pages for now now let's close that and let's focus on the root which is basically our entire application within the root we can create a special file called layout. DSX which will allow us to do some specific things to all of the pages within this route group what do I mean by that well let me show you if I run rafc right here that will allow me to quickly spin up a react functional component and we can call it root layout now if this rafc didn't work for you that must mean that you don't have the es7 plus Redux react native react Snippets installed so install it try running rafc and it should work what we can do here is wrap everything as a main component and then here we can return the children so we can say children which means that this component will wrap all of the other Pages within it and Children Of course comes from react props so we can say children and considering that we're using typescript we can Define the children type as react node coming from react and now to explain what I mean by creating different layouts if I now create a Navar here and I also create something like a footer here it should be present within all of the pages within the root folder in our case within root we'll also create a new route group called home where we'll put all of the home routes but for now let's just create a new page. TSX within it within this page within the home we can run RFC and create a home component and next to the home we can also create a new page called meeting within this meeting we'll create another folder starting with square brackets this time it's going to be ID like so and then only within this ID we'll create a new page. TSX where we can once again run RFC and call this page meeting so let's go over this in a bit more detail and explain what's Happening Here we already know about route groups right but in this case we have also used another concept called Dynamic routes so back into the docs here they say that when you don't know the exact segment names ahead of time and want to create routes from Dynamic data such as different meeting rooms with different IDs you can use Dynamic segments that are filled at request time or pre-rendered at build time and the convention here is to Simply wrap it in square brackets for example ID or slug which is exactly what we're doing here so here's an example for a Blog you could use a slug or an ID and then say my post and then render the blog post URL but an even more important takeaway here is that you can extract that Dynamic parameter through the params object so let's copy what they have here and I hope you're following these documentation pages with me while you're following this video as well as that's very very important important I don't want you to just follow along for the video and replicate what I do I want you to go ahead and explore even further within the docs until you fully understand each concept that I'm covering so how do you get to specific Pages well you mostly just Google nextjs and then the title of the Doc Page in this case Dynamic routes so let's copy this part and back in our meeting we can now paste this here and and instead of pams slug this name right here will be equal to whatever you use as the file name or the folder name in this case we use the ID so I'm going to say ID and now what we can say meeting room and we can say number and then you can render the ID that's going to beams. ID there we go finally let's go back to our app here on our Local Host 3 000 we still have just the button that says click me but if we now go to a meeting room specifically a room with a particular number or an ID coming after it like 1 two 3 and press enter you can see the knv bar and the footer because that's coming from the layout that's wrapping our page and then you can see the meeting room number 1 two3 that means that we're successfully fetching the perams and with that I hope all of this routing layouts and dynamic route groups make a bit more sense if they don't make sense right now they're going to make sense as we keep adding more routes once you learn it once it'll be easy to replicate it for all of the other pages and talking about fully understanding nextjs I can't not mention the ultimate nextjs course that we worked over a year on right now it's the best nextjs course out there so if you want to learn some of the next xgs Concepts deeper understand how all of the biggest companies use nextjs and not just use it as good old react to get stats like this rather learn how to properly use next4 and get perfect stats with deep Dives to understand how NEX GS truly work behind the scenes to build and deploying a most complex app out there and then even to active lessons that allow you to replicate what's being thought in the course with examples resources hints and even more I just had to quickly mention it as if you're watching this video you most likely will be interested in the course content as well but with that said let's continue with developing our Zoom clone now that we have our meeting room and our homepage we're going to create a space for our navbar and sidebar and they're not going to be right here within this layout as we don't want to show the nav bar and the footer within our meeting page rather we want to show it only within homepages what do I mean by that well check out our final application on the homepage you can see the sidebar and the knv bar but as soon as I jump into the meeting you can see that the sidebar and the Navar are completely gone allowing us to focus on what matters the most which is displaying our own camera as well as the cameras of all of the other users so to aeve achieve that we'll have to create a new layout within our home route Group by calling it a layout. DSX to get started with this one we can basically duplicate what we have in our root layout and paste it here we're going to rename this one to home layout and we're going to give this main a class name equal to relative and now we can show the nav bar right here on top of our children we're going to also create an additional layout including a div that will have a class name equal to flex right within that div with a flex will create a sidebar component below it will create a section that section will have a class name equal to flex Min dh- screen which will give it a full height Flex D1 so it expands nicely Flex Das call so the elements appear one below another padding X of six for horizontal padding padding bottom of six for padding bottom and padding top of 28 to give us some space on top on Max MD devices that means you can see it right here media not all and minwidth of 768 that means whatever class we give after Max MD it will appear only if the device width is greater than 768 pixels which is mostly tablet and laptop devices and here we can give it a padding bottom of 14 a bit smaller and on small devices we can give it a padding X of 14 once again a bit bigger this time than the previous one and this Tailwind intell ense is pretty cool so if you hover over it you get the exact CSS properties we're applying so if that's not working for you head over to extensions and search for Tailwind CSS intellisense it should be the first one right here install it and then you should be able to see exact CSS classes within this section let's also create a div with a class name equal to that's going to be w-o for full width and now we can move over the children within this Viv right here there we go so now we're creating the layout for our homepage if we go back to our app in progress and navigate over to Local Host 3000 of course we don't want to show the button here so we have to head back over to this page and actually delete it because this will not be the primary page of our application so now if you head back you can see navbar sidebar and home before we go ahead and add our navbar and sidebar components let's navigate over to app layout this is the primary layout that we have right here and let's just apply an additional class name to our set of classes attached to the body that's going to be enter class name of course and then we can also append a BG dark off two like this now this BG dark of 2 if you hover over it we don't see anything and if you go back to the app we don't see a dark background so how does this work well let's navigate over to our Tailwind config TS I told you we'll be using this a bit later on and what we can do is we can extend the theme specifically we can extend the Colors by adding a new dark color let's see yeah we don't have it right now so we can go extend colors and then right here we can say dark and then create an object for all of the dark colors we have and then give them some names like one this is our first dark color it will be #1c 1 F2 e and we have created our first dark color now you might wonder where did I get this color code from well for that we're going to use this phenomenal figma design page that our JSM designer has designed specifically for you and for this video so if you haven't already go to the description and get access to this figma page as we'll be using it a lot throughout this video and then head over to the second page which is design as you can see here we have everything from the off page moving over to our homepage so now if you click on the home you can try the dev mode and you can see a specific color right here and if you click on different elements you'll be able to see different colors which you want to use in this this case we're interested just in the background color which I do believe is this one so we can simply copy it going back we can say something like two and then Define the color we just copied in this case it will be this one right here and it's going to look something like this so for now let's say that we just need these two now that we have this dark 2 we can actually use it by saying BG dark 2 and and if you hover over it you can see the exact background color we added although yes it's applying a bit of opacity but that's basically the color that we chose and added it to our Tailwind config so that in the future whenever you want to use this specific hexadecimal Coler you don't have to say 161 925 we can just say BG dark 2 with that in mind if we go back to our app you can see that we have complete darkness don't worry the ab didn't break it's just that the text is also dark so we cannot see it on the dark background but this is much better for our eyes rather than switching from light screen to dark screen every time with that in mind we can now turn the knvb bar and sidebar into complete components so let's navigate over to our components folder and let's create a new file called navbar TSX inside of which we can run our fce and let's also create another one called sidebar. TSX and let's run RFC as well moving back over to the layout we can just turn these into self- calling and self-closing components and of course you can press control space to get this automatic import layout and autoimport it from components Navar and components sidebar that should look something like this now we're not going to touch the knob bar for now as what matters the most is the sidebar so let's navigate over to the sidebar by holding our Command or control key and then pressing with your mouse and why sidebar matters the most is because within it we'll do all of the routing to all the different pages such as upcoming previous recordings and creating a personal room so let's start creating our sidebar by turning it into a section and giving it a class name equal to Sticky which means that it will give it a position of sticky so it stays there left zero so it's positioned to the left so it's coming all the way from the top it will be a flex container it will have an H screen which means full height and w- fit so it fits the content we'll also give it a flex Dash Co so the elements appear one below another as well as the justify between so we have some stuff at the top and then later on we have some stuff at the bottom after justify between we'll apply a BG Dark One background so a bit of a different background than the entire homepage a padding of six to give it some space padding top of 28 text- white so we can actually see the text and now on Max SM devices which means devices with a min width of 640 we're going to hide it so if it's below 640 pixels we're going to completely hide it else on large devices we can expand its W its width to about 264 pixels I found that value to work the best then within this section we're going to have a div that div will have a class name equal to flex Flex D1 flex-core so the elements appear one on top of another and a gap of six to create some spacing between the elements and here we can map over a couple of our items that'll be things like the homepage the upcoming meetings previous meetings and more so let's create an array of links what we could do is create an array here like this and then create an object for each route that will have something like image URL for the icon of that route and that could point to something like icons home.svg we could also do a route that's going to be a forward slash and then a label which is going to be a home and then we could do this couple more times map over it but I think you can already see where this would end up we don't want to clutter our jsx with some data so instead of doing this array here we want to create this array from scratch so let's navigate over to our root and then right here let's create a new folder called constants within constants let's create an index.ts file and here we can create that array const sidebar links is equal to an array where we have an object that has the image URL of something like forward slash ions forward SLS something. SVG it also has the route of forward slash and it also has a label of home okay we can put the label on top maybe so that it makes more sense that we have this we have the route and then we have the icon URL for that thing now we can duplicate this four more times 1 2 3 4 and the second route will point to the upcoming meetings so it's going to be forward SL upcoming and it's going to say icons upcoming SVG the first one is of course home let's use it with a capital H right here then after that we have previous meetings so that's previous forward SL previous and also icons previous SVG then we have recordings route is going to point to recordings and it's going to be icons video. SVG with a capital V more than that soon and also we're going to have the last one which is a personal meeting room so we can call it personal room pointing to forward SLP personal Das room and it's going to be icons add- personal.svg so now we have this array which we can export and we can use it right here by saying sidebar links automatically importing it from constants which you can see it came right here at the top and we can then map over it by saying map where we get each individual link and then for each one of these links we want to open up a function block within this block we want to check if this link is currently active and we can do that by saying const is active is equal to and here we need to tap into the nextjs functionality of figuring out on which path are we currently on and for that we'll say con path name is equal to use path name coming from next navigation and then we can say if path name is triple equal to link. rout or if path name do starts with and here we can again put the link. route so what we're doing here is checking whether the path name is currently active so if we're on the homepage we want to make it blue if we're on upcoming we want to make it upcoming and you get the gist we also need this starts with because sometimes it's not going to be the exact path like something like meeting one to three right in which case we only are checking whether it starts with that link now that we have this information whether it's active or not we can return a link for each one of these and this link will be imported from next link it has to have an href meaning where is it going to and we can conveniently use our link. route right here as we have already stored it within our sidebar links so we know exactly where we want to point to we can also give it a key as we're mapping over it as link. label as they're all unique and we can give it a class name in this case it's going to be an object where we can use the CN function coming from lib utils this CN is shorted for class names and it allows us to add multiple and dynamic class names for example we can add the typical class name of flex gap of four between the elements items Das Center padding of four so we can give each element a bit of space rounded dlg and justify Das start but now we can also add a second parameter which is an object adding additional Dynamic class names such as BG blue one like this and it will only trigger if is active is set to true this is how we're modifying the styles of each one of these routes for now within this link let's simply render the link. label now if we go back to our Local Host you'll see that the Ed path name which is a hook only works in client components add the use client directive at the top of the file to use it so going back right here at the top we can add something known as as a use client directive and if we go back it works if you head over to client components documentation page it'll say that client components allow you to write interactive UI that is pre-rendered on the server but it can use client JavaScript to run it in the browser of course you get the interactivity and the browser apis now we want to add this used client to declare that a function is clientside the rendered because in new next s all of the components by default and pages are server side rendered so if you want to use hooks which is one of the most common use cases for client components you'll want to add this use client directive at the top or whenever you want to add some interactivity such as in our case we added it to be able to navigate to different pages and routes this is definitely a browser related event so we need to make it use client server components with all different types of rendering and their differences from client side components and much more is covered in a lot of detail within our ultimate nextjs course because if you make all of the components as client side the application can often be very slow but in some cases you have to keep them client side to still preserve that interactivity of course learning when to use which will take some time getting used to and is definitely something we talk a lot about within our course but for now what you need to know is that a good rule of thumb is that you can keep the component server side all the way until you actually start adding some interactivity to them such as hooks or browser interactivity which is when you're going to get an error and then you can convert them into use client components with that in mind back inside of here we now have something that looks like an actual sidebar let's also focus on adding icons to each route which we can do by taking a look at our public folder and completely deleting it because for the purposes of this course I went ahead and collected all of the icons and images we're going to use and put them in a zipped folder for you so simply go to the description download it unzip it and then paste it right here within the root of our application this new public folder will contain icons and images some of which will be our avatars as well as our hero background and then we have a lot of SVG icons so with that in mind now that we have this public folder we can add some more content to show alongside the label specifically that will be a nextjs self-closing image tag which we can import from next image we can give it a source of link. image URL I'll tag off link. label a width of about 24 a height of about 24 as well and then below it we can display A P tag that will render the link. label of course we can style it a bit by giving it a class name equal to text- LG font D semibold and on Max large devices we can simply hide it this is mostly for mobile devices that have the width less than 1,000 so if you go back to the app we now have something that looks and feels great but if you try going to upcoming you can see that it breaks we get a 404 so now that we have the sidebar that acts as a router what do you say that we go ahead and add all of the necessary routes and as nextjs uses the file based routing concept we can just add them as new files and folders within our app rout home and here we can create a couple of new folders first is upcoming that will have its own page. TSX which will also say something like upcoming next we're going to create a new folder called previous that will also have its own new page. TSX where we can run ource and call it previous after that we're going to do a new one that's going to say something like recordings and we can also create a new page. TSX where we can run rafc that will say recordings then we can create the last one which will be personal Das room which will also have its own page. TSX where we can run RFC and we can call it personal room there we go so now we have four different pages as you can see right here and that's more or less it this is the only thing we needed to do to implement routing so if we go back click home you'll be able to see that we are on the homepage although this text right here is not that visible so we might want to fix that so let's go to this homepage and let's turn it into a section this section will have a class name equal to flex size- full so it takes 100% width and 100% height Flex Dash call so we show some elements below gap of 10 and a text off wide so we can actually see what we're doing within it we can render an H1 that will have a class name equal to text- 3 Excel to make it very big as well as f-bold to make it bolded and it can say something like let's to home and Below we can have some kind of content so let's go ahead and copy this entire section we have here and let's also use it for all of the other Pages let's start with personal room override what we have here and instead of Home simply say personal room let's go to previous as well override the dev and say previous let's all Al go to recordings modify it right here and say recordings and finally upcoming let's paste it modify the home to say upcoming now if we collapse them and go back you can see home spelled out nicely right here and if we navigate over to upcoming we can see a large upcoming if it doesn't look like this for you reason why it's not active well our variable should work correctly it should say true for homepage but as you can see it's applying a property of BG blue one which we didn't yet add to our Tailwind config so let's navigate over to the config and let's add a second set of colors with the name of blue and we're going to do one and back in our Zoom clone we can double click a few times times to get to the exact color which we want to use going back we can simply paste it as a string now you can see Tailwind automatically picked up our variable right here and if you reload home sweet home here we are now the top of our application is looking a bit blank right now so what do you say that we go ahead and implement the knob bar to do that we can just navigate over to the knobb bar component and start working on it and now might be a good time to put our editor side by side by our browser so that we can see the changes that we make live there we go that's much better but you can also see that now our sidebar is hidden so we'll have to figure out how to show all of those links and routes on mobile devices as well so starting with the Navar we can turn it into an HTML 5 semantic nav tag and we can give it a class name it can have a flex between property because we want to show some things on the left and some things on the right it will be fixed to the top with a zindex of 50 so that it appears above other elements it will have a w all full for full width a BG dark one same as the sidebar padding X off six padding y off four and on large devices padding X of 10 now it's hard to see as those colors are very similar but it is there now right within our nav we're going to use a link coming from next link so we can autoimport it we can give it an hre of forward slash so it points to home with a class name equal to flex items Das Center and a gap of one right within that link we want to render a nextjs self-closing image which we can import from next image and we can give it a source of/ icons SL logo.svg we also need to give it a width of 32 and height to 32 now it will no longer complain and we can also give it an Al tag of something like Yom logo that's our version of Zoom with a class name of Max DSM size of 10 so we're modifying the size on different devices right below that image we're going to do a P tag that will simply say yum so this is again our version of zoom and we're going to give it a class name equal to text Dash inside of square brackets now why do we do square brackets well in T and CSS you do square brackets whenever you want to apply specific values so in this case I want to apply something like 26 pixels and that's how we do it we can also make it font something like extra bold text- white and on Max SM devices it can be hidden there we go so this is too small to show the logo because we'll show some more stuff later on but if we expand it you can see it and also on this tablet layout you can see that we don't show the text we only show the icons so now let's go back to mobile and let's implement the mobile nav mobile nav will look something like this we have the logo we have the clerk user management right here and then we have all of the links nicely animating from the left so to do that below the link we can display a div with a class name equal to flex between and a gap of five here later on we'll Implement our clerk user management so I'm going to add a comment for that Clerk user management but for now we want to go below it and we want to show a new component called mobile nav so let's create it by going to components and creating a new file called mobile na. TSX where we can run rafc and call it a mobile nav go back and render it as a self-closing component which is imported from that/ mobile nav and with that in mind we can now navigate over to mobile nav and implement the nav bar for mobile devices of course I mean the entire mobile sidebar that jumps out from the left so to get started let's wrap everything into a section that will have a class name equal to w-o Max dw- 264 pixels so this is the width we want the mobile nav to have have right within we want to use our first Shad CN component of the day and once again I want to teach you how to truly build apps on your own so we're going to refer to the documentations as every grade developer should I headed over to chatsi and UI I'm going to press command K which in most modern developer applications opens up this search bar here you can search for what you're after in this case we're going to use a sheet component sheet is a dialogue that expands so if we click open this is how it looks like let's go ahead and install it by copying this command and pasting it within our new terminal that we get after we split it and press enter next let's copy its usage so here we can copy just the Imports let's paste them at the top and then we can also copy this sheet right here and paste it right within our section of course let's also properly indent it and then here you have a couple of examples it can come from top bottom left right you can modify the size and more that's what I'm going to teach you but it's important that you know your way around using the components from shaten or any other external package so back into the home we cannot see much happening right here but don't worry we will soon first we have this sheet trigger which won't just say open rather it will render an image coming from next image and this image will have a source equal to SL ions SL hamburger. SVG it will also have a width of about 36 and a height of 36 as well so if we save it you can now see it appear right here and then it opens up this sheet that we have copied over from shaten let's also give it an Al tag of hamburger icon and a class name of cursor Das pointer so people know it's clickable and on small devices we're going to hide it also this sheet trigger will appear as child so that means that it's a child of a sheet component now currently we can see it below this icon later on we'll move it to the right side but for now we can leave it here as we'll still have a lot of work to do to make this sheet look a bit closer to our overall design so let's focus on the sheet content by giving it a side equal to left a class name equal to border Das none and a BG dark off one now in this case we won't be needing the sheet header title description and all that stuff so we can simp simply remove it and instead of that we can render a link this link will be the same as the link that we used before so we can head over to our Navar to Simply use it that's going to be Navar and here we have a link that says Yume so let's copy this entire link and paste it right here within our sheet content of course let's properly indented and let's also import link coming from next link if we now save this and if you click the sheet you can now see the Yum appear right there in this case we don't have to hide it because we are within the sheet so we're going to show yum right there and now we can go below the link to show all of the other links by wrapping everything in a div and giving it a class name equal to flex now we have to be careful here we have to give it a full height but we have to deduct by the height of the nav bar so we can use a cool tailin property called h- and then we can open square brackets and say cult so we can do calculations 100 VH which is full height minus 72 pixels that looks something like this no spaces we can also make it Flex Das Co so the links appear one below another and justify - between as well as overflow dy- Auto so we don't have a scroll bar now within this div we can also render something known as a sheet close which is a component we have to import from component UI sheet it will also appear as a child and it will have its own section right here this section will have a class name equal to flex h - full for full height Flex Das call Gap of six between the elements padding top of 16 and text- white finally within here we can map over all of our links the sheet close simply means that whatever we click here it will close the sidebar so let's head over to our typical sidebar and we can copy this entire part where we map over the sidebar links that supposed to be Flex one so with that done let's now copy the sidebar links all the way to the bottom here and paste it within this section and of course let's not forget to properly indent it by going all the way here and indenting it yeah I think that's looking good we can import the same sidebar links from constants which is another benefit of doing it this way and we also need to get the path name to know which one is active so at the top we can say const we can say const path name is equal to use path name coming from next navigation and of course as it's a hook we have to add the use client directive to this component and we can remove all of these components we're not using from components UI sheet and nicely put it in one line like this also we have to import CN right here as we have multiple class names coming from lib utils with that if you open it we have something that looks like this there's slight modifications which we'll have to still do first things first we have our link that has Flex rounded LG but in this case we don't need justify start rather we need a w full so it takes the full width as well as a Max dw- 60 and we're also applying the same property if it's active we can decrease the icon size a bit to 20 here and then we render a P tag that doesn't have to have font LG or Max hidden it can simply have font Das semi bold there we go that's much better now if we click on one of these it doesn't close so what we have to do is we have to wrap each one of these links in a new sheet close like like this and we're going to also give it as child and we have to give it a key of link that route since we're mapping over it so now simply copy the ending tag of sheet close and put it at the end of the link and of course indent the link properly so it fits right here and save it now if you click on a specific link it should nicely close the sidebar so you can then see the content which you clicked on great we're almost there with the sidebar But first you can notice a mistake we have and that is that both the home as well as another page are currently active and we don't want that so for that reason within our mobile nav we can just remove the second part of the equation here and just say if path name is triple equal to link that route and that will make it home upcoming previous and so on so this now works also shatan made the this component very nice you can just exit out of it you can click outside to exit it or we can navigate by simply clicking the link here you might have noticed something weird and that is that we're using a flex between property without first declaring this as a flex container also here and if you hover over it it's not going to give you Styles like it does in other properties so this must mean that this is a faulty CSS property but now not all CSS class names have to come from Tailwind you can also add your own classes to make your life a bit easier so if we head over to app global. CSS here you can find some class names that Tailwind added for you but on top of those classes you can also add something else add layer utilities and then here you can define utility classes to make your life easier such as Flex Center and here you apply a couple of tailin properties we're going to apply a flex a justify Center and items Dash Center and same things here Flex between here we're going to apply Flex justify between and items Das Center so you you get three class names for the price of one and as soon as you do that you'll notice that now Tailwind reads this or not Tailwind but rather our own CSS and knows to properly position this so now we have it at the end it nicely opens up and closes that means that now our mobile nav is good and we can actually navigate and we can get back to desktop and see this in its full Glory of course it will be better if we only have one active link so going back to our sidebar we can slightly modify our is active property by saying starts with open a template string and then say link. route and then add a forward slash so we want to ensure it has the forward slash as well as then it's not going to be just the home so now if you save this you can see it's just upcoming previous record Rec ings and more and home so what we have now is a beautiful modern and simplistic nextjs starter code where we have implemented routing as well as the initial design next I'm going to show you how easy it is to add o to your app and it won't be only o you'll use Clerk's UI components to manage your users now I know you're cautious about using third party tools especially for something as important as o but thankfully clerk is completely free for 10,000 monthly active users it's not just 10,000 users it's 10,000 monthly active users so let's read a bit more about the pricing it cannot be that good can it well if we go here you can notice that for the free plan there is no credit card required 10,000 monthly active users or all of the pre-built components and even a custom domain which is not something you see often with free plans and let's be honest our app won't yet pass 10,000 monthly active users so there's a high probability you will never have to pay for it at all and if your app does go viral and you really need to scale to hundreds of thousands of users you would need to pay only two cents per user after the 10,000th user but the fact that it's free is not the only only reason we'll be using clerk clerk integrates the nextjs applications incredibly well you get magic links social signin multiactor authentication and even more literally within minutes and this is something that would take you weeks to implement on your own so let's explore the docs here we can follow the nextjs quick start I can see that the next major version is coming soon so I'm going to use it immediately and you can use it with me but there's also a high probability that you're watching this video later on and that the version two is out of the beta in any case you should be able to follow along just fine so the first step is to install Clark nextjs let's copy the command and paste it right here next we have to set up our environment Keys we can create a newv dolo file and then add our environment variables so let's copy it head to the root of our folder and add a new file called env. local and simply paste it right here next we have to add clerk provider to our app so let's do just that going back to our outer layout which is just within the app so that is this one right here we want to wrap our body with clerk provider coming from clerk nextjs so we can just do this put the body within it it and indent it properly next we have to add middleware to our application so let's create a new middleware dots file and put it inside the root directory alongside the env. loal let's copy it and create a new file called middleware dots and we can paste it in this middleware we can Define our public routes by saying export default Clerk middleware later on we'll be able to modify our clerk middleware to make some routes public and others private next let's create a header with a cler component here we can use just this signed in to show the user button and we're going to use it within our navbar so let's head over to navbar and let's render it right here where it says clerk user management we show the signed in and we show the user button sign in is coming from Clerk nextjs and user button is coming from clerk nextjs as well finally we need to add a homepage which we already have and that's more or less it so let's sign into our clerk account so that we can fill out our environment variables because without them the app will say publishable key is not valid so to ensure your app is fully working and that you can follow the process exactly the same way that I do and that you see the same things that I do click the clerk Link in the description navigate over to get started and then either create your account or sign in once you're in you can create a new application and you can choose your signin options we're going to keep email let's do Google maybe LinkedIn and let's also add GitHub this is looking good so let's add the application name of JSM Zoom clone and let's click create create application and now we can copy our own customized environment variables and paste them right here and while we're in the dashboard we can also expand user and authentication go under email phone and username and turn the username on so we can uniquely identify all of our users with that in mind back in our code we can now see a user button if we're logged in but if we're not we have to to find a way to log the user in we can either do that by creating a signed out button so if we're signed out then sign us in using the signin button but we won't do it on click rather what we will do is we will set all of the routes to private so whenever you come to any route like Local Host 3000 or a meeting or anything we're going to automatically redirect the user to log in as our app requires authentication to work so to make that happen we can navigate over to middleware Dots here we can import a new thing from clerk nextjs server which is called create route matcher this will allow us to match specific routes which we want to make public or private so let's declare our protected routes by saying const protected routes is equal equ Al to create route matcher which we call and then pass an array of different routes first one is just the forward slash we definitely want to protect that we also have all of the other routes we have created so far such as the upcoming let's do a bit more for previous after that we have recordings finally we have the personal room and the last one is forward slash meeting and then in parentheses you can say do asterisk which is going to match all of the meeting routes and then you can create a callback function within the clerk middleware inside of which you get access to two different parameters oth and the request here you can check if we are on a protected route so if protected routes to which you pass the request to know whether the current request is going to the protected routes and then if it is a protect protected route then you call the O and then you call the do protect on it that's it that's the only thing you have to do no more going to the nav bar and then checking here whether you have access to the user and whether that user can see the page everything is done right here you can notice that we are automatically redirected to our authentication page now currently this off page has this weird URL improved javalin 31 what we want to do is get a bit more control over the URL as well as the customization of the page after all we know how our OD page should look like based off of this figma design it should look something like this so going back to our code we can go to our env. Lo and here clerk allows us to add two new variables nextcore undor clerk uncore signore inore URL this is the link to which we want to navigate for our sign in and that's just going to be/ sign-in we want to duplicate this and modify it for the sign up and of course this will simply say sign up now if we save that our environment variables should reload automatically and if we go back and rigate to Local Host 3000 you can see that now we are redirected to our own Local Host 3000 SL sign in but currently that page doesn't exist so let's create it back in our code we can close all of the currently open files and we can navigate over to app o sign in then within here we can create a new folder with a bit of a weird name it will have a double square bracket do dot dot sign in and then you close the double square bracket this ures that it catches all of the signin redirects and that we always end up on the correct page so now you can create a new file within it called page. TSX within this page you can run rafc and you can call it sign in page make sure not to just call it sign in because what we wrap within the main component right here is exactly that a self-closing signin component coming from clerk nextjs so basically the only thing you have to do is import sign in from Clerk and put it within the sign-in page and now if you reload you'll be able to see our own dark background on our own URL let's Center it by giving it a class name equal to flex h- screen for full height w- full for full width items D Center and justify Das Center 2 now we have a beautiful looking sign in right within our own domain let's also do the same thing for the sign up by copying all of this code and then creating a new folder with the same naming convention double square bracket do do DOT sign up and then you also close double bracket and you create a new page. TSX right within it within here you can paste what we copied and and replace the signin to sign up make sure to call it and then also replace signin page to sign up page now if you navigate over to sign up you'll have a create your account o page where you can navigate to sign in and vice versa this is beautiful and of course if you want to apply further customizations you can the only thing you have to do is inspect the source try to find an element you want to modify such as this entire background and then you can change the color for example background to something like black and there we go it changes instantly and you can also Target specific text elements to make them white the CSS classes that change some of the default behaviors are typically called overrides so while I was first developing this project I took some time to Target a few elements and show show you how we can override them so in the read me down below you can find a complete global. CSS files that contains a few of these overrides so simply copy it and paste it here if you scroll a bit up you'll notice that all the way at line 56 we have a few of these clerk overrides where we target a specific class name like CL logo box we modify the height the background mostly changing the colors of text to white and colors of backgrounds to a bit of a darker color and also we have a few overrides for stream components which we'll be using later on with that in mind if we now go back not a lot of things change just yet that's because these are only some small overrides that we applied but clerk has their own official way of customizing all of clerk components and you do it right here directly within the clerk provider the only thing you have to do is access the appearance object and then provide the customizations you want to apply such as variables and here you can choose an object and choose the color of the text so color text we want to make that # FFF which is the white color so now you can see that all of the texts change to White I also collapse this so that it's easier to see what's happening also we're going to change the col primary to a Coler of hash 0 e78 F9 there we go so that's the color of our button we can also change the color of the background that will be our black color # 1 c1f 2E there we go that's more like it we also have the color input background which will be hash 252 a41 okay that's more like it and then finally we have a color input text which will be once again # FFF which is the white color now some of the additional customizations we can apply is the fact that we can add our own logo so here we can say layout and then we can Define the logo image URL which will be for slash ions sl- logo.svg and just like that it appears right at the top we can also add social buttons variant and we're going to make it an icon button now this matches the look and feel of our application much better than before so let's expand it one final time to full view currently everything looks good besides this this GitHub logo right here which is dark so if we inspect it and select it you can see that we have this image right here that's the CL provider icon GitHub maybe if we select it and try to apply a filter of invert of one it will work and it does it changes the color from black to white some CSS magic for you so we need to copy the class CL provider icon GitHub like this go back to our code to global. CSS find our clerk overrides there we go and we can apply this property CL provider icon GitHub and then you can simply say filter is invert off one again no special magic here just CSS and I'm happy that this happened so I can show you exactly how you would go ahead and change any part of this clerk checkout by yourself it's completely customizable so going back right here we have such a beautiful checkout that matches the rest of our design so let's go ahead and sign up for the first time to our application as you can see you have a regular username email and password authentication with complete error handling or we can use Google after you sign up using Google it will ask you to choose a username and this this case I'll choose JS Mastery and click continue and just like that we are redirected to our Local Host 3000 which is our homepage and on top right we have a beautiful user profile icon where you can manage your account you can update your profile your username add multiple email addresses and even more connected accounts and all of that is integrated and customized so nicely within our application and now we can focus on Transforming Our homepage from this to this and then soon after start implementing all of the video streaming functionalities like creating a meeting joining a meeting scheduling a meeting and even viewing meeting recordings so with that in mind let's start with the UI of our homepage starting with this nice looking Banner that tells you the current time and then also creating these four home cards first I'll collapse my homepage to mobile view and I'm going to head over to app rout home page this is our homepage so to get started let's remove this H1 that says home and let's wrap everything in a div this will be a div for our Banner so let's give it a class name that will be equal to h- something about let's do 300 pixels like this we need to give our Banner a height W full for full width rounded Dash 20 pixels so it has rounded edges and BG D hero as well as BG Das cover now again if you hover over these you can notice that nothing is happening and that's because they're coming from the Tailwind config so if we go over to Tailwind doc config.sys remove all of these things that have been here from the beginning we don't need any of that all the way up to the key frames we can properly close the colors and right below the colors above the key frames we can say background image this will be an object that will contain a hero key and it will be a double quoted string that says URL and then once again a single quarter string pointing to forward slash images forward slh hero - background.png and then you have to properly close it now if we save this and reload the page now we should have access to this background image so if we expand it to full view you can see that we have some notes meaning that we're preparing for a meeting that is about to be held within this div let's create another div that will have a class name equal to flex h- full for full height flex-all so that the elements appear one on top of another justify between on Max MD devices so these are devices larger than 678 pixels we want to give them a padding X of five same Max MD we want to give it a padding y of eight and then on large devices we want to give it a padding off 11 all across the board right within that div we can create an H2 that will say something like upcoming meeting at and we can say something like 12:30 p.m. for now this is static later on we can make it Dynamic if we want to Let's style this H2 a bit by giving it a class name equal to Glass morphism like this that's going to give it this glassy background and this is another class that's coming from our global. CSS so if you simply search for it you'll be able to find it here it simply applies a background with a backdrop blur we can also give it a Max DW of about 270 pixels and we can also give it a rounded padding y of two for some spacing text- Center text- base and fond dasn normal so this is kind of like a little chip that says when the next meeting is now now below the H2 still within the div we can create another div that will have a class name equal to flex Flex Das Co gap of two right within it we can create another H1 that will tell the time and here we can tell the actual time and date so that's going to look something like this let's do 11:30 a.m. and then below this H1 we can have a P tag that will render the date so I'm going to copy the current date and paste it here now let's style it properly by giving this H1 a class name equal to text- 4XL font D extra bold on large devices text- 7xl so it's going to be even larger finally let's style this P tag by giving it a class name equal to text- LG so a bit smaller font D medium text- Sky D1 and on large devices text- 2XL this sky is a new color so you know what we have to do go back to Tailwind config and then add a new color of Sky that's going to be below Blue Sky off one that's going to be C9 DD FF and we have to add a hash up front that's going to give it this nice bluish color now the next thing we have to do is figure out how to use JavaScript to get the current date and time and not only get it in the date string but present it as a human readable string of time and date this can be a little exercise for you if you want to play with it so create a new variable called const time and that's going to be equal to some kind of of a string and you can also do a const date is equal to also some kind of a string right here now instead of using these values instead of time we can show the dynamic time and instead of date we can show the dynamic date property of course both of which will be gone as there's simply an empty string right now now just to give you a bit of a hint if you want to do it on your own we can say con now is equal to new date and now the way this is going to work is you can play with this now property calling now. to Locale time or date string for example let's do time string like this and that's going to give you some kind of a time but of course we don't need seconds in this case and we also need to get the date so if you want to pause this video pause it right now look into this new date object and try to make it exactly what we want in the final application which is a date and time that looks something like this no seconds and here we have a long date format give it a shot and then be right back if not I'm going to show you how to do it so first of all we can specify which countryes time format we want to get in this case that's going to be en nus like this and then we can also pass an object for this options such as hour is going to be in a two-digit format and then a minute will also be in a twod digigit format that's the only thing we needed to do for the time now let's play with the date it's going to be a bit more complicated but nothing special first we're going to wrap everything in a new parentheses and say new international so just entl date time format which you call as a function you pass the en nus and then you pass additional options of date style which is going to be set to full if you do this you're going to see that this is going to return a date object so now you have to call the dot format now onto that date object which will give you the current date and with that the top top part of our homepage has now been implemented and it's looking great now let's focus on one of the most important parts of our homepage which is this bottom part that represents four different cards allowing you to do four different actions within our Zoom clone application so one thing that I already know is that these things will have to either open up a model like this or they will have to do a redirect both of which are client side actions so that means that we have to convert this into a client component so that means that we have to put them within a client component so we can create one right away by going to the file explorer components and creating a new component called meeting type list because this is a list of different meeting types and we can call that TSX and run RFC and we don't have to turn it into a use client right now but based on our previous experience we can already know it will be a client side component so that's why we have taken it apart from the page and used it as a new meeting type list component which we can then refer to right here so now we have the meeting type list which is client side rendered but we don't need any client side functionalities for this so the entire page still is server side rendered I hope that makes just a bit of sense this will allow us for the page in general like the sidebar Navar and this main clock to load much more quickly than these links so that will increase performance and improve the overall user experience and if remember that will also give you much better core web vitals going from something like this to this but as I mentioned before you cannot always do it right we can do it in this case but for a meeting page the entire meeting will have to be client side as we'll be using hooks within them so with that in mind let's navigate over to the meeting type list and let's start implementing it we can focus on the jsx first wrapping everything in a section that has a class name equal to grid yep I don't use grids often I mostly use flex but when you literally have something that looks like a grid like four elements appearing one after another or one below another you can for using grid we're going to do a grid that has a grid KS one with a gap of five and on medium devices we can do grid calls 2 and on extra large devices grid calls 4 this is the only thing you have to do to get responsiveness with simple grid like this now with then here we can create our card that's going to be a div that will have a class name equal to BG - orange-1 and immediately you know that we have to add this orange to our Tailwind config which of course we can extract from our figma but now that you know how easy it is to create your own Tailwind config and now that you fully understand how it works in the description down below I'll provide you with the rest of the Tailwind config you can notice that basically everything is the same the background image and all of the colors we just added a few more colors and now it automatically recognizes this BG orange one while we're creating this box let's write something in it like box one and there we go we can see it so now let's continue styling it by giving it a px of four padding y of six Flex Flex Dash call justify Dash between w- full on extra large devices Max dw- 270 pixels Min dh- 260 pixels that's going to give it height rounded -14 pixels that's going to make it a box round it a bit and we can give it a cursor Das pointer to make it seem like it's clickable great now each one of these divs will actually be clickable so we can add add an onclick property which for now we can leave empty but later on we'll do some actions right here and there we go you know what I was saying as soon as you add some interactivity like on clicks or event listeners or so on you have to turn it into a client component so right at the top we can say use client which will make it a client component now within this box instead of simply saying box one let's render a div and that div will have a class name equal to flex Das Center Glass morphism size of 12 and rounded of 10 pixels that's this little box within this box which will render the icon so we can render the self-closing image tag which we need to import from next image and it will have a source equal to let's do icons add meeting SVG and of course we also have to give it an ALT tag of meeting and a width of about 27 and a height of 27 as well so now we have this add new meeting below this div we can have one last div that will have a class name equal to flex flex-all and a gap of two and right within it we we can have an H1 that will render whatever this box says so in this case that will be new meeting and below it we can render a P tag that will say start an instant meeting great let's style this a bit by giving this H1 a class name equal to text- 2XL and font Das bold and let's also style the P tag by giving it a class name equal to text - LG and fond dormal there we go that's our box now here's a quick lesson for you we don't need just one box we need four different boxes with different titles descriptions icons callers and actions that they do but still it's the same box right so what we're going to do is we're going to take this entire div right here with the class name of BG orange one cut it away and turn it into a new component which we're going to call home card so new file home card. TSX run rafc and then instead of this div paste what we just copied which is this entire div and it looks like I have to properly close it and and I have to import image from next image and it would be good if we properly indented all of this so I'm going to select all of the lines and just bring it back by one notch there we go so now we have this home card which I can call within the meeting type list home card and call it like this and here is our card now the power of creating a reusable component is that we can now simply reuse it three more times and we get something that looks like this but that's not necessarily what we want right we want to have four different boxes boxes that are blue purple yellow and that say different things so a lesson on creating reusable components is that first you have to figure out when you can reuse something and that's usually when it looks or feels similar like it has the same layout and then the second step is to figure out the things that are different in this case that is the color the title description icon and the action that happens once we click on it right so now that we know that you can pass those as different props so for example for the first home card we're going to pass an image IMG of SL iicons slad meeting. SVG we're going to pass a title of new meeting we're going to pass a description of start an instant meeting and we're going to pass the handle click functionality which will set the state so let's immediately create a new State field use State snippet that's how I like to create States it's going to be called meeting state and set meeting state which will at the start be empty we have to import use state from react and what we can do with typescript is Define the exact types of this state so it can be three different strings is schedule meeting or is joining meeting or is instant meeting or finally it's undefined at the start so now we know what this meeting State can be and on this card home card we're going to call a call function that will simply call set meeting State and set it to the first box will be is joining meeting there we go now of course if we go back and save this nothing will happen because even though we're passing these props in we're not properly accepting them within our home home card so let's just copy this and paste it for all other four home cards as well like this there we go and the last one and now we can modify the props for each different card the second one will have not ad meeting but schedule. SVG it will say schedule meeting and we can say something like plan your meeting for the description and for the handle click we can say is schedule meeting now cool thing about the types we provided is if you said something like is scheduling meeting it will automatically tell you that this type does not exist on this set state which is a pretty cool thing to save you from yourself next up let's focus on the recordings so this will be the box for recordings. SVG it will say something like view recordings and the description can be something like check out your recordings and the handle click will be a bit different for this one as we don't necessarily want to open up a model we want to reigate to the recordings page so for that we can use the router so right at the top we can say const router is equal to use router and we can import that coming from next navigation that allows us to very easily say something like this router. push and then we can push to forward SL recordings the page we have already created and these are our four boxes and I just noticed that I forgot passing one of the most important things to each one that is also different which is the color so we can pass an additional class name equal to let's make it something like BG D orange-1 for the first one we can copy this for the second one we can do BG blue one for the third one we can do BG purple one and for the last one we can do something like BG yellow one there we go so now we're passing all of these props but still nothing is changing but don't worry about it changing will be so easy now for all four of these as we can now navigate into the home card and accept all of these props for passing props like class name image title description and handle Click Of course as we're using tab script we have to Define these as home card prod props which is a new type or an interface so an interface is basically type that is extendable we're going to call it home card props and you basically Define of which types are these properties class name will be a string image will be a string as well we have a title of a string description will also be a string and finally the handle click will be a function that returns nothing void and now we can modify all of our properties let's start with the color we can remove the BG orange one and instead we can make this Dynamic and make it a CN function coming from lib utils to which we pass all of these existing strings which looks something like this if we properly close it there we go and then you can pass an additional parameter as we have learned before to this CN function which is the additional class you want to pass in this case the class name coming from the props so now immediately all of them get a different color let's continue with what happens once you click on them here we can simply call the handle click because each one now has a different action also we can modify the source of the image by saying source is equal to to IMG there we go we have a plus a calendar a camera and another plus right here oh I might have missed that one so at the bottom of the yellow one it's not add meeting rather it will be join meeting. SVG we can say join meeting as the title and we can add a description via invitation link and and this will set the meeting state to is joining meeting so now if we save it we have different icons on all four of these so going back to the home card let's also modify the title which is as easy as simply rendering a dynamic title property and a dynamic description property you see how easy it is once you pass all of these as props and that's it you just got four different completely custom cards this was a great lesson on when to create reusable components and with that if we now expand our homepage you can see how great it looks like in this case I will even skip adding these today's upcoming meetings because our home cards nicely fill up the space so now that we have our home finally we can start focusing on what happens once we click on a specific card and referring to the final application once we click new meeting we have a new model that opens up that says start meeting so let's do just that by going back home and back in the meeting type list we can scroll down still within the section we can create our meeting model this will be a new component called meeting model. TSX where we can run rafc and we can simply use it right here meeting model coming from that/ meeting model back in our existing application if we scroll down we can see the model right now at the bottom but we don't want it to be at the bottom we want it to actually appear as a model so first things first let's pass the needed props to it so that the model knows how it needs to behave when it needs to open and when it needs to close first of all we're going to pass the is open property which will be true only if the meeting state is triple equal 2 is instant meeting then we can pass the on close property which will be a callback function where we're going to set the meeting state to undefine so we're going to reset it so it closes we can also pass a title which will be start and instant meeting a class name of text- Center we're going to use this later on a button text so this is what the button will say start meeting and then we can pass a handle click so what will happen once we click it where we can call a new function called create meeting which we have to Define right here const create meeting is equal to a callback function so with that in mind now we're passing all of the necessary props into the meaning model so let's dive into it and let's accept all of those props first we have the is open we have the on close the title class name we can also accept children later on the handle click button text image and the button icon there's a lot of props for this one one and that's going to be of an interface meeting model props which we can Define right at the top by saying interface meeting modal props where is open will be set to a Boolean on close is a function that doesn't return anything so void title is of the type string class name is optional and it will be of a type string then we have children which will also be optional of a type react node which we have to import from react handle click which will also be optional and it will be a callback function that returns nothing so void a button text which will be of a type string and it will also be optional so sometimes we won't have a button image which will be optional of a type string and a button icon optional of a type string now we have everything we need to start creating our meeting model and this meeting model will be shat cn's dialogue it is a window overlaid on either the primary window or another dialogue window the way it works is you click a button and then it opens up so let's go ahead and use it by first installing it by copying the command going to our terminal and pasting it right here and while it's installing let's see the usage we can get a few of these Imports and paste them at the top and immediately after we can copy its usage so let's scroll down and replace the div with the dialogue and let's indent it properly now if we save it and go back to our working application at the bottom you'll be able to see the open Button which you can click click and it will open up some kind of a dialogue which doesn't seem to be quite styled so let's go ahead and fix it and make it look better this dialogue will have the open property equal to is open coming from the props so now if we click open it won't work because this is not a dialogue trigger rather we'll trigger it by clicking on one of these boxes so if you remember the is open we're passing to this meeting modal is if meeting model is equal to is instant meeting and we're setting it to is instant meeting if we are right here under new meeting oh I think this right here should have been is instant meeting not is joining meeting so now if we click new meeting it's going to open up now let's figure out what we're going to put within it we also can pass a function to close it on Open change and here we pass the on CLI close now within our dialogue content we don't need anything else for now so we can only keep the content and we can give it a class name equal to flex w-o Max dw- 520 pixels like this we can also give it a flex Dash call so the elements appear one below another a gap of six a border of none a b g- dark of one padding X of six padding y of N and a text- white this is better so now we have an empty dialogue content right within it we can render a div and that div will have a class name equal to flex flex-all and a gap of six and within it we can see if we have access to the image so only image and and if we have the image we under a div we have to close that div as well and within that div we will render a self-closing image tag which we have to import from next image give it a source equal to image an Al tag of image a width of 72 and a height of 72 as well now for this particular model we don't have an image so we cannot see it but for some other ones we might have an image in which case we'll render it it's all about making this component reusable for now let's give this div a class name of flex and justify Dash center now below this image we're always going to have an H1 so let's make it render the title in this case it's start an instant meeting we can also give it a class name equal to we're going to make a dynamic CN coming from lib utils which is going to render text- 3XL f-bold and leading das42 BX like this and we can also pass the class names which we're passing from props of course we have to fix this to say H1 and now it's a big start and instant meeting right below this H1 we're going to Rend their children if we have any and then below the children we can render a shaten button which is going to come from UI button and it will either render the button text coming from props or it will say schedule meeting like this to this button we can pass a class name equal to bg-- one to make it stand out focus Das visible ring of zero like this so this is just to fix some default behaviors when clicking buttons and then we also have Focus visible ring- offset d0 there we go looks good also to this button we can pass an onclick property equal to handle click coming from props and if we have a button icon so only if button icon exists then we can render a new image right here with a source of button but icon with an ALT tag of button icon a width of about 13 and a height of about 13 as well in this case we don't have it and if we do we have to provide some space between the button icon and the button text so we can do and mbsp and then semicolon this is an extra space between the two and this my friends is our model completely done and waiting to accomplish its first action which is start meeting that means that we can now go back to meeting type list check out our model see it's calling create meeting and now we can focus on this function right here where for the first time ever we can create our first meeting room and that means that we finally came to the part of the project that you clicked on this video for implementing your own Zoom clone with video streaming functionalities and to implement it we'll use the industry-leading tool called stream stream allows us to implement chat video and audio in days instead of months or even years that would take to develop such a scalable and robust video platform and I want to take a second here to discuss why we might want to use a third part party tools such as stream instead of building this from scratch inhouse streams apis and sdks are developed by hundreds of Developers for multiple years and they power a lot of huge companies so sometimes it's not necessarily about learning how to do everything from scratch it's about learning how to use the existing tools that are at your disposal to accomplish the tasks that your app needs to focus on in this case we'll be using their video and a audio better release so let's learn more about it it's built with developer experience in mind and docks to help you build it also you're following this video so you know that everything will work perfectly but let's discuss the elephant in the room pricing as clerk stream allows you to build using their sdks and apis completely for free you get 200 bucks of free credits which means $66,000 minutes per participant what's very important is that there's no credit card required and you're getting a lot of these features completely for free it would take us years to develop this on our own so why not use something that is compliant has phenomenal up times security and support it's about learning how to use what is at our disposal so with that in mind let me teach you how to develop worldclass video and audio directly within all of your apps first you need to click the stream link down in the description it will allow you to follow along and see exactly what I'm seeing on the screen and develop it without any issues once you're there head over to developer video and audio and then head over to react docs next go to installation first we need to install the packages needed for stream video react as decay let's install it using mpm back within our app we can run mpm install at streamio SLV video- react dsdk once that's done we can go to the quick start the quick start will give us a quick overview of how streams video sdks work first we have to set up the client and calls by creating an instance of stream video client that will establish a websocket connection by connecting a user then we have to create a co op and join the call by specifying create to true to create the call if it doesn't exist so let's go ahead and copy all of this and let's close all of the currently open files I want this to have a clean working slate to prepare for setting up stream so let's open up the file explorer and let's create a new folder in the root over directory called providers providers are components that wrap our entire application and Infuse it with additional functionalities within it let's create a new file called stream client provider. TSX right here we can paste what we copied we're importing stream call stream video stream video client and user coming from stream.io video react as thec and within here we have to get our API Keys user IDs tokens and more so let's remove this com M right here and let's first focus on getting those values let's sign up and create a free account by using email Google or GitHub next you can choose your username you can do something like JSM uncore and then type your name I'm going to do JSM Adrian next you can choose your role industry app user account and more choose video and audio and complete sign up and here we are welcome to stream let's start integrating our key and secret so in this case we have our JSM Adrian app with an app ID and the API key in this case let's copy the API key and let's add it to our environment variables by creating a new variable called nextore uncore stream uncore aior key and simply paste it right there next you can head to the dashboard we can see our app ID here but what we're after is our app secret so let's open up the project reveal our secret and copy it back within our environment variables we can create a new stream unor secretor key which will be equal to the key that we just copied now we can go back to the stream client provider and start setting it up first we can Define our API key and now we can get it straight from our environment variables by saying process env. nextore public _ stream _ API _ key the user ID and the token will get later I'll show you how we'll have to connect this to clerk so that each clerk user is connected to a stream user that'll be very cool so more in that later but for now let's turn this from an app to a fullblown stream video provider with which we can wrap our application so for now I'm going to remove everything just so it's easier to see and it's easier to develop the only thing we need to start off is stream video and stream video client these are the only two things we need for now and what we need to do is wrap our entire application with this stream video similar to what we're doing with clerk we're wrapping everything with a clerk provider here we'll be wrapping it with a stream video provider next let's export this app at the bottom by saying export default app and let's rename it from app to something more fitting like stream video provider great now every provider has to also return its children so right here we can get children from props and also give it a type children off a type react node coming from react now we can Define our video client and we're going to Define it as a state so let's say use State snippet we're going to call it video client like this set video client as well and at the start we're going to leave it empty we're going to import use state from react and we're going to make it of a type stream video client so it's nice that stream also provides us all of these types so we know exactly which properties are available on our video client and now we can say video client we're going to attach it to this stream video right now it's going to complain saying that it's undefined and that's because we have to properly set this video client up we're going to do it within a use effect so let's define a use effect hook you know it has to have a callback function and a dependency array whenever variables in the dependency array change the Callback function will be recalled and now things start to get interesting remember how I told you that later on we'll create a new stream user of course we need it to start its own meeting room right but we're going to create that stream user directly from our currently logged in clerk user let me show you how clerk makes it easy to get the information of a currently logged in user we can do it by saying const user and is loaded is equal to use user coming from clerk nextjs now we can add those two as dependency variables user and is loaded so at the start it's highly possible that we don't yet have the user so if notd is loaded or if the user doesn't exist we're going to Simply exit out of the function also if we don't have the API key like this we're going to also simply throw a new error something like stream API key missing there we go so now we have two fail saves built in next only if the user is here and the API is here we can create a new video client by saying const client is equal to new stream video client which we call as a function and provide an options object first parameter is the API key then we need to Define which user is creating this client each user needs to have the ID which we can simply grab from clerk by saying ID is user questionmark doid the second thing each user needs to have is a name and that's going to be equal to either user question mark. username or user mark. ID if we for some reason don't have the username and finally the image will be equal to user question mark. image URL again clerk nicely stores that for us now the last thing we need is something known as a token provider something that will verify that this user in indeed is that user and to do that we'll have to use this environment variable called stream secret key whenever a variable begins with next public that means that it's exposed to the client side of our application but if it doesn't have next public up front like clerk secret key or stream secret key that means that we can only access it from the server side for security reasons for that reason we can create create a new folder within our application called actions and within actions we can create a new file called stream. actions. TS why this file is special is that it's going to have a special use server directive which means that the code within here will only be run on the server and within here we can copy this API key and use it same as we would on the client side but now we also have access to const API secret is equal to process. env. stream secretor key here we can create a function which we will export so export const token provider is equal to an async function inside of which we'll get this token first we have to get the user from clerk by saying const user is equal to await current user coming from clerk nextjs server we can do a couple of checks like if there is no user then we can throw a new error saying something like user is not authenticated or logged in then we can do another one if there is no API key then we can also throw a new error saying something like no API key and we can duplicate that and if there is no API secret we can throw a new error saying no API secret great now we can begin by creating this new stream client const stream client is equal to new stream client and this stream client won't be coming from stream.io video react SDC rather it will be coming from the node SDC because we're on the server side and if we were using this app within a regular react ecosystem we would have to spin up a node Express server but here in nextjs we can do it all in one but we still do need to install that stream node SDK package so let's head back to the docs where we were in the quick start and we can quickly switch to API this API is basically the node SDK and here right at the start we have the installation where we have to mpm install stream.io node as decay which we can do by simply running that command and immediately after we have an example of creating a client so to create a serice side client you'll need API key a a secret found within the dashboard hey we already know that and then they give you an example of how to import stream client and create a new stream client using the API key secret and a timeout so let's do that first I'm going to put this to the side so we can refer to it later on and we can import this stream client with a capital letter s coming from stream node SDK right here we can call it as a function and we can pass the API key as well as the API secret let's call it just client so it's easier to see now that we have the client we can create users and user tokens which is exactly what we're here for see it's called a token provider so to create a user you need to provide an ID and their role optionally you can specify their name and an image as well tokens need to be generated on the server side typically this would happen where you register or loging your users yes that's it so to create it we'll have to do something like this new user new user object and then insert a new object what we care about the most is creating a new expiry date as can be seen here so let's simply copy this const exp as an expiration is equal to math that round new date get time and then we Implement a specific time this is going to be a token that's valid for 1 hour if you're not looking at the docs right now you can just copy it from here once we have the client and the expiration we also have to figure out when the token was issued and we can do that by saying const issued is equal to to math. floor date.now divided by 1,00 minus 60 something like this should do the trick once we have the client expiration and issued we can create a new token by saying cons token is equal to client. create token and then we pass the user ID which we have under the user. ID we pass the expiration exp and we pass the issued finally now that we have the token we can simply return it and this is our server action executing only on the server that does the logic Taps into the API secret creates a token for the user and we can now call it right here within our stream video provider without ever needing to spinning up a node Express server nextjs is very powerful and if you remember the ultimate nextjs course we talked about in lesson 22 we dive into learning how to create server actions which is exactly what this is an action being executed on the server with that in mind we can now say token provider is equal to the Token provider function coming from actions stream actions DS and we can shorten it like this that means that now we have everything needed to create a stream video client so once we have created it we can simply set it to the state set video client is equal to client and we can also set it here video client but what happens if the video client is not there yet of course at the start this use effect will run but before it runs it will try to get this video client which is undefined at the start so so we definitely have to provide some kind of a loader function so if there is no video client in that case we can return let's do a loader component we can do that by creating a new component within the components folder called loader. TSX we can run rafc that can be a div with a class name equal to flex D Center h- screen and w-o within it we're going to Simply render a self-closing image coming from next image with a source equal to icons or forward slash ions slash loading Das circle. SVG we can also give it an ALT tag of loading and we can give it a width of 50 as well as a height of 52 so now we can go back to stream client provider and simply return the loader in case we don't yet have the video client coming from components loader and you can see now it no longer complains because it knows that the video client is the right type finally once we have set up the client for the stream video we can return the children right here which means that now we have infused our application with the power of video by creating a stream video client and similarly how we're wrapping our app with the clerk provider right here within our app layout you see this we're wrapping our entire body we're going to wrap the second layout within root layout right now we're not doing anything here but this will be super simple right now we will wrap everything with the new component we have now created stream video video provider coming from ad providers stream client provider and the children will go within it this means that now our entire application knows about this video client we have how we have connected it to a specific user right here the token and everything so with that in mind let's see what the next step within the docs is and that is creating a call you can now create a call by providing the call type and an ID and here we have an example so remember where we were the last time we were at home page meeting type list and then we were working on this function to create a meeting and now finally that we have wrapped our entire application with stream video client provider we can now initiate a call first let's check if a user exists by getting the user by destructuring it and saying use us user coming from Clerk nextjs and also we're going to initialize a stream video client by saying cons client is equal to use stream video client coming from stream.io video react as thek now if we don't have a client or if we don't have the user we want to exit out we cannot create a meeting without those two but we have used clerk to get our users and we have just now also set up our stream client so it should be there next let's open up a try and catch block usually whenever you have a try and catch that means that the function must be a sync in the catch we can simply say something like console.log there's going to be an error but what happens in the try is much more important here we need to generate a random ID for this call see they're doing it right here as well my first call and just so we don't have to think of a new string every time let's use a library that does that for us or at least that's what we needed to do not that long ago but nowadays it's as easy as saying const ID is equal to crypto do random uuid which will create the ID for you now what is this crypto and where is it coming from well let's check out the MD and reference as you can see crypto is a global property available to you just by using JavaScript it allows you to quickly generate some random numbers and the random uid method will simply generate a random ID once we have that we can create a call by saying const call is equal to client of course referring to the stream video client. call and we can provide the type of the call we're going to say default and the ID of the call next if we don't for some reason get a call we can simply throw a new error saying something like failed to create call after that if we succeed we need to get a time that the meeting started at and we can do that by saying const starts at is equal to and for this one we'll Define a new used State variable use State we're going to call it values set values and at the start it will be equal to an object that has a property of date time equal to new date it has a description of the meeting equal to an empty string and a link of the meeting equal to an empty string as well so now we can tap into those values and specifically tap into the date time which is referring to the current date time and call the dot to ISO string which will give us a string of that date time or if that doesn't work we can create a new date and then pass the date dot now and then call the dot to ISO string like this now we know when it starts also we can get the description of the meeting by saying cons description is equal to values. description or it can be called old instant meeting the new one we created and finally now that we have the starts ad description ID call and everything else we can await call. getet or create so depending if it already exists or if we need to create it we pass an object and we pass additional data this data will include a starts at equal to starts at make sure to make it with an underscore right here for the key and then we have a custom property including a description once we create this call we want to set it to the state so let's create another state use State snippet call details set call details at the start it can be empty and it will be of a type call this call will be coming from stream video react as decay now after we get or create a call we can set call details to be equal to call you can see so far we're mostly following the docs right here then if there is no values. description we want to call a router. push and push to a template string of SL meting SL co.id so we're navigating over to that specific meeting room that was just created now now let's go back to our app and see if we broke something back in Local Host it seems like it has been broken for a long time but we haven't been checking it in between our provider or stream client provider definitely needs to be a client component so right here we can give it a use client directive that's going to fix this issue and we're back onto the homepage hopefully you fixed this one on your own now when we click new meeting everything we have been building so far led to this moment once we click Start meeting this function will execute and we'll see whether we can create a call and push to the meeting page so let's click Start meeting we indeed do get redirected to the meeting room with this newly generated ID that's good enough for now before we go ahead and continue working on the meeting room let's also implement shaten toasts a toast is a brief message that is displayed temporarily like this one that says that something has been scheduled in our case we want to know whether the meeting room was successfully created that'll be very useful for the user to know whether everything works or maybe something broke so let's quickly add this chatsi and toast by installing it second we have to add the toaster to the app layout so let's copy this import and let's navigate over to app and then this primary layout where clerk is let's import right here toaster and let's use it right here below the main finally how to trigger specific toasts is you use the use toast hook so let's copy this go to the meeting type list import this used toast use it at the top of the component like this cons toast is used toast and then we can call the function to initiate it let's first add it at the catch right here if something goes wrong so right here we can use just the title we don't need the description and we can say something like failed to create meeting there we go that's good we can also copy it and we can call it in a a couple of other places for example right here at the top within the try we can say if no values. dat time this will be important if we're scheduling a meeting then we can call a toast and say something like please select a date and a time that's good and we can also return because we cannot proceed with the function but more importantly we can call it if something goes right so right here at the bottom after we navigate we can call the toast with a title of meeting created that's the most important thing we want to see right now so now if we go back to Local Host 3000 click new meeting and click Start meeting oh we cannot see too much happening we're just redirected to the meeting room let's try reloading the page once more clicking the new meeting and start meeting oh there we go the toast is here it says meeting created but the design doesn't look that good this would be the perfect opportunity for me to show you how we can style shat and components you can go back to the layout hold command or control and then click on the toaster here on this div you can see the original toaster code and we can style it a bit by giving this toast a class name equal to border dnone BG dark1 and most importantly text of white now if we go back to Local Host create a new meeting and click Start meeting you can see a nice looking toast that says meeting created and we have a new meeting ID so do you know what that means it means that we can now navigate over to the me meeting page which is this special page. TSX within meeting ID page a meeting room yep we're finally transferring over from the homepage to first of all not yet in a meeting room but some kind of a waiting list setup where you can set up your mic camera of choice and more and then you can join the actual meeting room yep soon enough we be right within here so first let's do the setup page for the microphone and camera and then we can focus on the meeting room itself right within this meeting room we are already grabbing our params to know which meeting room we're in but now within here we also have to grab our currently authenticated user and that's easy right we can simply say const user and is loaded is is equal to use user coming from clerk nextjs but as soon as we do this you'll notice that if you reload it's going to require us to turn this into a client side component by the way it's not saying that explicitly right it's just saying that there's an error that's because this is a thirdparty hook not a hook that we wrote but a hook nonetheless so we still have to turn this into a use client directive component and once we do this we're now getting the user and we can also see the ID so instead of Simply showing the ID let's do a bit more with it let's wrap everything in a main section with a class name equal to h- screen and w- full so it's going to take the full height and width of the screen within this main we can wrap everything with a stream call coming from stream react video as the K and then within it we can also render the stream theme coming from video react as decay as well right within it we want to know whether the audio and video setup has been completed or not so to figure that out let's create a new use State snippet called is setup complete and set is setup complete at the start equal to false and we can import use state coming from react if the is setup is not complete then we want to render something like meeting setup else if the setup has been completed we want to render a real meeting room okay that's cool so we have either a meeting setup like right now as the setup hasn't been completed or a meeting room these will be two of our major components so let's create them within the components folder I can create the meeting setup. DSX where we can run rafc and I will also create a meeting room. DSX where I will also run RFC going back we can simply import those two as regular self closing components that's going to look something like this we close them we call them and we import them coming from components great of course we're going to start with the meeting setup but before we do that we need to know within which call we're currently in and we can do that by assigning a special variable call and passing it to this stream call but how do we get access to the call we're currently within well for that we'll develop a custom hook so we worked with server actions we worked with different providers with client and server components but we haven't yet created a custom hook so believe it or not within this video we'll do that too let's create a new folder within the root of our directory called Hooks and within the hooks we can create a new use get call by .ts and here we can create that hook a hook look is basically just a function starting with the word use so we can say export const use get call by ID is equal to a function that accepts an ID of a type string or an array of strings that's going to look like this and then we need to return the call so let's define the use State inside of which we can fill our call and set call like this we can import use state from react and at the start call will be empty but we know that it will be of a type call coming from react video SDK we can also create a loader which is a new state called is call loading set is call loading at the start set to True next we can get access to our stream video client by saying const client is equal to use stream video client and we can define a use effect so we can start fetching our currently active C we're going to recall our use effect whenever the client changes or when the ID of the call we're trying to fetch changes and we have to import the US effect from react right within here we want to check if the client exists and if it doesn't we simply want to exit on the other hand if it does we want to create a new load call function which will be an async function and then we want to immediately call it after word load call now why did I just declare a function and call it instead of just writing regular code like a normal person well that's because this function is an async function and you cannot write regular async await code within a use effect unless you declare it as a new function so that's why we needed to do it now the next thing we have to do is query all of the existing calls and query it by a filter which is ID that should be pretty simple right we can simply do something like const destructure the calls and say that's equal to await client. query calls to which we pass an object of filter uncore conditions where we simply filter by ID next we want to check if calls. length is greater than zero meaning if we fetched any calls and then if we did we can set calls calls zero so this is the first and most likely only call that we have fetched that's going to be just set call and finally we can stop the loading by saying set is loading is going to be set to false great so now we're setting the call we also to know when we're loading the call and the only thing that a hook needs to do is return something so at the bottom we can say return inside of an object call and is call loading that's what Hooks are for you call a couple of hooks you do some logic and then you simply return the output call and is call loading going back to the page right at the top we can say cons D structure the call and is call loading is equal to use get call by ID which we import from our custom hook and we simply pass the ID from the params and of course that ID is coming here so we can immediately destructure it great now just to be sure if not is loaded this is for the user or if still is call loading we can return a load component which we created not that long ago coming from components loader and finally we now have the access to our call and forever and ever whenever we're within this stream call we will know exactly which call we're in this stream call provider ensures that so with that in mind we can now head over to the meeting setup and prepare our camera and microphone for the meeting within our meeting setup we'll create a div with a class name is equal to flex h- screen for full height w- full for full width flex-all items Das Center justify Das Center Gap D3 and text- white within it we can render an H1 that will simply say setup of course we can style it a bit by giving get a class name equal to text- 2XL and f-bold and within it we'll render our first ever stream video component called video preview coming from stream.io video react as decay as a self-closing component right now it says video is disabled and that's because we have to allow local hosts to access our camera and microphone so let's do just that by creating a new use state which is going to be called is mik Cam toggled on okay A bit of a long one at the start set to false we also have to import used state from react we'll also create a use effect like this with a callback function where we're monitoring the changes in that is mic cam toggled on and we ALS also want to access the call itself and that's now super easy because we have given access to our call to our stream call provider so now we can simply say const call is equal to use call coming from stream video SDC and this call contains access to camera and microphone so we can say call. camera and call. microphone and of course we have to import use effect from react and we have to turn this component into a client component by saying use client within this use effect if is mik Cam toggled on we can then disable it so if it's already turned on we can turn it off by saying call. camera. disable like this and call. microphone. disable like this but else if it's not not currently turned on we can try to enable it by saying call. camera. enable as well as call. microphone. enable and just like that it will try to access your camera if you have previously given it your permission for now I'm going to clear my cookies and cash just so I can show you how that looks like from scratch right now the video is disabled so if I reload oh I have have to log in again so let me do that and it looks like it immediately gets it as we're on Local Host if we were on another website you would have to give it permissions to get it we can do another check and say if there is no call so if we cannot connect then we can throw a new error something like use call must be used within stream call component that's important and right now it is we have it right here stream cool so now that we have given it access to our webcam right here I can show this to you and it truly works so for the first time you're seeing that I'm not an AI I'm actually typing this out and explaining it to you so with that in mind let's continue now that we have given it video access let's continue with creating the rest of this we have video preview but we don't yet have the possibility to modify our camera or microphone and I hope this video right here isn't tooo distracting for you while you're trying to follow along right below this video preview we can have a div and that div will have a class name equal to flex h-16 items Das Center justify D center with a gap of three within it we can have a label for our input that that will have a class name equal to flex items Das Center justify Das Center gap of two and font off medium and finally you want to create an input right within this label which will be a checkbox so a type equal to checkbox and a checked property equal to is mik Cam toggled on with an on change where we get a callback function with an event set is mic cam toggled on and then we pass in the e. target. value so it's either on or it's off and yeah typescript save me here it's not value that's for regular inputs it's checked so that's going to give us a Boolean value and we can say something like below the input join with mic and camera off so in case somebody wants to turn off both the mic and camera they can easily do that and you can see how that works that's nice right below this label we can render the second video component coming from stream which is device settings coming from stream video react as decay so let's see what this one does as soon as we add it here and reload the page we have added it here but nothing seems to have appeared in the screen a quick look at the Docks says that this get device settings should open up a default device settings panel but why is it not appearing for us we should be able to get something that looks like this where we can choose a camera a mic and more but that's okay I don't mind that too much for now it might show up later the most important thing is that whatever browser you're using be that Arc like I am or maybe Chrome Safari Mozilla whatever it is you can go to site settings and then you can play with your camera and microphone permissions and you can allow them right here that's the most important thing later on we'll figure out why this device settings is not showing up and then you'll be able to choose your preferred camera and microphone devices for now let's go below this div containing the device settings and let's create a shaten button which we have to import from UI button and let's create a class name equal to rounded DMD BG green of 500 padding X of four and padding y of 2.5 and within it we can say join meeting there we go so now we have this beautiful green button and we can give it an onclick which we'll call a callback function where we simply call the call. jooin method and we can set the is setup complete variable from this page right here to complete so let's pass it as a prop to meeting setup set is setup complete which is equal to set is setup complete we can accept it right here at the top set is setup complete which is of a type function that returns void like this and now at the bottom we can simply call set is setup complete and set it to True looks like it's complaining a bit right now that might be because it's not void we need to give it a parameter so when we Define a type it's going to accept a value of a type Boolean and then return void there we go that's good so now once we see that our camera is working or or when we choose to disable it we can then join the meeting so let's give it a shot if I click join meeting we are successfully redirected to the meeting room because now the setup has been complete and we can now only see the meeting room component so finally we're diving into the part where we'll be able to have the grid of all of the participants within our meeting so let's command click into the meeting room and let's start implementing it first things first we'll start with the layout so let's transform this meeting room into a section with a class name equal to relative h- screen w- full overflow Das hidden and padding top of four as well as text- white this should set the tone right within it we can have have a div that will have a class name equal to relative Flex size- full items Das Center and justify Das Center we're just creating the layout one more time we're going to have a div with a class name equal to flex size- fo Max DW of 1,000 pixels and items Das Center next next right here we want to render a layout component that layout will be dependent on the layout state that the user chooses so we can first create a new used State snippet called layout set layout import the used State and at the start it can be set to speaker Das left where the speaker will show on the left side now we can have a couple different layout Styles so let's define them as the type type call layout type is equal to it can be either grid speaker left or speaker right so now we can Define the type of this state by rendering the call layout type now that we know the type we can create a new functional component const call layout which will render a specific layout depending on the current layout state so so we can have a switch statement and under the switch we're going to look for the value of layout and if that layout is grid then we can return a new component called paginated grid layout coming from video react as decay from stream if the case is something like speaker dasr we can render another thing which is going to be a speaker layout component from stream.io video react as decay with a prop of participants bar position equal to left participants are on the left speaker is on the right and we can duplicate this case one more time right below and call it speaker left and participants in that case will be on the right or we can simply make this a default return because if it's not Grid or speaker right then it must be the default case great so now we have this cool layout which we can just render right here there we go we can see our first user which is JS Mastery in this case I turn my camera off so we can see the full logo appear That's great let's continue with our layout right below this div which is wrapping the cool layout we can create another div this div will render all the participants so let's give it some class names equal to h- instead of square brackets cul 100 VH minus 86 pixels so this is for the navigation bar we can say hidden margin left of two so typically it will be hidden but if we want to show the participants we can make it active so we'll definitely need some kind of a state a new used State snippet called show participants and set show participants at the start set to false now we can make this Dynamic and call a CN function right here coming from lib utils and to it we can pass a string of all of these classes we have right now end it right here and as the second parameter we can pass an object where we're going to trigger the show block property only if show participants is set to true and right here we can render the Cole participants list coming from react video s DEC to it we can pass the on close property so we can close them as well where we can call set show participants and set it to false right now there are no other participants in the call let's see what happens if I try to join with another tab there we go I joined on the other screen and now we can see a participant appear right here over the screen of course it's not looking ideal right now but don't worry we're just setting things up we can now continue and Go below these two divs and focus on video layout and call controls by creating another div with a class name equal to fixed bottom zero Flex w- full items Das Center justify Dash Center as well and a gap of five and right within here we can render another stream react video component called Co controls there we go and let's save it and now we should be able to see a lot of different controls for managing our video audio and more right here at the bottom but there's nothing to be seen I do have this video camera coming from OBS which is a recording program on my computer but don't worry about that what matters is that call controls are nowhere to be found so with that in mind I just remembered that we wrapped our entire meeting page right here with something called stream theme and the react video SDK ships with a default UI theme that you can include in your application so the first thing we have to do is import the CSS that they provide to us so let's copy this or you can type it out with me and head over to layout just the base one within the app app that is this one right here outside of everything and at the top we can import at streamio SLV video- react dsdk this CSS style.css and now you can use this stream team and we can get all the base Styles just to reload the page and the Styles should load in now even back on our setup you can see that now now we have this settings icon which allows us to modify all the different devices microphone speakers and more and here you can choose your camera so if I switch to Adrian's iPhone camera hopefully I'll be able to show you that I indeed am a real person here are my hands and as you can see I am typing right here on the screen with you great so we can have this as our testing camera now with that in mind we can also change the microphone and the speakers so let's join the meeting and as you can see now that we have imported these stream video react Styles everything looks just a bit better we have our primary screen right here it says JS Mastery and if I modify my audio devices for example this microphone right here it should also be reacting to sound which is amazing and on the bottom right we can see the connectivity so with that in mind we can now go back to our meeting room and here are the call controls starting off from the left side we have recording functionality pretty crazy that that comes out right out of the box with stream if you click it it is going to load and it seems that nothing is happening but I do think that if we reload and then come back and it does seem like it's recording again this is something we're going to look into more depth later on for now I'm just going to pause it after the recording we also have emotes so that works right out of the box you can give somebody a thumbs up a thumbs down or confetti next up we have complete screen sharing yep it's built right in you can share your entire screen with just to click we're going to explore that later on a bit right here you can choose from many different devices or you can simply mute yourself and you can see how it's automatically reflected right here on the screen and then you can also choose from different camera devices or disable your camera if you do disable it you get this beautiful logo coming directly from your clerk profile so with that in mind you also have more functionality here such as muting audio video pinning it and even more and then at the end there's the possibility to Simply exit out of the call so you can do that too and let's also not forget that this will support multiple participants so more people can join and chat together we'll soon implement the button to change our layout as well so you'll be able to see different users on either a big screen small screen grid screen and more as I said a complete Zoom clone so just by following along with this video you'll be able to integrate video solutions to any of your future applications with that said let's go below call controls and let's create our first drop-down menu we'll use the drop- down menu to be able to change different types of layouts a drop down just displays a menu to the user such as a set of actions or functions triggered by a button so once we click it we want to see some options in this case we can copy the installation command and simply paste it in our second terminal next we can import all of the Imports coming from shaten right here at the top and we can copy the default use of shaten dropdown menu so let's simply paste it right here below call controls and let's properly indent it now if we go back to the app I can even turn on my cam I have a bit of a different setup for you here right now and what we can do is click this open button and as you can see we get a bit of a menu action going on so let's now style it properly first of all we can wrap our drop- down trigger within another div that will have a class name equal to flex and items Das Center we can simply put it within now we also wanted to render an icon called layout list coming from Lucid react with a size of about 20 and a class name equal to text- White and of course we have to properly close it that's a bit better so now we have a button that looks like it can change layouts let's also give a few class names to our drop down menu trigger such as a class name equal to that's going to be cursor dasp pointer we can also do rounded - 2XL let's do a BG off hash 1 192 32d I found that to work the best padding X of four padding y of two and on Hover a BG Dash # 4 C5 35b now we have this button which you can hover over and click great now let's focus on the drop- down menu content itself let's give it a class name equal to border D dark D1 as well as a BG dark one and a text- white and immediately these icons are going to look much much better now we don't need all of these menu labels or menu items so so let's remove all of the menu items and the drop- down menu label and let's create a new Dynamic block where we can map over grid speaker left as well as speaker right and we can call the do map on it we're going to map over each individual item and we can also get the index of that item and for each one we can automatically return a div that div will have a key equal to index and it will return a drop- down menu item like this which will simply render the item itself so now we have the grid speaker left and speaker right let's also give each drop down menu item a class name equal to cursor Das pointer so now we can almost click on them and we of course have to give it the actual onclick so let's say onclick is equal to a callback function where we're going to call the set layout function or set state to which we're going to pass the item do to lower case as call layout type that's just for tab script to know that we're passing one of the types and we can also introduce this drop- down menu separator right below the drop down menu item and give it a class name equal to border D dark D1 there we go so now we have a bit more space and I noticed I misspelled speaker right here there we go so now if I click on grid you can see it actually changes the view speaker left that's good and also speaker right this works great now we can actually change the layout and it makes even bigger difference on desktop devices I'm going to hide my camera for now as we don't need it and the logo looks better and we can go below the drop- down menu here we'll render a new button called call stats button coming from stream SDK now if we save it you'll see that a new button appears on the right side it's almost out of view right now so I'm going to bring it back into the view and click right here as you can see just by introducing a single component we can see live call latency so that we know whether we can conduct quality video and audio calls through streams apis in this case everything looks good on my end now right below let's add a button that will allow us to see or hide all the participants we can give it an onclick equal to that's going to be a callback function where we can call set show participants and we're going to make it a callback function where we get access to the previous state and we're going to call not previous state so we're going to toggle it on and off within the button let's render a div and that div will have a class name equal to cursor Das pointer rounded -2 Excel and we can copy all of the same class names from the button we had before it's this one right here BG Coler padding X padding Y and hover BG Coler as well so let's simply paste it here and within it we can render the users icon coming from Lucid react with a size equal to 20 and a class name equal to text- White and there we go we have the users icon and just by clicking on it you immediately get a nicely animated sidebar that shows you all the participants where you can even search and you can give or remove some permissions for other participants this is amazing as I said this is so great because a lot of these features are coming directly from stream which Empower you to build incredibly scalable video experiences within all of your apps finally we want to have a button that would allow us to end the meeting for everyone in case we are the room owner and if this is our priv room so let's create a new state at the top or we can figure it out by perams by saying con is personal room is equal to search params doget and we're going to try to get the personal query from the URL and to get the access to the search perams we have to say const search perams is equal to use search BRS great and now if we do have access to the personal meaning that it is a personal room then we want it to be true else we want it to be false if we don't have access to it so a quick trick to do that conversion from a try or falsey value to a true Boolean value of true or false is to use a double exclamation mark the first one will turn the falsy value like this one let's say that it is a personal meeting room so we're going to get back something like personal that is Trudy so if we call not personal we're going to get back a falsey value so that's going to be false and then we use another exclamation mark on false to get the true okay so we're just turning a try value into a real Boolean true but if it's not personal if it's undefined we call a not on the undefined which is true and and then finally we convert it into a Boolean false so this is a quick lesson of why we use a double exclamation mark now that we know whether it's a personal room that we can actually close right below this button we can render a dynamic block of code and say if not is personal room then we can render the end call button and this is a component that we will create so let's go to components and create a new end call button. TSX run rafc and simply import it right here at the bottom and I'm going to turn off my camera and join the meeting and here we have our end call Button now let's go into it and let's implement it right within here we want to get access to the information about the coal which we can easily do by using the used coal hook con call is equal to use call coming from react video stream as decay and since we're using a hook we must transform this into a use client component now that we have access to the call we can use another hook called const use local participant which is equal to use call State hooks coming from same stream.io video react as decay and we can get ACC access to that local participant by using the use local participant hook in this case we can simply call it local participant there we go so now that we have access to the call and the local participant we can check whether we are the meeting owner so const is meeting owner is equal to does the local participant exist if they do check the call. state. created by does that exist was this meeting created by someone if it was check if local participant. user ID is equal to call. state. created by. ID so we're checking whether the ID of the owner is the same to the meeting of the current participant and if we're not the meeting owner so if not is meeting owner and let me spell that properly is meeting owner we're going to Simply return null meaning not show the button but if we are the meeting owner then we want to show a shatan button that looks something like this we have to import it from UI button and on click we can give it an action a call back action that is asynchronous and here we can simply await call do end call like this and we also want to renate back to the homepage so we can get access to the router by saying const router is equal to use router coming from next router and now we can say router. push and want to push to the homepage finally I noticed that it should have been imported from next navigation so I'm going to fix this and I'm going to let the button say something like end call for everyone and we're going to give it a class name equal to BG red 500 there we go end call for everyone looking great so let's give it a spin I'm going to click end call for everyone it ended the call and redirected us back to the homepage that is great now let's make some small adjustments to make our app function even better we can get access to the use call calling state by saying const use call calling state is equal to use call State hooks coming from react video as thec and then we can get access to the calling State calling state is equal to use call calling State this is pretty interesting and something that I have never seen before the stream team created a use call State hooks hook which is a function that exposes All State hooks a pretty interesting thing to do so basically you say hey I want to get this hook and you get access to it and then you can do something with it now that we have the calling State we can check if calling state is not equal to calling state. joined like this and this calling State we can import from stream video SDK and if that is the case we can return a loader component which of course we have to import from loader now let's quickly fix this Model start an instant meeting let's see where that is that is right here under meeting types and then meeting model here it looks like I forgot to put a space between the max W and flex Co so just a simple space we'll do the trick now let's click Start a meeting next let's join the meeting and see if there's something we can do here well some of these buttons are jumping out a bit so let's go to the meeting room and let's see where we're calling the buttons here call controls fixed bottom maybe we can add a flex wrap into the mix there we go that's much better this makes the buttons wrap so as we expand you can see they nicely show on the screen and we can also play with the layouts of course we don't have any people in right now but this is looking good another thing that I noticed that we still didn't do is modify our favicon and title so to fix it we can go to our primary layout which is this one right here and add a title of something like yum or Zoom you can add a description of something like video calling app and we can add icons iconicons SL logo.svg now if you do this you can see this looks much much better now let's end the call for everyone we get redirected and you can notice that we also have to move this metadata to other layouts as well for this to work so we have to add it here don't forget to import the metadata type coming from metadata from next and we can also do it for the home layout as well so paste it here and import metadata from next great with that in mind as you can notice we're kind of slowly starting to make these smaller quality of life changes as we're approaching the end of this amazing build but we have only made our first box work start an instant meeting what do you say that we focus on the next one right now which is schedule a meeting that's going to require a bit of a different functionality so let's focus on that right now let's navigate back to our meeting type list these are the cards on our homepage and right now we're always showing a single model which is the one for starting an instant meeting but instead of doing that let's do a bit of dynamic checking let's check if call details exist that's coming from the top right here called details and if they don't exist that means that we want to create a new meeting or schedule a meeting so then we can render a new meeting model and as a matter of fact we can just copy this one as they're quite similar so this is going to be the one not for starting an instant meeting rather this one will be opened if we turn on the is schedule meeting the title will be something like create meeting and we don't need the button text nor the class name next if the call details already exist we want to show a different kind of model so I'm going to open up a new block and once again I'm going to paste the copy of this create model right now there we go this one will also be is schedule meeting it's going to say meeting created as the title and the handle click will be just a bit different it will be a callback function where we're going to call the Navigator do clipboard. write text so you can do this to copy the meeting link once we actually have one but first we'll have to schedule it and then we can also render something like a toast that's going to say title off link copied so once we actually schedule it we'll be able to do this to show that to the user next let's add the image of SL iicons SL checked. SVG a button icon of SL ions SL copy. SVG and a button text equal to copy meeting link and we don't need this button text right here great so now we have a model that opens up at the start if we don't yet have C details and then the other one once we do have the meeting details so how are we going to schedule a meeting well for that we'll expand our meeting model we won't make it just a typical self-closing component we will pass some children into it so whatever you type into it will appear right here which is pretty cool it allows us to make this model a bit different from the typical new meaning model this one will have additional fields and going back to our figma design it shows us how it should look like it should have something like the description and a date and time and then once we create it we'll be able to copy the invitation this is what we're working on it's much easier to understand what we're building when you can visualize ize it within figma right so would you like to see a figma shared every time when we develop one of these projects let me know in the comments down below with that said let's create this form we're going to wrap everything in a div and that div will have a class name equal to flex flex-all and a gap of 2.5 it will also have a label and let's go back to our code so we can see it off add a description that will have a class name equal to text-base text- normal and leading of 22 pixels that will change the line height we can also make it a text Sky 2 to change the color a bit below it let's render a text area and this is a special text area component coming from shat ZN so you know the drill let's install it by running MPX shaty and UI latest add text area now we can import it from shaten and schedule a meeting there we go it looks good but let's style it a bit by giving it a class name of Border dnone BG D dark D3 Focus Das visible off ring- Z and focus - visible D ring- offset d0 we need to do this to override some of the default behavior from these text areas such as this ring that happens if you can see it so this is a colon right here and now it doesn't occur that's great let's also give this text area an onchange which will get the event a key press event and set values to an object where we spread all of the previous values and modify the description to be equal to e. target. value great and finally now we have to add an actual date and time picker and for that we'll use the react date picker package so let's run mpm install react DD picker and press enter just like that below this text area and below the div that's wrapping it we can create another div this div will have a class name equal to flex w-f flex-all and a gap of 2.5 within we can also have the same label as we had above but this time it will say something like select date and and time there we go and finally we can render a react date picker component react date picker like this it's going to be a self-closing tag and we have to import it at the top by saying import react date picker coming from react dasd picker let's make sure we install that package correctly there we go I think we did but for some reason it has trouble finding it so if I exit the file and come back yep oh that's because we have to also install the types for that package which is always handy so just copy this command that you get on Hover and then paste it right here this will allow us to know whether we're using it properly or not it's going to tell us exactly which properties are we missing the first property we need is the actual value so we can say selected is values. date time like this then on change the way that works is we get a callback function where we get the date and then we set values similar to what we have done with the text area so we do an object we spread the previous values and then we modify date time to be equal to date and for typescript we also have to add an exclamation mark at the end next let's see how that looks like right now okay doesn't look that good let's give it a show time select to make it look even worse there we go let's also give it a time format of hhmm like this let's give it time intervals to something like 15 minutes time caption of something like time date format will be a bit longer mmmm d y y y y h mm and then a a it's a long one I know but it will allow us to properly see all dates and times and let's give it a class name equal to w-o rounded BG dd-3 padding of two and focus off outline Das none so now if we come back this looks just a tiny bit better but for this package I do believe that we also need its corresponding CSS file so if we go back to our layout within the app right below the stream video SDK CSS file we also need to import a react DD picker for/ slist SL react-datepicker docss so I think you can start noticing how all of these external packages might have their own CSS files which you need to import so now if we reload the page and go to schedule a meeting you can see that now we have something that looks like a decent looking calendar and of course you could further style it but for now this is good enough so we can choose a date and we can choose a time for when we want to schedule a meeting we can also add a description great so let's give it a shot let's say new JSM video brainstorming session so we can choose a nice topic for you guys and I'm going to do something like 22nd at let's do 2 p.m. and scheduled meeting there we go meeting created and we got a meeting link which we can copy or we cannot yet but if we go back here and we uncommon this code we should be able to get a meeting link so that's now the question where are we going to get the meeting link from well remember how before right here within this function we set call details to the call when we create a new call well what we can do right here above the return turn is say con meeting link is equal to a template string where we first render the process. env. nexu Bas URL So currently this will be Local Host but when we deploy it it will be your own domain name so what do you say that we go right here to environment variables and actually create this next public base URL I don't think we need those strings right here and this is actually the last environment variable of the day on our current local host it will be Local Host 3000 later on we will change it to a domain where our app gets deployed and we're going to also add the forward slash meeting and forward slash call detail question mark. ID and I think I'm missing an S right here there we go so now if we schedule our meeting and click schedule it says meeting created and we immediately get navigated over to it but What mattered even more was to see right here meeting created there we go it's in the future and we can copy the meeting link and it says link copied and then we can share it with somebody this is great so now that we can create instant meetings and more importantly we can schedule those meetings let's Implement our upcoming meetings page where we can see all of the meetings that we have scheduled to do that we can navigate to our upcoming page right here within home and we're going to be smart right from the start we'll create a component that we'll reuse across upcoming previous and recordings as all of these will show similar screens in this is yet another reason why it's good to have a proper Plan before starting with the development as you can see in the design it's pretty clear what we have to show on the upcoming meeting some kind of a card that has an icon a title date people who will be joining start and copy invitation in the previous meetings it's basically almost the same besides the start and copy invitation as the meeting has already been completed and then for the recordings it's also almost the same but we show a bit of a different information like the start time the ID and then the play and share buttons so with that in mind let's create a reusable call list component which will render a different set of cards so going to the components folder and creating a new component called call list. TSX there we can run ource and import that call list right here under upcoming and we're going to import it and we're going to be smart right off the bat and we're going to pass it a type which is a prop that will help us recognize whether this is upcoming or recordings or something else so now that we're passing it we can go into the call list and start implementing it first we're going to get access to the prop called type which will be of a type ended or upcoming or recordings so now we know exactly what the type can be next let's work on fetching those calls to do that we'll create a custom hook so let's go to Hooks and create a new hook called use get calls. TS within here we can export const use get calls which will be our custom hook like this and let's figure out how we can load those calls first we'll need to create a use State snippet called calls and set calls as well at the start equal to an empty array and it will be of a type call array which has to come from stream we also have to import the use State great now how do we get those calls well we're going to get them by using the client which is equal to use stream video client through it we'll be able to fetch our calls so let's also create another use state that will allow us to track of the loading State when we're fetching them so is loading and set is loading at the start set to false and also we have to fetch calls for a specific user so we can also get the user by saying const user is equal to use user coming from clerk see how easy it is to now fetch the user the video client and more and make them work together to do whatever our app needs to do next let's create a use effect it has a callback function and then a dependency array within a dependency array we'll listen to the changes for the client as well as the user question mark. ID because we have to have both to be able to properly fetch the meetings for that user and as you know the fetching of the calls is a synchronous and we cannot simply put a weight or make this function right here a sync that doesn't work so what we have to do is create a new async function so const load calls which will be equal to an Asing function and we can call it right after load calls next within load calls if there is no client or if there is no user question mark. ID we can simply exit out of the function so simple return else we can set the is loading to true because we're starting to fetch the calls so let's open up a try and catch block to make sure that everything goes right and we can also add a finally Clause where we can stop the loading so whether we succeed or whether something goes wrong in any case we need to stop the loading and in the catch we can simply conso log the error so what matters the most is what is in the try here we can destructure the calls by calling the await client this is the video stream client. query calls and of course this is also within the docs where you can call the query calls and then pass different options as you would into any other database like sorting options limiting options and more you can filter by ID by the team by the type even by created by user which is going to be very useful in our case so let's do just that we can pass an options object into query recalls where we first want to sort them by field starts at and direction of minus one so this is interesting we're sorting them by when they start because it's not really important when we created them since these are upcoming meetings we want to sort them by the starting date we also want to filter them somehow so we can apply filter conditions and also filter them by whether the start ad property exists so we can say dollar sign exists is set to True these are the only calls we want to show or we can run the dollar sign or add an array and then filter by created underscore byor user _ ID which we're targeting for the user ID this is the currently logged in user or if members is in user. ID like this make sure to copy it exactly as it is so we want to show this call if we are the one that created it or if we're the member within the call finally once we fetched the calls we want to set calls to be equal to calls wonderful now that we're fetching all the calls we want to make this hook reusable too so what we could of course do is just return calls and call it a day but instead I want to filter them out I want to create multiple variables const ended calls con upcoming calls const recordings and then we want to return all of that inside of an object so we want to pass ended calls upcoming calls for the recordings we're going to pass over all the calls because we're going to do some additional filtering later on and we also want to pass the is loading state so now the last thing we have to do is figure out the logic for knowing how to filter out the ended and upcoming calls first of all we have to get access to the now Time by saying cons now is equal to new date because if it's after now it's an ended call and if it's before now it's an upcoming call so let's first deal with ended calls by saying it's equal to calls. filter where we can immediately destructure the state of a call and then D structure there starts at as well as ended at variables and that's going to be of a type call then for each one of these we can return in inside of parentheses if starts at exists and if new date of starts at is lower than now or if double exclamation mark ended at so if it already ended that means that it is an ended call if the start date is lower than now and if it has already ended now for the upcoming calls we're going to do it a bit differently by saying calls. filter once again we're going to destructure the state of the call and then D structure that starts at that's going to be of a type call and we're going to return if starts at and if new date starts at is greater than now then it means it's an upcoming call so now we are returning ended calls upcoming calls and recordings from the used get calls which we can now call within the call list as the call list is a reusable component that can show the upcoming recordings and ended it is only suiting that the used get calls hook will also be reusable so let's call it const ended calls upcoming calls call recordings we have to properly call it like that within the used get calls as well so let's call it call recordings and finally is loading is equal to use get calls coming from hooks use get calls there we go now we can do something with that first of all we have to figure out on which page are we on are we on upcoming or are we on something else and of course don't forget to make this a use client because we're calling hooks so now based off of the parameter that we are on such as the upcoming right here at the top we have to figure out where we are for that we can use the router by saying con router is equal to use router coming from next navigation and then we can create a new function const get calls which will return the exact type of calls that we want to get back depending on the page we're on so we can use a switch statement and depending on the type of our call which we're passing through props we can now either return ended so if Case is ended return ended calls then if Case is recordings we can return now for the recordings we'll create a new use State snippet called recordings and set recordings at the start equal to an empty array let's not forget to import the use State and make the call recording of a type call recording again it's a bit different working with recordings rather than it is with calls so we'll have to do some additional filtering with that later on but for now we can return recordings and finally let's make a case for upcoming and here we can return upcoming calls is that how we call them upcoming calls let's make sure oh yeah upcoming calls anded calls that is looking good and default we can simply return an empty array great so now we can call this get calls to get the exact type of calls we're after also to make our life easier let's duplicate this function and let's call it get no call message so in case we don't have any ended calls we can say no previous calls if we don't have any recordings we can say no recordings and finally if no upcoming calls we can say no upcoming calls and by default we can return an empty string so now that we have the calls and that we have the get no calls message we can start Crea creating the jsx to show all of our cards let's first wrap everything in a div that will have a class name equal to grid yeah so this is the second time we'll be using a grid and as I told you when you have just cards that show one on top of another or one next to another using a grid is the way to go so we can say grid Das calls das1 a gap of five on extra large devices grid Das C -2 which means two per column next right here we want to map over our calls so we're not going to do a separate call for you know call recordings or one for upcoming calls or stuff like that right here at the top we want to call const calls is equal to get calls which will'll call this function and return the exact type of call that we need to get and we can do the same thing for the calls message so no calls message is equal to a function call to to get no calls message now we can map over the calls only if they exist so if calls exist and if calls. length is greater than zero then we can call the calls. map where we map over each individual meeting of a type call coming from stream video video SDK or call recording also coming from stream video SDK and for each one we can return something known as a meeting card this is a new component we'll create so we can reuse multiple cards so let's create it within the components folder called meeting card. TSX run rafc and let's import it right here also I didn't provide the ending statement right here so I have to say if calls don't exist then we can render an H1 that set that renders the no calls message and of course let's fix the indentation right here I am ending this one here I have to end another more and I have to import the meeting card yeah this is looking good to me there we go so it renders the card that's great so let's go into this card and this is the card we have seen on figma before looks like this for previous meetings and it looks like this for upcoming meetings it is a card similar to the one we have created for the homepage with two buttons images and some additional info so with that in mind I want to invite you to create it give it a shot pause this video open up figma try doing the card for upcoming meetings you don't even have to make it real data yet you can just use the fake title time and so on also create a version for previous meetings with conditional rendering of the buttons so you can dynamically show or hide them depending on the meeting type and also do it for meeting recordings pause this video right now and let me know how it goes after that I'll provide you with a code good luck so how did it go well let me paste the code right here for the meeting card it's about 90 lines long and it starts off just as a section covering an article which is basically a div where it shows the image for the upcoming meetings then we map over the images of avatars I should have mentioned that this is coming from the images within the public folder and then finally if it's not a previous meeting we also show some buttons like the one to watch the recording or to copy the link and for these Avatar images they're coming from constants so we have to actually create them so let's go to constant and let's export const Avatar images is equal to an array of SL imagesavatar d1.jpg and we can now duplicate this four more times do Avatar 2 3 4 and five and we're now properly importing them and mapping over them so now what we have to do is go back to the call list and pass all the necessary props to make her card shine and in case your code looks a bit different from this one you can find this complete code within the read me down below just go to the GitHub repo and find the provided code the most important thing you needed to learn here is that we can pass additional props into this meeting card props like the icon title date is previous meeting and more that then change the look and feel of the card depending on where that card is showing and that's true for any reusable components in the future so let's copy all of these props and let's add them to the meeting card of course I want to remove the commas and rather make it an equal sign like this so now let's start passing proper stuff to the meeting card first off as we're mapping over it it needs a key equal to we can do meeting question mark. ID and just so typescript doesn't complain we need to say that the meeting is of a type call so that it knows that the ID exists on it then we can pass the icon and don't worry the app right now doesn't even work and that's because we need to fill some data into these empty objects so instead for now I will just make it an empty string and that way it will at least work there we go we have a card at least which doesn't yet have the data but now as we start filling the data in it will be better and better and if there's nothing happening for you here in the upcoming you must go to home and schedule a meeting for the future so let's add a description something like let's do new JSM video live session and let's add it for let's say this Friday at noon and scheduled meeting now if we go all the way to our upcoming meetings you can see two different meeting cards let's modify the icon if the type is triple equal to the ended meeting then we can render for SL ions slr.svg but else if the type is triple equal to upcoming then we can do something like forward slash ions SL upcoming SVG and else if it's not upcoming or previous it will be icons recordings. SVG so now we have the icon both of these are upcoming next we have the title in this case we can say something like meeting. state. custom. description so this is the description we have added to the meeting and if it's a bit longer we can also just take the first 20 characters like do substring from 0 to 20 so we cut it a bit or we simply show no description there we go this looks good and it looks like typescript is complaining a bit the property state does not exist on type call or call recording this is interesting so right now it doesn't know whether the meeting here in this case is a call or call recording if it's a call it will have the state if it's not a call it won't have the state so we have to say meeting as call and then it will know it has access to the state next let's work with the description where we can say meeting. state. starts at. to Locale string like this and now we have the date or if it's a recording it will be a bit different it will simply be startor time. to locale string which we call as a function and this start time is also coming from the meaning now every time we would have to wrap this in either a call or a recording but in this case I will just ignore tab script for this entire file just so we don't have to type it out so at the top I'm going to add a comment of TS ignore or if you want to do it for the entire file it's simply no check I believe and there we go now it's good so now we have the date and we have the title and maybe we can do a bit more characters here so everything fits there we go this is great now let's check if we have access to the previous meeting so we can say if type is triple equal to ended then it is a previous meeting and it should not have any buttons right now that's not the case the button icon if type is triple equal to recordings will be equal to SL ions slpl that SVG so we can play the recording else it will be undefined right now we're not under recordings but we do have a button text and if the type is equal to recordings then it will be play else it will be start then we have the the link which if the type is triple equal to recordings then the link will be equal to meeting. URL and that's it else if it's not a recording we have to create a new meeting link so it will be a template string of process. env. nextt corpu basore URL to make it work for a deployed application as well for slash meeting slash and then we can say meeting. ID so we're crafting the entire link finally it looks like I have two button texts and I can remove the second one so now the button says start in the handle click we want to check if the type is triple equal to recordings and if it is then we want to create a callback function that will call the router. push and push to a template string of meeting. URL like this else we want to do another callback function where we call the router. push and we push to a template string of SL meting SL meeting. ID like this there we go so now we have a complete upcoming card let's reload the page one more time first we have the loading we can briefly see no upcoming calls right here why is that that's because it seems that at the start the calls that length doesn't exist so it renders it what we can do is we can say if is loading then we show the loader so return loader coming from that slash loader let's see if we have a proper loading now it loads it loads and then they show without flashing the no calls message that's great so now we have our upcoming cards which look great we can copy the link of a meeting immediately this works and we can start the meeting right of the bat which navigates us to the meeting setup page this is phenomenal but now what would happen if we navigate over to previous well right now not too much right so maybe to test it out we could schedule a new meeting call it let's have a meeting now and it will happen right this minute I will schedule it and I will join that meeting let's join it oh it looks like some of our avatars break that's not good so if we go to our constants it looks like I have Avatar 1 two 3 and so on oh I think only the first two are jpex and the rest are going to be pngs so that was my bad right here and of course this is right now hardcoded but of course if you want to you can just remove it or show anything else right here this is it for the previous nicely shows up in a grid right here and for the upcoming you can actually copy the link or start a meeting now the last thing we have right here are the recordings before I played a bit with the recordings and tried to get the meeting recorded it did kind of say that it was recording but I'm not sure if the recording was saved so let's go to recording page and let's also render the call list and let's pass type is equal to recordings and let's see what happens yep it says no recordings for now but of course it says that that's because these recordings right here are currently empty so what we have to do is some special logic to extract the recordings from each meeting so let's go right here below the get no calls message and let's create a use effect within which we we will fetch the recordings for each specific call it's a typical use effect where we look for the type and we look for the call recordings within here we want to create a function called const Fetch recordings is equal to an async function and of course we call it at the bottom only if type is triple equal to recordings we can call fetch recordings so now how do we fetch recordings for each different call well first let's get the access to actual meetings that these members were in by saying const C data is equal to await promise. all because we can fetch multiple things at the same time we want to get into the call recordings map and map out each meeting and for each one call the meeting. query recordings which will give us back the recordings for each one of our meetings next we want to extract those recordings by saying con recordings is equal to call data. filter for each call we want to ensure that the call. recordings do length is greater than zero so that it actually has any recordings attached to it and then we want to call something known as a flat map so what a flat map does is if you have an array with multiple arrays for example if each one of these arrays is a meeting and it has a recording one for that meeting it also has a recording two then you have another array within that array with recording three and so on what the flat map will do is it will go through all of the arrays and put them within the same single array so that it looks like this just give us back a list of recordings so here we can get all different calls and simply say return me all the recordings from each call flatten it out once we get the recordings we can set them to the state by saying set recordings is equal to recordings now if we save it it says use effect is not Define so we have to import it from react but after that is done we get two more errors or three more errors the first one is that we're trying to invoke a Constructor without the keyword new which is happening under promise. call so it looks like I misspelled it here it was supposed to be promise. all so try to get the recordings from all of these meetings at the same time if we do that and reload we get cannot read properties of custom for the description okay that's because the recordings don't have descriptions so if we go here the title will be meeting State custom description and right here we have to add a question mark dot so it doesn't break our app if it doesn't exist we also have to do the same thing for start at at line 96 so meeting. State question mark. starts at and there we go we have two different meetings and instead of simply saying no description we can add another or right here and say meeting. file name. substring and get the first 20 characters so this will give us the ID or the name of that recording this is great and we can also try to play that recording which will navigate to the actual recording of that meeting which is pretty crazy although right now it's an empty screen we'll try to make it work later on so let's see it there we go this one is working it shows us the entire screen where we had our camera and where I was testing different emojis that's great and we can also copy the link now sometimes you might might get an error saying too many requests happening at the same time for this promise that all so for that reason we can add a try and catch put all of this in the try and in the catch we can just add a toast right here by using the toast at the top so const toast is equal to use toast coming from UI toast and it has to be within curly braces and then if we cannot fetch it we can return a toast that says title try again later so now if you reload it looks good to me we don't get it now but as I said if you do get oh there we go we did get it try again later so this happens if you try to reload and try to fetch to minute recordings at the same time with that in mind our upcoming meetings are now done you can start them early or you can copy the link and send it to your friends then we have the previous meetings which you can just see right here and remember what happened and then we also have the recordings right here which looks like we tried to call too many of them but you saw that it was working before so there we go if you reload after some time you will will be able to get it and you can play them or even copy the link to the recording and share it with your friends and colleagues now with that in mind our app is looking better and better every second and what we can do next before we finalize our meeting page and the rest of these home cards is focus on the personal room this is similar to just creating a new instant meeting but instead it allows you to send all of your meeting details at all times with your own personal invite link so let's create that next to get started with creating our personal meaning room we can navigate over to the personal room page. TSX right here within our app rout home and then personal room right here below this H1 we can create a div that will have a class name equal to Flex w- full flex-all Gap of8 and on extra large devices Max DW of 900 pixels and right within it we want to start creating the layout you have seen not that long ago and for that we'll create our own custom functional component called table so we can say con stable is equal to a function that accepts the title and description as props and it can immediately return a div that will have an H1 within it that will render the title and right below it it will also render on H1 that will render the description of course we also have to Define the types of these props title will be a string and description will also be a string now right within this we can call our table component like this and we can pass it a title equal to topic and we can pass it a description equal to it will be a template string of user question mark. username we're going to add an S so their meeting room and of course we have to get access to the actual user and I think by this point you know how to do that right at the top we can say const user is equal to use user coming from clerk nextjs looking at the title right here it says that the topic is not assignable to type string let's see why that is I should have just said string like this there we go this is good and also since we're using a hook we have to make this a use client component there we go and now we have topic tests meeting room now we can style this a bit by giving this div a class name equal to flex flex-all items D start gap of two and on extra large devices Flex Dash row we can style the H1 by giving it a class name of text- Bas font Das medium text- K-1 on large devices text- XL and on extra large devices m-w of 32 we can also style the description by giving it a class name of truncate so we can cut it out if it's too long if we spell that properly truncate text Dash SM f-bold on Max SM devices Max Das W of 320 pixels and on large devices text- XL there we go so now we have a topic and we have the description in this case I'm logged in as a test user but we can easily fix that if I go here oh look at that nice animation coming from clerk that's nice and I can sign out now I logged in through my JSM account and we can check out with the personal room and we can see Js masteries meeting room we can also make the meeting room small so that it looks great with a username great now we can duplicate this table two more times one two the second time the title will be the meeting ID and in the description we can simply render the meeting ID meeting ID but the question is where are we going to get the meeting ID from well since this is our personal room the meeting ID will be equal to the user ID so we can say const meeting ID is equal to user question mark. ID and for tab script we can add an exclamation mark at the end the last table row will be simply an invite link and there we can render a meeting link once again where are we going to get the meeting link from well we can generate it and and I think we have already created this link before so meeting link yep here it is so we can copy it const meeting link coming from meeting type list and we can paste it right here but in this case it will be meeting SL meeting ID because it's a personal room and we're going to also add question mark personal is equal to true so we know that it's a personal room and now we can simply use that meeting link right here in the description and you can see how nicely we have created these three table rows now we also want to go below this div and create another div that will have a class name equal to flex gap of five and right within we want to render a shat CN button and we have to import it from components UI button we're going to also give it a class name equal to bg-- one and an onclick property equal to and here we're going to create a new function that will start a new meeting room so let's create it right here at the top const start room is equal to an async function and we'll implement the functionality a bit later for now we can simply call start room and right here we can say something like start meeting and we can have another button right below that will have a class name equal to BG D dark D3 and we can also give it an onclick equal to a callback function and here we want to copy something to clipboard and I think we have already wrote similar code so we can just search for navigator dot clipboard copy to clipboard there we go we used it in the meeting card so we can copy this Navigator and the toast and simply add it to this onclick and we're going to copy the meeting link this time and we also have to get the toast and the way that you get the toast is at the top by saying const toast is equal to use toast coming from components UI toast and we can say something like link copied and the button itself will say copy copy invitation there we go so this is our personal room if I click copy invitation we see link copied make sure to reload the page and then try it out and if I click Start meeting nothing happens yet that means that we have to develop this start room function but don't worry it will be very similar to the one we have already developed within the new meeting so we can go to the meeting type list and search for the function we have here yeah this will be good so we can simply copy this await call. get or create this is the only thing we need we can paste it here and we need to get access to the call which we can do by saying const destructure the call and that's equal to use get call by ID this is a special hook that we have created before so we have to import it and we're going to pass the the meeting ID as the parameter and also to start calls we need to have our video stream client coming from stream so we can say con client is equal to use stream video client like this and then we can check if there is no client or if there is no user we can simply exit out of the function but if we do have a client in the user we can create a new call by saying that's equal to client. call that's going to be a default call with the ID of meeting ID like so but if the call doesn't already exist so if there is no call in that case we can await this thing that we have here and create a new call like so so this can basically be within the if statement as well and in this case we don't need a descript deson and the starts at will simply be equal to New date. 2 ISO string like this so now we're either looking for an existing call or we're creating a completely new call that will act as our personal meeting room finally we have to navigate to that meeting room by getting access to the router which is equal to use router coming from next navigation and then at the bottom right here we can simply say router. push and want to push to forward slash meeting forward slash we want to make this a dynamic string like this then it's going to be meeting ID question mark personal is equal to true this will just allow us to know whether it's a personal meeting or not so with that in mind let's give it a spin I'm going to click Start meeting right here and we automatically get redirected to a new URL with our own custom user ID and at the end it says personal is true so now we can join that meeting this is looking good and we can share this link with other people that can immediately join our meeting as well great so now let's exit out of it and if I exited it looks like I wasn't properly redirected so we have to go back back to our meeting page or meeting room and here to call controls we have to pass a function that will allow us to leave the room and we can do that by saying on leave is equal to a callback function where we need to call the router so let's initialize the router at the top by saying const router is equal to use router coming from next navigation and then we can say router push and we want to push to forward slash which is the homepage so now if we go back to Local Host 3000 let's admire all of this in its full screen Glory we can go to upcoming previous oh previous fails right here this is good because our personal meeting didn't have a description so I'm glad we caught this we can go to our previous page and then to the co list and it looks like it's pointing to line 80 so right here under line 80 there we go I think we need to add another question mark dot right here for the description and then yet another question mark right here for the substring or maybe it's this second substring right here so I'm going to add two more question marks right here so it can say no description there we go no description and now we know when this no description happens it happens when it's a personal meeting so we can simply say that personal meeting there we go that's great so as I was saying we have the beautiful homepage upcoming previous recordings and now we also have this great looking personal room where we can copy the invitation send it over to anyone so that they can join and then once they do we can iMed immediately start a meeting we can turn the camera off join and we can end the meeting right here and it will redirect us back to home this is amazing now I have showed you all of the functionalities from the left sidebar but let's not forget there's also the start and instant meeting very similar to personal room schedule meeting where we can schedule it for a time in the future view recordings which for now doesn't do anything and join meeting which also doesn't do anything so these are the two last missing pieces of the puzzle so do you know where they are well let's go to our home page and meeting type list right here at the bottom we are creating different meeting models if we are scheduling the meeting and if we are creating an instant meeting but we are not doing anything on view recordings or join meeting so let's find this home card for view recordings that one will be pretty simple it is right here view recordings and on handle click instead of setting the meeting State we're going to Simply call the router. push and we're going to push to forward slash recordings that's it pretty simple right because we already have this recordings page so this will just act as a quick link to it but what about join meeting we want to open up some kind of a model there right so let's do just that right at the bottom of this page we can create a new meeting model we can definitely copy this one to give us some Head Start let's indent it properly and this one will open if is joining me meeting for the title it can say something like type the link here class name text Center and button text join meeting and the handle click will be a callback function of router. push to values. link there we go so now it says type the link here if you click it but we don't yet have an input where to type it in and we don't know where this button would lead right so let's expand it so that it accepts children and let's render a shat CN input field I don't think we have installed inputs yet but don't worry it's pretty simple a shatan input is just a simple form input that looks like an input field like this so let's install it by copying the command and pasting it right here and and we can also copy or no we don't have to copy anything you simply call it like this so let's call an input import it from UI input let's give it a placeholder equal to meeting link now if we go back and click join meeting we can see the meeting link let's also give it a class name equal to border Das none BG dark 3 Focus D visible ring0 and focus Das visible ring offset zero as we discussed this will remove that glow that happens once you click on it and we have to give it an onchange where we get an event and we set values where we destructure all the previous values and we set the link to be equal to e. target. value why is this important because we're using that link right here when we push to that meeting so now let's say that somebody else shared with you their personal meeting link the other person can now go here to their homage click join meeting and click join meeting oh but it looks like nothing happens so if we go back we're passing this router. push to values link but let's see whether the meeting model is doing something with that handle click so handle click is being used right here and the onclick button has the handle click maybe we have to prepend it with HTTP colon forward SL SL for local host and click join meeting and there we go now it works the reason why I didn't work right off the bat is because we're still on Local Host so we need that HTTP but all of the future domains where we deploy this project will have that prepended so I think it's going to work right off the bat even for personal meetings now with that in mind all of this is looking great and finally Let's test out live functionalities with a couple of different users and here we are we have our own Central screen right here Devon doe who is sharing his microphone we can see the audio right here we can modify our devices we can give some emojis to other people as well we can share our screen too which is pretty cool you can choose to share a specific tab there we go and now you are presenting your screen with the video still on that's great so we can stop the screen sharing you can see how here you can see different people as well we can try to record some stuff as well and it looks like it's just spinning around but it is possible that it's recording in the background we'll see later on if the recording actually shows up under our recordings now another cool thing is that you can of course modify your camera and you can change the layout so here we have a grid layout so if you had like 10 or 20 people this app would be able to support it all we can have the speaker on the left side always enlarged and we can also have the speaker on the right while all of the other people are on the left not to mention that we can choose a specific user to pin in case you want to see their screen in more detail there we go the recording sign now looks good which is always great we can have different profile icons from different people right here see the people's connectivity status microphone and even more we can see the live stats of our call with the latency region Jitter and even more which is phenomenal and at the end of the day we can also expand the participants list to see exactly which users are in and we can even modify some of the stuff related to them that is great with that in mind you have now successfully completed this video so here's a little thumbs up and we can now focus on getting this project deployed so let's do that next to deploy our project you can head over to GitHub and then create a new repository let's call it something like Zoom clone I'm going to make it public and create a repo next we can follow the steps right here by opening up our terminal removing the second terminal and stopping the original one from running then we can run get in it get add dot git commit dasm first commit git Branch DM main get remote at origin and get push you origin Master that's the only thing we have to do to get our code up on GitHub next make sure to go to your env. looc and copy all of the variables from here next head over to versel you might not have this many projects but I have quite a couple most of these projects are belonging to either our ultimate nextjs course or to different teams and cohorts of our JSM Master Class I talked a bit about the ultimate next course but I haven't mentioned the master class so far right now there's not a lot of info on the homepage there might be at the time you're watching this video but we have recently made significant improvements to the master class which if I had to summarize it is jsm's project-based and career focused boot camp that helps you position yourself as an expert developer so if you're interested put your name and email right here now going back to the deployment of our project let's click add new project find one from your GitHub and click import then go to environment variables and add your envs right here for now let's click deploy there we go the deployment has started custom environment variable applied and the app is building soon enough it will start installing the dependencies and I'll be right back and there we go our project has been deployed so let's continue to dashboard and it looks like the domain name has changed a bit so we might want to modify our next public domain environment variable so let's go ahead and copy the domain name from here head over to settings environment variables and then find your next public base URL and edit it to be this new domain name that was given to you by versel we need is to match our app exactly save it and then let's go back to deployments and let's just redeploy the latest push so we're sure that the new environment variable will be applied let's give it about a minute and we should be able to see our app live and we are live let's go back to our project and let's click visit there we go we are immediately greeted by this beautiful clerk signup page so let's sign in or sign up with Google and we are back on our homepage we can of course modify everything that has to do with our account and we can schedule a meeting for future we can join a meeting or we can create a new meeting not to mention all of our meetings from before like upcoming previous recordings and more are right here so let's go ahead and start a new meeting there we go our meeting has been created and once again we are asked whether want to give permissions to camera in this case I will join with mik and camera off and we also ask for microphone permissions which we can allow now let's try to just copy this link right here and share it to another person and there we go looks like they're in as you know we can modify the grid layout microphone camera and every single little detail of our application so let's end the call and as you know we build this using clerk for simple but robust user management and we have used streams video and audio apis and sdks for really an unparalleled video streaming experience and with that in mind you now know how to create these phenomenal video experiences and Implement them within any app you can imagine so if you're really watching at this point and if you came to the end of this video I really do know that you would be the perfect fit for the ultimate nextjs course this video contained a lot of nextjs stuff but still there's only as much we can cover in this YouTube video in the course we go into much more depth into optimizations SEO server side rendering and deep dive with a lot of different animated graphics to really explain how NEX GS Works behind the scenes and then you go ahead and build and deploy a 20 plus Hour project which which is our Flagship modern stack Overflow clone application that looks something like this with server side rendering all around search filtering AI code generation tags recommended content profiles and even more this is the perfect project for you to become an expert in xgs and Lead all of these companies future developments with that in mind thank you so much for watching this video and coming to the end of this amazing project and I'll see you in the next one have a wonderful day