So now that we've ensured that in our database we have a stream for each and every of one of our users we are ready to create this page right here for when we click on chat. So right now when I click here I get to a 404 because that page could not be found and my URL is slash you the username of the currently logged in user slash chat like that so if yours is anything different of course your username will be different but you can go ahead and confirm that by going inside of the dashboard, you, username, navbar, sorry sidebar and in here you have a navigation component and I believe that in here we defined our routes so just confirm that your chat has the proper href to go to slash you, the current username and then slash chat. And now what I want to do is I want to go inside of the username folder here and just as we created this home folder let's go ahead and let's create the chat folder like that and then inside of chat create page.tsx and let's go ahead and do chat page, return a div, chat page like this And now if you go ahead and try and click on your chat again, you should be seeing the chat page right here and you can see my URL here at the top.
Great, so let's go ahead and give this a proper title here. So I'm gonna go ahead and give this div a class name of padding six, so everything is a bit indented. Now I'm going to add a new div with a class name of flex-item-center justify between and margin-bottom of 4 and then inside I'm going to go ahead and render an h1 element and I'm going to write chat settings and I'm going to go ahead and give this a class name of text to Excel and font bold like that. So a huge text which says chat settings like this and we actually don't need this classes here so we can just do a margin bottom of 4 like that you can also put this class directly into the h1 element if you need it. So it's the same, great.
And what I wanna do now is I wanna create my stream service so that I can get the stream model from the database using the user ID. So let's go ahead and create the stream service. So go inside of your lib folder and create a new file stream-service.ts and in here let's go ahead and let's import our database. I'm just going to change the import to go to slash lib and then I'm going to export const getStreamByUserId to be an asynchronous function which accepts the user id and simply loads the stream using await db stream find unique where we have a matching user ID and then just return the stream. So this is going to work because in our schema here in the stream we have a relation with the user ID which is unique So only one stream can exist per one user.
So this will work with the find unique instance here. So now let's head back inside of our chat page. So app folder, dashboard, you username, chat page. Let's go ahead and turn this into an asynchronous component. And first thing I want to do is I want to get self so await get self from the out service and then get the stream using await get stream by user ID and passing self dot ID like that.
And if we don't have a stream, we're gonna throw a new error here, stream not found like that. Great, so if you have everything properly in your database, you should not be getting absolutely any error here instead it should just load chat settings without any errors like it is on my screen. Great! So what we're gonna do next is we're gonna build a component called ToggleCard. So let's go ahead and first add one component which we're gonna need from ShatCN.
So you can actually shut down your webhook ngrok. So I'm gonna close everything and I'm even going to shut down npm run dev. You don't have to do it but I will shut it down and do npx chatcn ui add latest add switch like that so that's one of the components we're going to need here and now it's installing the switch component great let's do npm run dev again make sure you refresh your local host so it's up to date and we're now going to create toggles to change these properties of the stream model so is chat enabled is chat delayed and is chat followers only so we're gonna create the functionality for that. So let's go inside of our chat page here and outside of this div which is wrapping the h1 element let's create a new div like this and inside of here we're gonna go ahead and give it a class name of space Y4 and then we're gonna render toggle card here and we're gonna be reusing this toggle card three times for each of our properties. So isChatEnabled, isChatDelayed, and isChatFollowersOnly, right?
So we don't have to create three different components. Instead, we're gonna create one reusable component, which is going to modify a specific field. So go ahead and give this a property field of is chat enabled. That's gonna be the first one we're gonna do. Let's give it a label of enable chat and let's give it a default value of stream.isChatEnabled and we load the stream from here.
I made one mistake while I was developing this on my own. I wanted to use a property key for this but remember key is a reserved keyword in React right it's used when you do a map or an iteration over items, right? So key will not be passed as a prop inside of that component. You won't be able to access it. So just in case you're wondering why I chose the name field for this prop, so just make sure it's field or if you're doing something else make sure it's not key.
Great, so now let's go ahead and create our toggle card component. So I'm going to go inside of the chat here and create a new folder underscore components and in here I'm going to create toggle-card.tsx like that. And let's go ahead and mark this as use client and let's export const toggleCard and return a div toggleCard. Now let's go back inside of the chat page and now we can import the toggle card from .slash components toggle card and when you save you should no longer be getting any errors and instead you should see the text which says toggle card just below the chat settings here. So let's go inside of the toggle card and now we have to enable all of these props to be passed here.
So I'm going to create an interface here. An interface called toggleCardProps. I'm going to give it a label of string and a value of boolean. And for the field property we're gonna create a type so type field types are gonna be a couple of possibilities and the possibilities are only gonna be these three so is chat enabled then we're gonna go ahead and use is chat delayed and then we're gonna go ahead and use is chat delayed and then we're gonna go ahead and write is chat followers only so that's gonna be our field types and then we can pass the field to be field types like that and then when you go ahead here and assign those props so toggle card props make sure you add this and go back inside of the page you can see that in the future when we write field you can see how it will auto-complete to only control one of the three elements because that's the only thing we enable for this component. So that's why we did this.
We could have just passed the string but this way we ensure type safety for our API, I mean our server action which will receive this. Great, so let's go ahead and extract this field. Let's extract the label. Let's give the value a default value of false, and let's extract the field like this. And now I'm going to go ahead and give this div a class name of rounded, extra large, bg muted, and padding of 6.
And inside of here, I'm going to go ahead and create a div with a class name, flex-item-center and justify-between. And I'm going to go ahead and create a paragraph here which will render the label and I'm going to give it a class name of font semi-bold and shrink zero. Great and now below that I'm going to go ahead and create a div here with a class name of space y2 and inside I'm going to render my switch component make sure you import it from add slash components UI switch like this so don't import it from erratics and inside of this component I'm going to render a value right so if value is true then it's going to say on otherwise it's going to say off And let's go ahead and give this a couple of properties. So I'm gonna give it unchecked property of value for now. That's the only one we can give it now.
And now, as you can see, we have a nice little toggle here and we cannot click it yet but we're going to go ahead and enable that in a second. So what I want to create now is the server action to actually update the stream So let's go inside of our actions folder and create a new file stream.ts. Let's mark this as use server that's very important and now let's go ahead and let's import revalidate path from next cache and let's import database from add slash lib database let's import the stream from Prisma client and I will just move it here at the top and let's also import get self from lib auth service like this And now export const update stream here to be an asynchronous function, which takes in the values and is going to use partial values of stream like that. And now let's go ahead and wrap this inside of a try and catch block and let's throw new error, internal error in the catch. Great, and if we are inside of the try block first let's attempt to get self using await get self then let's attempt to get self stream from await DB stream find unique or we could have used that lib which we created but let's do it like this for now and let's do user ID to be self.id.
If we don't have self stream it means there's nothing for us to update so throw new error stream not found like that And now let's go ahead and create valid data here. So name is gonna be values.name and we get values from our props here. And we know that it has name because we're using the stream model, but only partially. So only some values will be able to be updated here and we're going to define which ones using this object so name then is chat enabled which is going to be values is chat enabled then is chat followers only is going to be values is chat followers only and is chat delayed is going to be values is chat delayed. So just make sure that all of this are matching right.
Great so these are the values which we're going to allow the user to update from the stream. And then let's simply do const stream to be await database stream update, where id is self stream.id and data is simply gonna be a spread of valid data, like this. And now let's revalidate a couple of paths here. So let's revalidate the path slash you self.username slash chat. Let's revalidate the username entirely.
And let's also revalidate the homepage where someone else will be looking at the stream. So those three and Simply let's return stream at the end. Great. So this is our update stream server action. You can of course, always confirm your code with my GitHub if you're having any doubt.
Now let's go back inside of my app folder, dashboard, you username, chat, components, toggle card here. And let's go ahead and import a couple of stuff. So I'm gonna go ahead and import post from Sonar. I'm gonna go ahead and import use transition from React. And then I'm gonna go ahead and import update stream from actions stream like this and now let's create an on change function here so I'm gonna write const on change to be an asynchronous function and let's go ahead and write startTransition and we actually don't need to make this asynchronous so just a normal function startTransition inside and let's do updateStream and we're going to update this specific field so make sure you wrap it inside of square brackets because this means the actual field, right?
And you can see how it already throws an error here because that's not part of the partial stream values, right? So let's go ahead and make sure that this is inside of square brackets and then we write field and then you can write stuff like this and this is completely okay because field can only be a part of is chat enabled, is chat delayed or is chat followers only, which matches the partial of the stream because it has those three values right here. You can try and if you go and write something else, like ABC, you can see that we have an error because that's not part of the stream. Great now let's go ahead and add .then here and let's write post.success chat settings updated and let's do .catch post.error something went wrong like that and I just realized that I didn't tell you where I got this start transition from so my apologies yes there is a start transition from react but that's not the one that I wanted to use my apologies so remove this for now and instead go here and add const is pending and start transition from use transition like this my apologies all right and now let's go ahead and assign this onChange here to the switch element so let's give it a property of onCheckedChange to be onChange and let's also give it disabled of is pending like that and now what I want to do is also open my terminal and run npx Prisma Studio here So make sure this is running so you can see the result in the database.
So refresh this. I'm gonna open this in the new terminal. And in here I have two streams. Right? So right now I'm logged in as Antonio, which means that the stream I'm looking at is Antonio's stream.
And right now it says, is chat enabled is true. So let's go ahead and click on this. And what should happen is this should turn off like this. And it says chat settings updated. And if I refresh my Prisma schema, I should be seeing that for Antonio's stream, is chat enabled is now false.
Great. And now I should be able to turn it back on again. So now it's pending. And then when it gets updated and revalidated it should go back to this one. I'm not really sure why is it not going back so let's go ahead and see if we did...
Oh, because we hard-coded this to false, my apologies. What we should hard-code it is to the opposite of the current value like this in the on change, my apologies for that. Let's try this again. When I click here now, there we go. Now we can switch between on and off.
And one thing that I want to do is I'm gonna go ahead and change this switch component to look a bit nicer because it's almost invisible when it's turned off so let's go and close everything here, let's go inside of our components UI switch component here and in here what I want to do is I want to change the colors a bit right so inside of this first one switch primitives dot root I believe that we can control the color of checked by finding this data and then in square brackets state checked and let's move it from bg primary to bg emerald 500 like that and let's go ahead and change this one to not be BG input when it's unchecked but instead be BG white slash 10 like this and now what we have to do is we have to change the color of the thumb here as well So let's go ahead and change this from using BG background to use BG primary like this. And there we go now our button looks much nicer as you can see now when I turn it off it should be visible still. Let's go ahead and try, see why this is not working.
So this is state unchecked here. And it looks like BG, oh, I wrote BG, BG white. So it should just be BG white like this and there we go now It's visible when it's off and when I turn it on It's back here again. Great So now we can just easily copy and paste this component a couple of times for all of our other fields, right? So let's go back inside of our chat page.
So app, dashboard, username, chat, page here. And we can copy and paste this two times. So make sure you have three instances of this. The second one is gonna be is chat delayed and let's change this to be delay chat and the last one is gonna be is chat followers only and this one is gonna say must be following to chat like that And let's go ahead and try this three out now. There we go.
So we have enabled chat. This works. Oh! Looks like it's not exactly working. Let's go ahead and see why.
So isChatFollowersOnly, isChatDelayed. Let's go ahead inside of the toggle card to debug why this is happening here. So we have our field property. And Let's go ahead inside of the update stream action to see what's going on here. So we get the values Is chat enabled is chat followers only is chat delayed.
So what I'm gonna do is I'm gonna console log the valid data here to see what's going on. So I'm going to open my terminal and I'm going to be inside of where I'm running npm run dev to debug this. So it seems like whenever I turn one on, Everything else seems to trigger as well, even though they explicitly get undefined, so I'm not sure why they're changing their values. So let me check inside of the actual database to see what's going on here. So in my Antonio's stream, isChatEnabled is false, isChatDelayed is false and isChatFollowersOnly is false.
Oh, okay, okay, okay, I think I know what's going on here. We can remove this console log here. It's because I don't pay attention. So go back inside of the chat page.tsx and you can see that for the value I use the exact same one instead of using the appropriate ones. So for this first one it should be isChatEnabled for the second one it should be is chat delayed and for the last one it should be is chat followers only.
So make sure you modify the values of each and every one of them. Let's go ahead and refresh this. Let's try this again. When I click here only that one is enabled, when I click here only that one and when I click here only that one and in my Prisma Studio if I refresh now all of the values for Antonio's stream, in my case, should be true, except of course, isLive. So isChatEnabled true, isChatDelayed true, and isChatFollowersOnly is true.
Perfect, So this is working. What I want to create now is the loading skeleton for our toggle cards. So let's go ahead inside of the toggle card component here and let's go ahead and let's import the skeleton. So go ahead and import skeleton from components UI skeleton and then let's go to the bottom here and export on toggle card skeleton here and very simply we're just going to return a skeleton component with a class name of rounded extra-large padding 10a and width of full like this and now what I want to create is the loading page for our chat. So in here in the page we call these two items.
So we cannot wrap this page inside of suspense because it's in the root page so what we can do is add loading.tsx which is going to act as our suspense around the page. So create a new file on the same level as page called loading.tsx and here we have an error now so we can quickly fix that by adding chat loading like this and let's add a div loading like that so make sure you do an export default and now I think for a second there we go you can see how it says loading for a second here and now we're going to replace that with our new placeholders. So let's go ahead and let's import the skeleton component from components UI skeleton and let's also import toggle card skeleton from at slash components underscore components toggle card like this And let's give this div a class name of padding six space Y four. Let's go ahead and give this a skeleton component which is going to represent the chat settings label, right? So let's give it an h10 and a width of 200 pixels and then below that open up a div with a class name of space y4 again and render three instances of toggle card skeleton like that.
And now our skeleton should look much better. So when I refresh here, well, it's very hard to see, but you can see how for a second there is a nice loading skeleton here. And it's especially visible when we go from one page to another. There we go. You can see how we have a nice loading state.
Now it's cached of course, so it's much faster. But if I refresh from here and then go here, there we go. You can see how we now have a nice skeleton. Great. So now that we finished our chat settings here, what I want to do next is I want to go ahead and create our keys.
So I'm going to leave the community for last. What's going to be inside of the community is an ability to unblock someone. But we're going to do that later because we have to create a whole table for that, right? So instead I'm gonna go ahead and create keys which means that we are finally starting to create some ingresses here and some live stream connection. Great, great job!