So what we have to do now is we have to use that stream player component and add it so that it appears when a random user clicks on a specific user's ID like this. So right now we have this little screen. So what I want you to do is I want you to prepare two browsers. In one browser make sure that you are logged in and make sure that you have access to this dashboard from where you have set up your stream key so that when you click start streaming in the OBS this should stay connected like that. So make sure you are logged in in one browser as that and then go in another browser and make sure you are logged out.
You can do that by going in the incognito mode. So if you try just in a new tab that's gonna log you out from everywhere. So make sure that you have two browsers one in incognito and one logged in and now if in incognito mode you try and click on any of the users here you can see that it leads us to the login screen so we don't want that to happen so what we have to do is we have to go inside of our middleware so let's find our middleware.vs file here and we have to add slash username like this with the colon here and that's going to allow logged out users from for clicking on this specific user we just have to refresh this let's try now and there we go you can see that now it still works even though this information well I don't know if how much of this information is true because we are logged out completely So what I want to do now is I want to go inside of my app folder, browse username page.csx right here. And I want to go ahead and completely remove this and let's just add a div saying username page like that.
And in fact, you know what, let's remove everything just so we can do it one more time together so you can understand what's going on here so inside of this user page we are inside of the username folder right so inside of the browse folder we have the username folder and then page.csx which means that we have access to that username in our URL params and that means that we can go ahead and get the user using await get user by username from our lib user service. So let's pass inside params.username like this and this function will return the user but also the relation to the stream so we can write if there is no user from the database or if this user has no stream for any reason we're going to return not found from next navigation so that's going to immediately redirect us to the not found screen like this and this will prevent anything else from being rendered so you don't have to put return here that does it automatically for us great and now what we have to do is we have to check if we are following this user and if we are blocked by this user so let's write const is following to be await is following user from the follow service and pass in the params, sorry, the user.id and let's the const isBlocked be await isBlockedByUser and pass in the user.id So ensure that you have isFollowingUser and isBlockedByUser from libBlockedService and then go ahead and write if isBlocked in that case we are also going to throw a not found so the user can never have access to this user's stream And now what I want to do is simply render the stream player from components, stream player, so make sure you add this import.
And now we have to pass some props here. So let's give it a user which is a user, let's give it a stream which is user.stream and let's give it isFollowing which is going to be isFollowing constant like this and now what should happen is that in your incognito mode you should be seeing this screen you can try on some another user and you can see that we have their information here great So now we reuse that entire stream component we can still collapse the entire thing here. Amazing! Everything works great. What I want to try now is start the live stream.
So let's go ahead and start the live stream And let's see if this incognito user will be immediately connected and they are. Great! And now what I want you to do is confirm inside of your chat settings, so go into Creator Dashboard where you're logged in, and make sure to turn off Must be following to chat. So make sure that this is turned off and then that will allow the guest to send messages so let's see if that is working because we didn't have a chance to try it out besides we only sent one message right but that was ourselves so let's see if other user can send so I'm gonna write hello I am a guest like this and there we go it's working a guest has sent a message and it was immediately received here so I'm going to write hello I am Antonio like this and there we go they are now chatting perfect and you can see incognito mode right here I can take a look at the community here but I have no actions here but if I go inside of the stream chat here you can see that we have a block button here and there seems to be a little bug here with multiple participants but I think it could be just some cache.
There we go yeah when I refreshed it finished. Okay it's not a big deal I'm gonna go ahead and if it appears again I'm gonna go and debug it but I think it was just a one-off thing. So what you have to realize now is that inside of the creator we have the option to block this user but if you try this now it's actually not going to work. The reason it's not going to work is because this user is not logged in so we cannot actually block them from the database. So what we have to do is we have to go inside of our block action.
So go inside of actions block right here and we have to extend this to do this. Well to do's which we've written right here. So what I want to do is I want to add my logged in user, so const self to be await getSelf from the auth service, so make sure you add this and then I'm going to dynamically get this blocked user because if we try and block a user just by their ID it could be that it is just a guest right and let's take a look at our token action you can see that in here where is it there we go So in here if the user is not logged in we just generate a random ID using the UUID package right and we generate a random name for them. So when we try and block that in our database it cannot find that user because it doesn't exist, right? So here's what we're gonna do instead.
We're gonna dynamically get the blocked user. So let blocked user, we're gonna define it first like this and then we're going to wrap this inside of try and catch function like that and in here in the catch we're just gonna write a comment This means user is a guest. So if this function fails, we're gonna assume that that's because the user is a guest and we cannot find them in the database. So next thing that I want to do is I want to get my room service so I can actually kick the participant if I cannot actually block them. And we can I believe get the room service from our ingress.ts function?
Yes there we go Here we have the room service. So go ahead and copy and paste this. So go inside of your ingress.ts and copy the room service and we use the room service here in the reset ingress which we fire every time someone tries to generate a new key, right? So make sure you copy the room service, go back inside of the block function and just paste that here and you can import the room service client from LiveKit server SDK and make sure that you have all of these keys. You can always double check in your environment that you haven't misspelled anything.
Great and now that you have the room service you can go ahead here and before you revalidate the path what you can do is open up another try and catch block here and inside of the try write await roomService.removeParticipant() and this removeParticipant() is a function which first needs to accept the room from where we are planning to remove a participant and then in the second arguments it accepts the identity of that participant. So what do we know about our rooms? How do we know which is the room that we are trying to kick the user from? Well if you remember our ingress action in here when we go ahead and actually created the ingress you can see that a room name is self.id which means that what we have to pass in the first argument for the room id is self.id that is our room and in the second argument we have to pass in the ID which we are trying to kick. So this function will work for both logged in and logged out users because we don't care about the database, we just care about the currently connected users and since this user has a viewer token created with their ID, we can kick them out of the room.
And there is a chance that this fails as well, and that most likely means this means user is not in the room. Right? So this function can fail as well so that's why we wrap it inside of a try and catch. And then what I actually want to revalidate is the following. So I don't need to revalidate the blocked user here.
Instead, I'm just going to go ahead and revalidate slash you self dot username slash community which we are going to create later to unblock our users. Like this. So let's go ahead and try this out now. So let's refresh the creator dashboard and let's refresh this user. Make sure that you are live, make sure that you are streaming and now go inside of the community as the creator and click the block button for the guest and what should happen is that the guest is disconnected in real time and you can see it's removed from the community chat here.
Great! But if they refresh they're gonna be able to join again right because they're logged out users and no matter how much we try to protect this they can always clear their cache, they can always go in incognito mode right. There's no way of actually preventing them from doing this. That's why we added the feature that only followers can chat. So you can always enable this as a streamer and then when the guest joins again they're not gonna be able to bother you.
If anything they're gonna be watching your stream and well that's a good thing for you you're gonna have more viewers. Great! So we now confirmed that this is working but I want to check one more thing. We just confirmed what happens when a streamer is watching and when a guest is watching but now I want to check what happens when a streamer is watching and another logged in user is watching. So in order to do that you're probably gonna have to use another browser right because or you can log in in incognito mode but we do use Google login so I'm not sure if you're gonna be if you know your password great then you can go ahead and log in.
So just make sure you are either in incognito mode and then log in as the other user here or just use another browser. So I'm just gonna go ahead and prepare that setup. All right, so in this browser I'm logged in as CodeWithAntonio and in another browser I'm logged in as Antonio right here. So in this another browser this is the user of who is currently streaming So I can see my creator dashboard here and this other user should just see Antonio and look at the Antonio as a viewer like this. So let's go ahead and try this out.
So now it says only followers can chat. So go ahead and make sure in your chat settings that you have turned that on and let's go ahead and see if that will be working. So right now I cannot send a message or anything but if I go ahead and click follow this should be revalidated and there we go. Now I can send a message like this and there we go. You can see how it's here in real time.
Let's go ahead and try an unfollow and now I should be blocked from sending this again. There we go. Now I cannot click here. Perfect. So what I want to try now is I want to go in chat and I'm going to turn this off and I'm going to turn on the delay chat.
So let me refresh here and let's see if now, first of all, I have this text that messages are delayed by three seconds so let's try this out this should be delayed and there we go one two three and now it's being sent perfect so we have slow mode on but I am noticing a small little bug here. You can see that the corners of my input are rounded here. So let me quickly go ahead and see what we missed. So I believe that should be inside of the components stream player we have chat form component in here and the input says if is followers only in that case rounded the. So let me go ahead and I believe we can use something else so we should also do if is delayed so let's go ahead and combine those two here so go ahead and find the input where we do the rounded the and I'm gonna go ahead and do if is followers only or if is delayed then show this and maybe we have to wrap this in parentheses like this and now it should be better there we go now this is resolved Perfect and it should be resolved here as well.
Great and one more thing I want to try is what happens when I turn on both of them. Now I should be getting a different message which should say followers only and slow mode, great and one last thing I want to try in the chat if I disable the chat, will that completely disable the chat? And there we go, the stream has loaded but the chat is disabled and if I refresh here once the stream loads there we go chat is disabled. Perfect. So we have all of that working.
Let me just go ahead and enable the chat now and now let's go ahead and try blocking this user. So I'm gonna go ahead and refresh here and this user, sorry this user right in your creator dashboard go ahead and find another user and click block. So this should immediately disconnect me but since I'm logged in this should also block me in the database so when I refresh here I should be seeing a 404 page and I am great so this is working and now what I want to do is I want to unblock myself but we don't have functionality for that yet so I'm going to quickly show you go into npx prisma studio right here and then go inside of the block model right here and just delete the only record you have like this so just delete it and now they should be unblocked so if I go ahead and refresh again I should be seeing this user right here great so this is working perfect So I'm gonna stop streaming now and what I want to do now is I want to add a proper loading state for this. So let's go inside of the app folder, browse, username.
We have page.tsx. Let's go ahead and let's add loading.tsx right here and let's go ahead and do user loading and we can simply return stream player skeleton and let's go ahead and just wrap it inside of a div here so let's write class name h-full like this so this this should improve the loading state a bit I think it already looks a bit better there we go great so now every part of our app which is loading has a loading skeleton. Great! But I want to do a couple of more things. So I want to add a custom 404 page and I want to add a custom error page.
So inside of the username go ahead and add a new file not-found.tsx like this and let's go ahead and call that not found page let's create a div here with a class name h full flex flex call space y flex flex call space y for items center justify center text muted foreground and inside let's add an h1 element 404 and let's give it a class name of TextForExcel and below that a paragraph we couldn't find the user you were looking for like that and let's add a little apostrophe here you can use this unicode like that so you don't have the error and let's go ahead and add a button component from components UI button let's give it a variant of secondary as child and let's render the link component inside from next slash link and let's write go back home and let's give it an href of a forward slash like this and you can try this out either by blocking yourself again or more simple you can go inside of the URL and change from the valid username to something that doesn't exist like this. And there we go now we have a custom 404 page and we have a link to go back home and I want to do one more thing I want to create an equivalent error page so this is going to be quite similar so you can copy not found paste it inside of the username here and this time rename it to error.vsx like this and error has to be use client so go ahead and add use client at the top and let's rename this page and the default export to be error error page like this Let's change this, let's remove the h1, we're not gonna need it.
Instead let's just render the paragraph something went wrong like this and if you want to try that out go inside of an individual user profile here and inside of page.vsx what I want you to do is just throw an error so throw new error test like this and that should trigger the error page and there we go you can see how we have a custom something went wrong page and this will only show in development so you don't have to worry about this being shown to your users. Great so we handled that case as well and now just remove this from the user page so you don't have that hard-coded error. Great! And now I just want to do one more thing and that is that since we have custom error and 404 pages for this one I want to also copy them and apply them to the entire application. So copy error and copy not found and you're going to just paste them inside of the app folder like this.
So you should have the error and you should have the not found in the root of your application. So they are most likely not going to show right because well you can try me let's try and trigger and not found so I'm gonna try try with like multiple sub paths like something that doesn't exist slash something that doesn't exist slash something that doesn't exist and there we go you can see how now we have a custom 404 page perfect and that error page which we copied and put in the app is going to be a fallback if the error happens anywhere inside of our application great And perhaps we can change one thing. So inside of this app folder, inside of not found, instead of telling the user that we couldn't find the user they were looking for, we can just write we couldn't find the page you were looking for right because this is like a global 404 whereas this one is the user 404 so that's why we have a different text here. Great so we just confirmed that a lot of things are working perfectly inside of our application right and let's also check this right So if I follow this user will the number of followers increase?
There we go, the number of followers increased. Great, that's one last thing that I wanted to check. Perfect! So what we're gonna do next is we're gonna go ahead and we're gonna create our home page and then we're gonna create our search page and then let's not forget we also have the community tab which is just gonna be a big table of all of our blocked users and the action to unblock them. So next step is the homepage.