The goal of this chapter is to create suggestions. So basically this part of our video page right here. So far, we've wrapped up the reactions, we've wrapped up the user info, the description, and the comments here. In order to build the suggestions section, we're going to have to build a few components, starting with the compact video roll card, which is this exact thing which I have screenshotted. That's going to be used for suggestions, for playlist items, and in general for small devices.
Besides having a compact variant, video roll card will also have a regular video roll card. This one is going to be quite larger and it will be used on search results and large devices. Besides that, we're also going to have a video grid card which is going to look like this. That's going to be used for feed grids but also it's going to be used as a fallback for small devices. Because once we go ahead and collapse this area underneath this one, because that's what's happening on mobile, it will be a better look for us to load this type of card.
So that's why we have to develop all of those in this chapter. And besides that, our goals are pretty similar to our previous chapters. We will create a suggestions procedure, which lucky for us, won't require any new schemas. We are simply going to use the data which we already have. We will prefetch our suggestions inside of our video ID page server component.
We are going to create the necessary components and finally we are going to connect the suggestions section with the new API. Let's go ahead and do that. First thing I'm gonna do is I'm going to visit my MOX assets. So I'm going inside of my YouTube development and checking out my assets. And I can see that some of them will be deleted soon.
So this is what I'm going to do. Ensure that you have Bun Run Dev all running, so your webhooks are connected, and I'm simply gonna go inside of each of these and delete them. Once I delete the first one, you can see that I now have this deleting video webhook here, and I'm gonna do the same thing for this one. So basically, all of my assets will now be deleted. If I go inside of my Drizzle Studio and refresh the entire thing now, so I have Drizzle Studio running here as well, you will see that I have no comments, no comment reactions, no video reactions, no video views, no videos, meaning that it cascaded all of my records, which is exactly what I want to happen.
So now I'm gonna go ahead and go inside of my localhost 3000 here, and I'm going to upload a new video from scratch and we're going to use that to load our subscription. So let me just do another refresh here, go inside of my studio and let's go ahead and upload a video. The video I'm going to upload is going to be the same as it was in every previous chapter, just the demo video. And the only thing I'm going to do is, well, go to its link. I'm not going to assign any category.
That will be kind of important, right? So let me just go ahead and pause this video and this is what we are working on now, the suggestions. So what I want to do is the following. I'm going to go ahead and create a new module called suggestions simply because I want to keep that in a separate module. I don't want to mix it with videos or anything else.
I will create a server here and what I'm going to do is I'm going to copy the procedures from the studio. You're going to see why. So let's go ahead and copy the procedures from the studio and paste them here. Inside of here, I'm going to rename this to suggestions router and I will remove the get one procedure entirely like this. I just want to have the get many procedure.
Basically I'm saving some time since in here we've already developed this cursor limit. Basically we've developed the infinite pagination here so I don't want to write that whole thing again. Just ensure that everything is updated, working with updated at, simply because we're using the sending on updated at. And make sure that you have that in the cursor here. Basically, if it was working previously, it should still be working.
If you're unsure, you can go ahead and visit the comments procedures for GetMany. And inside of here, you should see a similar cursor, right? ID updated at the limits. And then inside of here, we're doing a very similar querying. I mean, not very similar, the exact same, but for different schema and ordered by the same things.
And this is the same logic. Great, so let's focus exclusively on the suggestions procedure here. And this is what we are going to do. First of all, This is not gonna be a protected procedure. It's gonna be a base procedure.
So anyone will be able to do this. The cursor ID, all of this will stay the same. But one thing that we're going to have is the video ID, which will be required. So that's going to be a string and the type of UUID. The reason we are going to need a video ID is to know suggestions for which video should we load.
Now we could go ahead and debate what is the best way to recommend suggestions for the video. There are many ways you can do it. You can use, I think the startup's name is Algolia. I think that one is used for AI suggestions and recommendations. You've probably seen their search in a lot of documentations.
I think that Drizzle uses them. Let me try and go inside of Drizzle here. If I, there we go, Algolia, right? So I think that is one startup you can explore, which might give you a much, much better suggestion algorithm. But the one we are going to build is gonna be quite simple, just to kind of get the idea of how we would build a suggestion algorithm.
Basically, what we're going to do is we're going to load similar categories and we're simply going to exclude the video that is currently loaded. But let's do what we can with the data we have. So besides the cursor and limit, we now have the video ID from here. And since this is no longer a protected procedure, we will not have this at all. And more so, in fact, we won't even need the context.
So the first thing we ought to do is find the existing video. So let's do that using await, database select from videos, and simply check if videos ID matches the video ID. Like this. In case there is no existing video, that means we cannot load the suggestion so let's throw new TRPC error here with the code of not found. Looks like I have to import TRPC error from TRPC server.
There we go. Make sure you have imported the TRPC error like this. Great! And after that we have to go ahead and find our videos. So let's go ahead and see how we're going to do that.
This will no longer be the case. Instead, what we will be focusing on is the following. We're going to focus on videos category ID being the same as existing video category ID. That will be our focus. And since it's possible that a video doesn't have a category, we're going to do the following.
If the video for which we are trying to load the suggestions has a category, then we're going to do this. Otherwise, we're simply going to load all other videos because we don't really have nothing smart to show for this. So I think that this should be enough for the logic. We are descending videos, updated at and video ID here. We are doing the limit to check for more data, all of this should be quite fine and that's how we are going to load our suggestions.
There is one more thing we're going to have to add here and that is to exclude the present video, but we're going to do that in a moment simply because I only have one video right now. So let's go ahead and go inside of our source and let's go inside of TRPC Routers app and let's add the suggestions router to be our suggestions router. The suggestions router will come from modules suggestions server procedures. Make sure you're using the suggestions module. Now let's go ahead and find our app folder, home videos, video ID page.
And besides prefetching comments and video, we are now going to prefetch. Oh, I've called it suggestions router. That's not what I wanted. Let me go inside of my app router and rename this from suggestions router to suggestions. There we go.
So this should be suggestions like this. And this will now be accessible through suggestions, get many, refetch infinite, video ID and limit is going to be the default limit. So we are now going to load five suggestions, basically five videos with the same category, or if no category, just five videos. Let's go inside of the video view. And now we have to go inside of our suggestions section.
We have two instances of suggestions so basically what that is doing is rendering one on mobile and rendering one on desktop. So let's go ahead and mark the suggestions section here as use client and inside of here what we are going to do is simply load the suggestions by using the PRPC from the client suggestions use suspense get many use suspense infinite query passing the video id which we don't have and we also have to pass in the limit which will be the default limit from the constants so Let me go ahead and just do an interface suggestions section props. I expect it to receive video ID, which is going to be a required string. Let me go ahead and add those props here and the structure the video ID. And now we can safely assign the video ID like this.
But besides passing this, we also have to pass the second argument. We can copy it from the comments section. Basically, it's the getNextParam. So let's add it here. There we go.
And now let's go back inside of the video view and we simply have to pass the video ID to both of our suggestion section components. There we go. And now we have the suggestions and now we can go ahead and JSON stringify these suggestions here. So we can see our data. And there we go.
Now that we have loaded this video right here, looks like there is something going on. This video is still being processed. I'm not sure why that is still the value here. Perhaps one of my Mux webhooks has failed. Maybe that happened.
This video is still being processed. So this is an idea, you know, of a video. Yes, it still says that this video is being prepared. So it may be a good idea to try and refresh this status manually, simply so we can always you know manually check if a webhook has failed, right? So if I go inside of my Result Studio here, inside of the video itself, it still says that this video is preparing, But if I go inside of my videos here, so I don't know if you have this case, but I'm just debugging what's happening to me right now This one says it is ready.
So that's interesting This one says it's ready recent events Looks like we got the asset ready I'm just unsure And the webhook also says that it was successful. And it was definitely hitting the correct webhook here. So I wonder if there was a conflict, maybe one occurred before another. So yeah, this is an edge case, right? And one way we could have handled this more gracefully would be using background jobs from Upstash, right?
So we could order them in a specific way. So here's what I'm going to do. I'm going to go inside of my assets here, and I'm going to delete this asset like this. So this should now also delete this entire video here. And I will just try and upload a new one, simply to confirm that whatever's happening is a rare thing and shouldn't occur again.
Let's see. So It's still preparing the video. It's preparing the video. And now it's ready. Yes, looks like that was an unfortunate edge case which happened.
So it always stayed in the preparing section. But that gave me an idea that we probably should add something in the studio that will allow us to maybe like from a drop-down let's try refreshing mux status something for us to able to manually check hey maybe this video is now ready right I will explore if that's possible all right so now let's go ahead and let's go inside of this video here. And in here, I have loaded the rest of my videos. Great. Now the issue is, we are going to need some more information from here because if you take a look at our suggestions here, we're going to need the number of views, the number of likes, oh maybe better to look here, the number of views, let me just go ahead and grab my pen, the number of views, likes and also the creator.
So we have to join all of those things. Let's start with adding the likes and views because that's the easy part. So we're gonna go ahead inside of our suggestions server procedures right here and we're gonna go ahead and modify the select here to include view count and view count will be database count video views from schema equals video views video id to match videos id that's it that's our view count now we need the like count it's going to be similar looking for video reactions from database schema. So just make sure you've added both of those. There we go.
Video reactions, video views and videos. But besides looking at one argument, we will be looking at multiple arguments. So let's go ahead and add and here. And besides matching the video ID, we also need to match the type to be like. And let's add another one here.
My apologies, we should add video reactions here and video reactions here. And this is this like count. And again, change this to video reactions. And this is this like. So there we go.
Now, when you hover over your data, you have view count, like count, and dislike count. And one thing we forgot to do is to spread the rest of the video information from videos. And you have to import get table columns from Drizzle ORM right here. And We also have to join the user. So let's go ahead and do an inner join here.
From users, you can import it from database schema equals video user ID matches users ID. There we go. So make sure you have imported the users from the database schema and now you should have the user here as well. So we can simply add it as user and let's make that the users schema. So now we have the user information, view count, like count, dislike count, and all other video information.
Perfect. So, we can now go ahead and focus on the suggestions here. If I refresh again, there we go. Much more information in here. View count one, like count zero.
If I like this video now and refresh, like count is one here as well now. Perfect. Even though this exact video should not be shown in the suggestions, but we are simply working with the data we have at the moment. Great. So let's go ahead now and let's try...
What should we do now? Well, we should develop the compact video raw card component, right? But the only way to develop that is by simultaneously building the compact one and the regular one. So let's go inside of our source, inside of our modules, and inside of videos. And inside of here, UI components, let's create video-raw-card.tsx like this.
And what I'm going to do is I'm going to import CVA and variant props, because we will have two variants. And for that, we will also need the CNUtil. Let's also immediately handle all the imports so we don't have to go back and forth. So we're going to use link, we are going to need use memo from react, later on we will need a skeleton, we're going to need tooltip, tooltip content and tooltip trigger And we're going to need from the users, let's see, do we have user info? We already have user info.
Perfect. We have user info. We'll also need user avatar. Now, where did I define the user avatar? Because it definitely exists, but it's inside of my source components so in my original source code I've added it inside of here but I think it makes more sense for this to be in components actually so let's add that from our components because it exists right We've already used the user out there throughout our project.
Let's add the video menu from .slash video menu because it is in the video components here, right here. And let's add a video thumbnail from .slash video thumbnail and finally let's add video get many output which looks like it doesn't exist so we're gonna have to create that or does it? So only video get one output exists inside of modules videos types so go inside of modules videos types and basically what we have to do now is the following we have to export a type video get many output and add infer router outputs app router and usually we would now go inside of videos but we really don't have videos get many yet so what we're going to do is we're going to go inside of suggestions and grab that there get many and I'm going to add a to do here change to videos get many because right now it's using the suggestions one. But that's the only one we have. So this should now be working.
There we go, it works. Perfect, now let's go ahead and let's create our variants. So we are going to need constant video roll card variants to be CVA, And then we're going to pass some default classes, group, flex, and a minimum width of zero. And then we're going to add variance here, size, default, gap four, compact, gap two. Default variance, size, default.
Size, default, gap4, compact, gap2 Default variance, size, default Then let's define the thumbnail variance They are going to have the default classes of relative and flex none. Let's add the variance here. The size again on default is going to be width of 38% and on compact is going to be fixed with 168 pixels and the default variance for the size will be default. There we go. Let's just fix the typo here.
Thumbnail variants and video roll card variants. So now what we can do is create an interface. Video roll card props extends variant props with a type of video roll card variants. And that's enough because both the thumbnail and the video roll card variance have the exact same props here. So that's fine.
Besides that, we're gonna have data, which is video get many output items and the number. So this is accessing one item from the pagination and we will have a size which is optional and it can... Oh we don't have to add that now because that will automatically be inferred. Let's also add an optional on remove here, which will be a void. There we go.
And now let's export const videoRowCardSkeleton. Let's just prepare it with return div skeleton. And let's create the actual card. So export const videoRowCard. Let's use the props for this component let's destructure the data the size and the on remove method And let's go ahead and actually build this component.
So I'm going to return div here with a class name of VideoRollCardVariance and pass in the size prop. I will then add a link component, which we have imported with an href leading to slash videos and then data ID and the class name of thumbnail variance and passing the size prop as well. And inside I will render my video thumbnail component. The video thumbnail component will have an image URL of data thumbnail URL, preview URL of data preview URL title of data.title and duration of data.duration. Since in my source code in schema in my videos I have defined duration right here as default 0 and not null.
I don't have to add any fallbacks. In case you didn't do that, because I didn't explicitly tell you that you have to, only if you want to, you're going to have to add this to avoid errors. Great. So we have this. And now I want to go ahead and render this card.
So let's go ahead inside of our suggestions section here and Instead of doing this, what we're going to do is we're going to go over our videos, pages, flat map, get the individual page, return page items map, get the individual video, and then go ahead and render the video roll card. You can import it from components video roll card. This will be suggestions not videos of course. But in the end that is a single video right? So let's give it the key of video ID.
Let's give it the data of video. And now you should see the thumbnail right here. And if you pass in the size of compact, I'm not sure how much actually changed. Let's see for the thumbnail variance. I think just the amount of space the thumbnail takes is different, right?
So not much we can see now. So let's go ahead and continue developing the thumbnail now. The next thing we're gonna do is outside of the link here, and this will be our info part of the thumbnail, I mean of the card. So let's add a class name flex1 and a minimum width of 0, a div flex justify between and gap x of 2, a link leading again to videos, data ID and a class name of flex one and the minimum width of zero and inside an h3 element rendering the title of the video. Now, let's give this a dynamic class name.
The default classes will be font-medium and line-clamp 2 But depending if the size is compact, we will either have text small or text base like this. So now you should see the title right here. Outside of the h3 element, we're going to check if the size is default. In that case and that case only we are going to render a paragraph which will render the views. So let's go ahead and do video view count my apologies data view count views And now you have to find like a little dot or you can just do this.
You can just add, you know, like a straight line and then data likeCount likes. And let me go ahead and check this. Okay, so I think that size is default, so I have to go here and change this to default to see it. So one view, one likes, but what I prefer is using this, but I have no idea how to make that with your keyboard. I just copy it every time.
So you can like Google it, bullet, Google for like bullet point and just copy it. There is a Unicode character of it. Maybe you can ask AI to generate it for you. So just Google the bullet Unicode and then copy it from Wikipedia or somewhere and then directly add it here in the code. And now let's go ahead and check another thing.
So if... Oh yeah, we have to add some class names to the paragraph. So class name here will be text extra small, text muted foreground and margin top of one. Then again, if size is equal to default, in that case, we're gonna go ahead and just add a fragment here, and we are going to render some user information. So let's add a div with a class name, flex-item-center, gap-2, and my-of-3.
Render the user avatar component, give it a size of small, give it an image URL of DataUser, image URL, give it the name of data user name, and then render the user info component and give it a size of small and the name of data user name. I'm so happy we developed these components before so we can simply reuse them now. And outside of here, we're going to add a little tool tip with the tool tip trigger, and then as child prop, and give this a paragraph of data description or no description, and give this a class name of TextExtraSmall, TextMutedForeground, WFit and LinePlamp2. So we are only going to show an Xscript of the description and let's add a tooltip content here and simply add from the video description like this. Let's give this a side of bottom and a line of center.
This is a side not size and let's give it class name background black with 70 opacity. So there we go This is how this will look like, but it doesn't look too good at the moment, simply because this is not a space to render the default video roll card. This is a space to render the compact one. But I will just double check my code just in case I didn't maybe forget to add something. Flux one minimum width of zero, justified between.
I think all this is okay. We are going to detect if something is obviously wrong later on when we purposely start to render this default methods, right? So now what we're going to do is outside of here, we're going to check if size is this time compact. If the size is compact, In that case, we're just rendering the user info like this with the size of small and the name of data user name. And then again, if size is compact, we're going to render a paragraph, which is going to render data views, my apologies, view count views, and then either a bullet point or a straight line, whatever you prefer, data, like, count, flex.
And I will just copy my little dot from here. So basically a bullet point Unicode, you can Google it and you will find this. And outside of this link, Finally, let's add a div with a class name of FlexNone and video menu here with a video ID to be DataID and onRemove will be our optional onRemove so we will later be able to reuse this entire thing inside of playlists. This is video ID. And there we go, we now have this.
And do I have to add a variant to this video menu? Yes, instead of my video menu, I added a to-do to implement what's left. By that, I probably meant the playlist and the on remove here. But one thing we can immediately do instead of the video menu component, which we have inside of modules, videos, components, video menu, we can go ahead and define the variant by default to be ghost. So we don't have to change it every time.
And now let's go inside of our suggestions here and let's change this to compact. There we go. Now it looks better, but this text is still huge. That's probably because we're missing any kind of class names here for the compact. So let's add a class name here, text extra small, text muted foreground and margin top of one.
There we go. I believe that now it does look exactly the same, right? The thumbnail, the two line text, this, let's see. Yes, I'm pretty sure that is it. Great, so now what we have to do is we have to implement two types of view count and like counts here because it's going to be displayed differently on the compact version and on the default version.
So let's say compact views here will be use memo and simply use memo and simply return intl.numberformat English notation compact and simply format data view count. That's gonna be the compact view. And the second argument here will be data view count. You can copy this and then add, oh, this is compact views, and then this will be compact likes. And simply change this to like count and use a like count here.
Great, and now let's go ahead and just use those. So starting with default here, actually we're going to use it everywhere. So this will be compact view, compact views like this, and this will be compact likes. Same thing here. Compact views, and this will be compact lights.
Great, there we go. So now we have this. And we can just, let me check my video roll card. So it's because of the skeleton here. So for now, I'm just going to render that one skeleton simply so we don't have any errors.
We will create the skeleton later when we finish the suggestions section. So what I want to do now is I want to go inside of my suggestions section here like this, and we now have to create a proper way of rendering this, right? Because it's going to be displayed differently if we are on this mode and if we are on this mode for example. So let's go ahead and do that. So I'm going to wrap this entire thing in this suggestions section inside of a fragment and I will give this a class name of hidden on a mobile but it's going to be visible on desktop and I'm going to give it a space y of three of each item here.
Then below this I will create a new div with a class name of BlockedMDHidden and space y of 10. And I will copy the entire thing in here. But there will be one crucial difference and that difference is that this is going to use the video grid card component. So let's go ahead and rename this to video grid card which doesn't yet exist. So let's go ahead and just pass in the props we need.
That's going to be the key, the data, and size is not really going to exist. So VideoGridCard will not have any different sizes. Let's go ahead inside of our videos UI here. And let's go inside of components and let's create the video grid card, the S X like this. And We can also keep our video roll card opened simply because I think we can import some things from here.
Starting with the video get many output. That's definitely one thing we can have. And then our video grid card will be able to use that and also have the optional on remove here. Great. Now, let's export the video grid card.
Let's add video grid card props here. Inside of here, let's return a div. And I will say, I am a grid card. Now let's go inside of the suggestion section and let's import the video grid card from ./.components video grid card. So now, it still shouldn't be visible.
If you completely go compact you should see I am a grid card. So just go as compact as possible here. And let's develop the video grid card. So its div here will have a class name of flex, flex column, gap 2 with full and group. Then we're going to add a link, so import link from next link with an href of slash videos and data ID.
We have to extract the data here and on remove. Data ID like this. And we will render a video thumbnail component, which we can import. It already exists. The video thumbnail component will have an image URL of data thumbnail URL, is going to have a preview URL of data preview URL, a title of data title and duration of data duration.
Again if this is an error you have to add this, If you have set in your schema for it to have a default value, you can leave it like this. And Now you should be seeing a larger thumbnail here. So this is what we expect on mobile, right? In this collapsed mode, we don't want that grid like that. And now we have to build a video info component.
So I believe we don't have the video info component. Video info is basically going to be similarly to this video roll card, this part, the info. I simply put it inside of here in this case. So what I want to do is I just want to separate that video info.esx simply I don't know why but to keep it simpler right so we can have a separate skeleton for this and everything let's create an interface video info props give it the data of video get many output items and numbers So we only pick the single item. And on remove will be an optional void.
Let's export constant video info like this. And let's change. Let's import data and on remove here and now what I want to do is I want to prepare my compact views the same way I did in my video row covered. So let's import useMemo. And let's import, let's copy the compact views here and paste it inside of video info.
Then we can copy and paste this and we can create compact date and this one will be a little bit different. So it's going to return format distance to now from date FNS and passing data created at and add suffix is going to be true. And use data created at here. And make sure you have imported format distance to now from date FNS. And let's assign the proper props here because that seems to be missing.
There we go. And while we are here, let's actually go ahead inside of the video grid card and outside of link render the video info component. Pass in the data to be the data and on remove to be on remove. There we go. There should be no errors.
Video info cannot be used as a component. That's because we haven't returned anything, so that is fine. And now let's go ahead and wrap this up. So I'm going to go ahead and return a div here with a class name, flex, and a gap of three. I will go ahead and add a link to the href here to go to slash users and users, my apologies, data user ID.
So we are adding info about the author of the video. Link is not defined, meaning we have to import next link. Inside of here, let's go ahead and let's render the UserAvatar component from Components UserAvatar. Let's give the UserAvatar an image URL of DataUserImageURL and the name of DataUserName. No need for any explicit size because we will use the default one.
Below the link let's add a class name of a minimum width of zero and the flex of one. Let's add a link which is going to redirect to videos. Video ID. Always be careful. Data.id actually.
Always be careful when typing links because they're not yet strictly typed. Fun fact, Next.js does have an experimental option you can turn on inside of the config to have strictly typed links. So if you type this, you will get an error like, hey, you've never defined that in your app router. But for this tutorial, I'm not gonna be using that. But you can explore that.
You can Google strict links, strictly type links next JS. In the meanwhile, just be careful about your links. Now, inside of here, we're gonna add an H3 element with the data title, and I'm gonna give the H3 element a class name of font-medium-line-clamp-1, lg-line-clamp-2, text-base, and break-words. Outside of this link, I'm going to open a new link which we can copy from here because it's also going to redirect to the user because this will be some more user information here. Inside of here we're going to render the UserInfo component from Components, Users, UserInfo and pass in username here.
So make sure you have imported the user info from its proper modules. There we go. And besides this, we're then again going to open a link. Let me just close it here. There we go.
And inside of here, we will add a paragraph, which is going to render the compact views, then the little bullet point and compact date. And let's just add views suffix here. Let's give this paragraph a class name of text small, text gray 600 and line clamp of one. And outside of this div right here, let's add a new div and render the video menu component from .slash video menu and let's give a video ID here to be data ID and on remove to be on remove so a lot of reused elements here and let's pass a class name here to be flex shrink zero actually oh that's interesting shrink zero are both of this valid yes both of this do the same thing I was I was thinking one of them is wrong, but it looks like both of them do the same thing. Okay, so yeah, shrink zero or flex shrink zero, looks like both of them are okay.
Great, So now let's go ahead and just copy the bullet point here. So we do this. And I think that should be it now. There we go. A nice component for mobile mode.
So in mobile, this is how you are going to see the suggestions, right? And on desktop, if it's collapsed, it's still gonna be this. If it's extremely wide, it's gonna be like this. Perfect. So what we have to do now to wrap up this is add the infinite scroll to our suggestions section.
We can do that quite easily. Inside of this fragment add infinite scroll component and pass in has next page and we have to extract the query from here like this query has next page is fetching next page will be query is fetching next page and fetch next page will be query fetch next page and is fetching next page So now we have the infinite scroll. But here's one thing I want to add. So let me just move that here. As you can see, instead of our video view, there are two places where we render the suggestions.
This one is hidden on large devices, meaning this will be the mobile one. So for this one, I wanna add isManual prop. Well, let's add isManual as an optional Boolean. And if we have isManual, I simply wanna pass this component here, isManual, to be isManual. So if we are on desktop, I am fine on our suggestions here being loaded automatically on infinite load, right?
I will just be scrolling and just load more, that's fine. But if I'm on mobile mode, I want this to be manual, right? Because the user will scroll and keep scrolling and we were just going to have infinite suggestions. They will never be able to reach the comments. And on the comment side, I think it's fine to be infinite because this is the last element.
So I think it's fine to infinitely scroll here. Great, perfect. So I think that is it. I think we've done the suggestions. Obviously, we have to do the skeleton for the suggestions, but you can see how fast this loads thanks to prefetching.
So let's go ahead and see whether we've done everything we've needed to do. We created the suggestions procedure, we prefetched the suggestions, we've created both video row card and video grid card components. I have some doubts about the default size of the video row card, but we will see how these components behave later on when we reuse them. And we connected the suggestion sections with the new API. Excellent.
So what I'll focus on in the next chapter is probably building the search results simply so we can test out the video roll card. And After that, I also want to talk about this little issue that we had, which clearly told me, you know, that my asset was ready in Mux, but my webhook for some reason has never upgraded that. Right, because inside of here, we saw video asset was ready and we saw that the webhook was successful, right? So there should be no reason that it didn't update my record. What I'm thinking happened was maybe a conflict, maybe a video asset ready, fired too late, perhaps, maybe it timed out, but there's no reason because it did fire a success attempt.
So yes, webhooks are not, you know, exactly too reliable. So what I want to do is I still want to rely on the webhooks, but maybe in my studio for each video I should have a little button which says like, hey, can you refresh the status from Mux? I just want to see what does Mux say about this video and just update all of my statuses, playback IDs and all of those things if that's true. Great, amazing, amazing job. We are getting closer and closer to wrapping up the project.
You