In this chapter, our goal is to implement custom playlists. That's going to look something like this. On the personal section, we're going to have an option to load all playlists, and in here, we will load our custom playlists. We're going to display the number of videos and we're going to have a nice UI indicating that there's multiple videos here. We're also going to have a add button which will allow us to create a new playlist.
That's all we're gonna do in this chapter. We're not gonna work on loading the individual videos nor are we gonna work on adding videos to the playlist. That will be for a separate chapter because we do have some UI components to build so I don't think this chapter will be that short. Let's start by creating the playlist schema since that is the new thing we have to do. So I'm gonna go ahead inside of my database schema.ts and well, anywhere Let's go ahead and create the playlist schema using pgtable.
Playlists here, let's add an id, primary key and default random. Right here, after that let's give each playlist a name which will simply be required. My apologies for moving the screen. And let's give each one an optional description. We are not really going to use this field, but let me add it for you in case you might want to explore something with it.
Each playlist will be created by a user, so we are going to need an individual user ID, which will be a reference to user's ID. And let's add on the lead cascade here, and it will be required. And then let's just add the two of our timestamps, which we always have, Like this. There we go. So this is our playlist.
And now what we have to do is we have to go ahead and potentially create... I'm not sure why I described it as potentially create. Basically, what we have to do is we have to create a table, which is going to connect each video with a playlist. Because a video can belong to as many playlists as we want them to. So adding optional playlist ID to our video field, for example, if we were to go ahead and just add playlist ID the same way we have user ID and just make it optional, well yeah, that could work but then one video can only belong to one playlist.
That's not the kind of relation we're trying to build here. So what we have to do is we have to create a new table called playlist videos. Let's go ahead and add playlist videos here. And now we're going to have a playlist id which is uuid playlist id with references the playlist schema. This is playlists.
Make sure you do it like this. Playlists ID and onDelete, my apologies, let's add onDelete, cascade and of course not null. Besides the playlist ID, each of our playlist videos record will have a video ID, which will reference the videos. So video ID and the videos schema and required as well. And we are also going to have these two timestamps right here.
If you want to, you can also add the position like this and make it required. But I am 99% sure that in my original source code, I have never used this. So this position is if you want to implement ordering of the videos in the playlist, which is not something I'm going to do. I will order them by the time they have been added. Right, so that's going to be the way I will do it.
So I will remove the position but just to give you an idea that's something you can explore on your own later. Now we have to create a composite primary key here. So let's get the table and let's return an array with the primary key here. Name will be playlist videos primary key and columns here will be table playlist ID and table video ID, basically a combination of those two. This is the ID like that.
There we go. Make sure both of them are required and make sure that when one of them is deleted, they cascade, meaning we can no longer sustain this record. There we go. Oh, now I'm noticing that we have a warning. Deprecated.
Please use primary key columns instead of this function. Okay, let me just pause the video a little bit and research the documentation so I can see if this is something we can refactor quickly. All right, false alarm, I don't understand JS doc. This is a reference, right? So if you try using primary key like this you will have a crossed line and when you hover over it it will tell you that it's deprecated and to use the primary key like this, right?
So I confirmed by going inside of answer overflow here where someone asked a question and they told them to use it like this, which is exactly the way we used it. So this was four months ago, and this is the new approach. And I can also confirm in the documentation, they told you to use the primary key like this as well. Basically, the message for deprecation is probably for the syntax, the way it was previously used, right? Now it's accepting this.
Okay. I was just worried that I was teaching you the wrong syntax the entire time. Great. We now have the playlist videos here. And now what we have to do is, I believe, push all of these changes.
So let's go ahead and do that. I'm going to go ahead and I will shut down my studio and do Bonnex Drizzle Kit Push. Let's wait a few seconds for this to update and then we're going to open our studio and take a look at what we have here. So it's pulling schema from the database. We've added two things, so changes applied.
Great. Now let's go ahead and do Studio. Let's see what we have. So we should now have the playlists. If you go here, you should have the ID, Playlists, Video ID, Playlist ID.
We also have an otherwise nice user relation right and in the playlist videos we have the playlist ID the video ID and the playlist and the video react connection, right? And if I go inside of my videos here, I should now also have the following relation. Let's just find it at the end. There we go. ID, playlist, videos, video ID.
Great. So the relations are set up, but on application level this is a little bit ugly. So let's go ahead and export const playlist oops playlist video relations and let's add relations playlist videos and let's destructure one from here let's return one playlist one playlists yields playlists Playlist Videos, Playlist ID and References to Playlist ID. Like this. And then just copy this one and add one video.
One videos, targeting video ID, videos.id and fix this. There we go. So Those are our relations for the playlist video relations. And now we have to do the playlist relations as well. So let me just copy this entire thing here so I don't have to type it twice.
And instead of playlist video relation, these are playlist relations like this and make sure that they actually target the playlists. And we're gonna have both one and many in this case. So inside of here, I'm going to change to have one user. So user1 belongs to users and uses playlists userid and references to users id. And we're going to have many playlist videos.
So I'm going to call this many playlist videos like this and there is no configuration for this just this many playlist videos. Now we have to find the user and we have to tell the user that they can have many playlists. So let's add playlists, many playlists. There we go. So let's go ahead and shut down the studio and start it again and do a hard refresh to see if everything on application level is now improved.
Let's start with playlists themselves. So now there we go. We have playlist videos written here. I purposely didn't want to name them videos because they're not exactly a video relation, right? They are a special table which simply holds the ID of the playlist and the ID of the video, right?
And in here, playlist videos, we should also have the nice playlist and video here. So those two seem to be fixed, no longer that weird ID. Now, if we go inside of users, Users now have very nice playlists here. Perfect. And if we go inside of videos, let's see what's going on here.
I think that maybe we didn't fix this one. Let's wait. There we go. Here we can still see this. Again, not important for our development at all.
All queries will still work, but if you want to have a nicer way here, you can find the videos schema and simply tell it that it can have many playlist videos. Many playlist videos like this. I think this would be you know the correct notation to use. I know that it makes more sense to just say playlists, but then again, this is not a direct relation to the playlist, but then again, neither are the views. Yeah, I don't know.
I would kind of keep it like this, simply so we know that, hey, this is a different type of relation. It's not directly a relation to an individual playlist. There's a table in between those two. There's a connection table. So that's why I'm gonna call it.
I mean, this ritually doesn't matter. We have established our foreign keys. They are what's important. The rest is not. I mean, not for the type of querying we are doing anyway.
All right, I think that should be enough. As always, we can do one security push just to confirm that our relations changes didn't change anything, but I'm pretty confident that this will just print out no changes detected. Let's wait a few seconds, no changes detected. Perfect. So now what we ought to do is go ahead and create the all playlists page right here.
So I want to start there. Let's go inside of source, app folder, home, playlists, and let's create a page.dsx and let's return the page. Like this. And a div, playlists, just like that. If I refresh, now it should work, because inside of my personal section, that goes to slash playlists.
And that's exactly where I have created my new playlists page.tsx. So let's go ahead and create the playlist view which is the next step anyway. Instead of modules, we already have playlists. Now let's create a new view here, which I'm going to call PlaylistsView.tsx. And let me copy the history view, the liked view or the history one, whichever.
And let me go ahead and change this to playlists view. And this will simply say playlists. And the text will be playlists you have created. Or maybe better collections you have created so it doesn't repeat the word playlists. I think that looks better.
And let's change the maximum width here to be 2400 pixels because we will be displaying things in a grid here instead of our normal sections here. So what you can do now is the following. You can remove the liked videos section. We're not going to immediately use this anyway. And you can go ahead and wrap this in a div one more time.
And give this a class name of FlexJustifyBetween and item center, like this. And then add a button here from components UI button and give this a variant of outline, a size of icon, a class name of rounded full, and an on click for now to be an empty arrow function, which means that we are going to have to mark this into useClient, which is not something we've done before. Usually we only do useClient in the section, but it's okay because we won't be doing any server-side fetching here. So just make sure you add a use client here if you will add this here and add a plus icon from Lucid React. I'm now gonna go back to my page.dsx here and I will simply add playlists view and I will already prepare the hydrate client from TRPC server here even though there is nothing to hydrate really and let's make this an asynchronous component in advance So now when I refresh we should see the text playlists and collections you have created and here we should see a big plus button and there should be a limit to how wide this component can go all right so now that we have this let's go ahead and let's create the playlist procedure to actually create a playlist.
So I'm going to close everything and I'm going to go inside of my modules, inside of playlists, specifically server procedures. And I will add the create method which will be a protected procedure and I will add an input which will be z.object name z.string and the minimum value of 1 And let's add a mutation which will be an asynchronous method. From here, I will destructure the input and the context. From the input, I can destructure the name and from the context, I can destructure the user ID like this. Let's go ahead and let's simply create a created playlist here to be await database insert into playlists which we have to import from our database schema here and let's set the values to have the user ID and the name and of course returning.
In case there are no created playlists we are going to throw new trpc error here with a code bad request something happened make sure you have imported the trpc error from trpc server and finally let's return the playlist create the created playlist This is our procedure to create a playlist now. So let's go ahead inside of our new playlists view. And in here, we have prepared a button to obviously create a new playlist. And we're going to do that by building a playlist create model. So what I want to do is I want to copy one existing model that we have, which I think I can find instead of a studio UI components.
And we have the thumbnail generate model. So let's go ahead and use that. I'm going to copy the entire thing and I'm going to go inside of my playlist UI and I will create the components folder and I will paste the entire thing inside and rename it to playlist create model. I will then make sure that I am inside this new component and I will rename my props from thumbnail generate to playlist create model. There we go.
My form schema will be changed to name and use a Z string of minimum of one and the video ID actually won't matter at all in this case. I can remove it. I can remove it from here as well and this will be set to name like this. And instead of having generate thumbnail we will have a create method which will call playlists and create. And instead of Toast saying background job started, we'll simply say playlist created.
And no need for any further clarification about what happened. And let's go ahead and ensure that we call on open change false here and basically yeah this doesn't matter this was fine as it was and on submit we're going to call create mutate and simply pass in the values nothing else will be needed here The responsive model will say create a playlist and inside of here we will change the name and we will be using a very simple input component from components UI input So no need for this class name nor the calls and rows. And let's add an example, my favorite videos for example, right? Make sure you have imported the input and remove the text area since we are no longer using it. And I think this should be it.
So this will be create is pending and this will be create. That's it. That is the entire component. And we can now go back into our page view, my apologies, playlists view, and let's go ahead and render playlist create model component. And now we have to create the states, so let's go ahead and quickly do that using use state.
So import use state from React and add create model open and set create model. And inside of here, add the appropriate create model open and on open change to be set create model open like this. And then on click is very simply going to call set create model open back to true like this. There we go. I'm now going to prepare my Bonnex Drizzle Kit Studio here and what I should now have or more specifically maybe not have, oh I somehow opened this changes, Okay, in my playlists they are completely empty.
I have no playlists at all. So I'm gonna go ahead and try and click on the plus button, create my music and let's see. Playlist created and if I go inside of my studio, this one and refresh, I expect to have a playlist and I do with the name of my music, no description, have a user ID. We have no playlist videos and we do have a user who created this. Excellent, exactly what we want.
So that part is done. Now we have to go ahead and render them in this very cool way. Let's start by building the GetMany procedure. So I'm gonna go back inside of my source, modules, playlists, server, procedures, and I will copy one of the existing ones. For example, get history, and we're going to do our best to modify it to load playlists, right?
So just copy either get liked or get history, one of those, and I will add it here to the top for now since it's my newest one, so it's easy to keep track. And let's call this GetMany. So it will definitely be a protected procedure. One thing that will be a little bit different is that it's gonna bring back the updated path for the cursor. Now inside of here we will have the user id because this will always be a protected procedure but we actually won't be needing to connect any of the commentable expressions So we can remove this part as well.
And inside of here, here's what we are going to do. We don't need the user who owns the playlist. We don't need viewed at. We don't need a view count, nor the like count, nor do we need the dislike count, and we are not even going to spread videos inside because we will be loading playlists, Right? We have this imported from the schema.
So inside of get many, make sure that you are spreading the columns of the playlist table and you can add video count. For example, how many videos are inside? And we can just use good old count here using our playlist videos connecting table. Right, I'm sure there's a proper terminology for this kind of table but I can't remember what it's called so I'm gonna keep using the connecting table. Playlist video cannot find name.
Okay, of course, I added this here. Playlist videos equals and let's add playlists id, Playlist Videos, Playlist ID. There we go. So this way we can count how many of these connecting rows we have and that is ultimately going to be the video count. So technically this could be playlist video count, but since it's coming to the front end, I want to make it as clear as possible what I'm trying to do here.
Great, so that's actually the only thing for now we can add here, but let's also add user just in case. Maybe we will need it, I don't know, We'll see. So instead of from videos it's going to be from playlists. Like this. We will inner join the user but no need to inner join viewer video views at all.
No need to do the equalization here. And now let's just modify the cursor but there will be one more thing before the cursor and that is that the playlist user ID we are loading matches the user ID that's logged in so we are only loading their playlists no one else's. Now let's fix the cursor. So we will be doing playlists, that's for sure, and we will be using updated at in all four places and then change this to Playlists ID. Make sure you don't accidentally use videos here, right?
And then order by Playlists, Updated At, and Descending Playlists ID. There we go. And inside of here, change both of these to updated app. That's it. That is our procedure to load all playlists from a user, including the video count to tell the front end how many videos are inside.
Excellent. So now let's go ahead and let's go inside of our app folder, home, playlists page right here, and we are going to prefetch that using void trpc from the server. Let's get the playlists and let's get getMany.refetchInfinite and pass in the limit to be the default limit. There we go. We now have this.
We can now go inside of the Playlists view. Let me just separate these components like that. And now inside of here, we will finally have to create the Playlists section component. So in order to create this, I would like to copy something from my playlists, for example, the history videos section. And let's rename it to playlists section.
And I let me remove the copy. There we go. So now this is called the playlists section. What I'm going to do is replace all instances of history videos with playlists section. Oh, the section already exists, so just playlists.
There we go. Playlist, section, playlist, section, playlist, section, here as well and here. Perfect. And inside of here we will be using get many. That's it.
And set the default limit. So already errors should be popping here because this data is not compatible with the current components that we have. So what I'm going to do is I'm going to delete this one and I will remove this completely. And I will very simply just do JSON stringify of our videos, which are technically playlists. So we should rename them and stringify the playlists.
And we can remove video grid card from here and we can remove video row card. It's fine to leave the skeleton for now. We are going to change it later anyway. And now let's go inside of the playlist view and let's add the playlist section here like this. And now if I refresh, I'm getting an error.
Let's see what exactly is going on. Missing from clause entry for table videos. I think I know what's going on. I'm using videos somewhere here. Let's see.
Inside of my get many procedure in the Playlist Router. I think that somewhere here I am using video. Maybe that, at least that's what the error seems to be telling me, but Let me double check. And it's right here. When I join the users, I'm joining on the video schema, which is in no way used here.
So it's breaking the query. So double check that you're using Playlist throughout this entire query here. And now if I refresh, perfect, we get JSON stringified data. Exactly what we need. So we can now focus exclusively on the playlist section.
But now we have to go ahead and build our own components. Let's go ahead and go inside of the components and let's create a new folder called Playlist Grid Card. Let's go ahead and create an index TSX file. And while we are here, let's go inside of playlists and let's create a types.ts and inside of here let's import infer router outputs from TRPC server. Let's import app router type from at TRPC routers underscore app.
And let's export type playlist, whoops, export type playlists, actually playlist get many output. And let's make it the infer router outputs at router, playlists get many. There we go. Now Let's focus back on the playlist grid card index here and let's create the type. Interface playlist grid card props will have a data of playlist get many output items and a random item in the array.
So that's going to be the type of data we are expecting here. And let's export const playlist grid card. Let's go ahead and assign this props right here. We can then restructure the data. In here, we are going to return a link component, which we can import from next link.
Just make sure you do that. We are going to add an href here to slash playlists data ID there we go and now let's create a div with a class name here flex flex column gap to all width and group so for now that will be it And you can just render data, whoops, data.name maybe. And let's add this to our playlist section right here so we can render these properly. So what we're going to do is we're going to go over our playlists, pages, let's do a flat map. From each page, get all the items and then iterate over each playlist and simply render a playlist grid card component.
You can get it from dot dot slash components playlist Grid Card And let's go ahead and pass in the data to be Individual Playlist And the key to be Playlist ID Like this So now in here you should see my music. And if I create a new one, another one, it shouldn't revalidate just yet. So you do a hard refresh, and then you will see a new one. The reason it doesn't revalidate is because we didn't make it revalidate. Let's go and quickly do that.
Instead of Playlist.CreateModelComponent, once we do this, why don't we just revalidate the list? So let me go ahead and add the utils here. Const utils trpc use utils and let's do utils playlists get many dot invalidate like this. So now if you refresh each time you create a new playlist, it's going to be revalidated immediately. Excellent.
Let's continue working inside of the playlist grid card index here. And let's create the fun part. How about we create the playlist thumbnail? The playlist thumbnail is going to accept an image URL which for now will be our fallback thumbnail URL. I have this somewhere I'm just not sure where.
Let me see. Fallback. Let's use the caps lock. Thumbnail fallback from modules videos constants. Yes, I guess it can come from there.
Okay. Thumbnail fallback from modules, videos, constants. So let's add this. Then let's set the title to be data name and video count. Let's make that data video count.
So we should have all the information and now let's go ahead and actually build the playlist of thumbnails so we're going to do that inside of the playlist grid card here because it's only going to be used inside of here so playlist dash thumbnail dsx up here. So playlist-thumbnail.tsx. Let's go ahead and create the interface here. So we're actually not gonna need the aspect ratio. That will not be used anywhere.
And this will be image URL. The rest is okay. Let's export const playlist thumbnail. Let's assign the props. Playlist thumbnail props.
And let's go ahead and simply render out all the destructured items we need and now it's time for us to make this look good so let's start by adding a div thumbnail let's render the playlist thumbnail So to confirm that our types are correct, like this, I have an error. That's because I'm never returning this. So now we should just see thumbnail, three instances of thumbnail. That's what we should see. Let's go ahead and build this.
We're going to start by giving this a class name, CN from Libutils. It's gonna be dynamic. And we're going to define this by a relative, padding top of three and group, and then an optional class name if we ever want to pass something from the outside. Now we're going to create an effect of stack effect layers. That's what we're trying to do now.
The stacking effect, which I have screenshotted here. These stacks right here. So we're gonna try and do that by first adding a div and making it all relative. Then inside, let's add our background layers. So these are gonna be self-closing vids.
And this one will have a following class name. Absolute minus top three, left one and a half, minus translate, x one and a half, width of 97%, Overflow hidden, rounded, extra large, background black with 20% opacity, and we are going to have aspect video here. So there we go. We can now see this. That's fine.
Yeah, how about they do this? I just want to quickly go back to my playlist section for one reason. So we can add a proper grid here because it's gonna be easier to see what we're doing. And we can copy the grid from, I believe we have the trending feed, trending view, trending video section. We can copy the grid from here.
It's going to be exactly like this one. Trending videos section. Basically find that, go inside of playlist section and simply use this. Not only is the playlist section going to have to use the same grid, but the playlist view also uses the same maximum width. Make sure you did that.
If I go inside of the trending view, you will see that they have the exact same thing. So now, there we go. You should see the three of them stacked like this. So let's go back instead of the playlist grid card, instead of the playlist thumbnail and let's continue developing. So now that's the first layer.
Now let's copy this and let's create the second layer. This one will be a little bit more visible. So using 25% like that. And we're going to position it minus top 1.5. The left will still be the same.
The width will be a little bit bigger, 98.5. The rest can stay the same, like this. And now what we have to do is create a main image here. So let's add main image. And this will have a div with a class name, relative overflow hidden, full width, rounded extra large and aspect video, and render an image component inside using next image.
So let me just add this. There we go. This next image will have a source of image URL. Let me just see. Did I extract my apologies image URL?
Or it's going to fall back to thumbnail fallback like this. Let me just separate that. And it's going to have an out of title. It will have a class name of full width, full height and object cover and a fill property like this. So that's going to be our main image.
Let me just refresh to see. There we go. So I think that is pretty much what we saw here. Great. And now what I want to do is I want to create a hover effect on this and I also want to add the number of videos inside of each of these lists right here.
Let's start with the hover overlay. For the hover overlay, we will open a new div with a class name here, absolute inset zero, background black 70 percent. Opacity is going to be zero by default, so this is not visible, right? But then when we hover over the group, opacity goes to 100. In order to properly be able to do this we must ensure that our most outer div has the group part.
So when you hover there we go it's going to be like this. And then let's add transition, opacity, flex item center and justify center. Like this. Inside of this, another div with a class name, flex items center and gap X of two. And let's set a play icon from Lucid React and a class name size4-text-white and fill white.
Make sure you have added the icon here. Just below the play icon we're going to render a span and text play all. And let's give the span a class name text white and font medium. If we've done this correctly when I hover we should have a nice effect. Play all, perfect.
Now, let's go ahead to the end of this and let's make our video count indicator here. Using a div, giving it a class name, absolute bottom two, right two, px1, py 0.5, rounded, background black with 80% opacity, text inside is going to be white and extremely small, font will be medium, flex, items center and gap x1 between the two elements which will be inside, which One of is a list video icon with a class name of size or and video count videos. And just make sure you have imported this. There we go. Now this says zero videos right here.
Perfect. So this is how it looks like. I just want to double check that my grid section is correct. Well, actually, since we copied it from the trending page, it should be exactly the same. Three videos fit in the exact same place, so this is consistent.
So I don't even care if it wasn't what I initially wanted, but as long as it's consistent, it looks good. We are not yet done inside of the playlist index here. We are done with the playlist thumbnail, so this is fine. But inside of here, we still have to create the playlist info component, which is not going to be difficult. Let's just go ahead and pass in the data and let's create this Uline component.
Playlist data, my apologies, playlist info. Let's go ahead and create an interface, playlist info props, playlist get many output, which we can import. No need to use the modules because we can just do this. Back, back, back, back. And we should be right here.
There we go. And let's go ahead and export const playlist info, assign playlist info props, destructure the data, return a div and we are just going to render a few things, just some information here let's give this a flex and a gap of 3, a div with a class name, minimum width of 0 and flex 1 and this an h3 element data name let's give this h3 element font medium line clamp one lg line clamp lg line clamp to text small and break words. Below this, a paragraph, playlist and the class name, text small and text muted foreground. Below this, another paragraph saying view full playlist. Besides having text small and text muted foreground, it's also going to have font semi-bold and hover text primary like this.
Now let's go ahead back inside of the index and import the playlist info from .slash playlist info. Let's refresh. And there we go. We now have a much nicer view here. I just want to confirm one thing.
You can see that I have wrapped my entire component here inside of a group. So when I hover over anywhere the play all will be triggered which means that inside of my playlist thumbnail I don't need the group here. If you want you could leave it if you plan on using the playlist thumbnail independently. But in my case, the group hover will be on this group hover, right? Whoops, this one, this group.
So even if I hover over the info, it will show the play all thing. And we also have this effect right here. One thing that I want to do is just get the compact views example. For example, in my video, I'll just copy this. I'm going to go back inside of the playlist thumbnail, and I'm just going to go ahead and do that.
Compact views. So just in case, if we happen to have a few thousand videos, which is normal for a playlist, honestly, Let's find a nice way to render that. So import use memo from react and simply use the view count or video count in this case. And then just render compact views here. So you should still say zero videos here.
Great, so now what we have to do to wrap up this part is just do the skeletons. Let's start with the playlist info because this one is relatively simple. Export const playlist info skeleton and let's return a div with a class name flex gap3 an inner div with a class name minimum width of 0 flex 1 space y 2 and inside of here a skeleton component from components UI skeleton Give this one a class name of height 5 and width of 90% and below that 70% and below that 50%. Make sure you have imported the skeleton component here. Now let's go ahead and do the same thing for our playlist thumbnail.
This one will be even simpler. Since we're not going to do the stacking effect for a skeleton. All this is going to be is a class. Actually, we can save us some time. Go inside of video thumbnail and just copy the video thumbnail skeleton.
It's exactly the same. Just make sure you import the skeleton here. From components UI skeleton. Like this. Let me just double check with my code.
Yes, it's exactly the same. Make sure you have aspect video relative, actually. Not sure if we need it, but yeah, it's okay. This is fine. And now finally, we can go inside of the index and let's create export const playlist grid card skeleton and all we're going to do is return a div with a class name we can copy from here flex basically this no need for group And render playlist thumbnail skeleton and render playlist info skeleton from these two respective components.
There we go. Now we have the playlist grid card skeleton and we can now go ahead and create the proper skeleton here. To create the proper skeleton, let's save us some time by going inside of the trending videos section from where we stole the grid. And now let's steal the entire skeleton here. So I will just steal the skeleton like this.
And instead of importing video grid card skeleton, we will import playlist grid card skeleton. This is the one I will import. You can just join this a little bit like this. Rename this of course to PlaylistSectionSkeleton and use it here and remove the imports from videos UI because now we have the proper skeletons here. If I refresh, there we go.
Let's try another one here And this one should appear first one here. Excellent! Amazing, amazing job! All of this is working just fine. Let me just try.
I'm barely testing my infinite load, right? But you can see how that works. If I do a hard refresh, initially I load just five and then auto loads the other ones until it reaches the end of the list. Amazing, so that is definitely finished now. I think this is all we wanted for this chapter.
In this chapter, we're going to learn how to add videos and how to load them inside, which is also going to be a cool query we're going to have to build because we're going to have to generate the thumbnail for each of these by the last video added inside. So that's going to be pretty cool. Great. Amazing, amazing job. You are so close to finishing this.
Great, great job.