So there is one thing left for us to create inside of the StreamPlayer component and that is the About You card. So let's go back inside of our StreamPlayer folder and find the Index component and I'm going to close everything but that and we're going to go and find our last component which was the Info card and now we're going to create the about card. Let's go ahead and pass this about card some props. So host name is gonna be user username. We're gonna have host identity, which is gonna be user ID.
We're gonna have viewer identity to be the identity like this, bio is going to be user.bio and also one more thing that I want to add here is followed by count and in order to get this followed by count we have to go ahead and extend our user module right here So let's go ahead and see what we have to do. First, I want to go ahead and see where we actually use this stream player, right? So we use that inside of dashboard, you, username, home page, right? And in here we have our user. So this user should be giving us the count of their followers, right?
And we have this function getUserByUsername. So let's go inside of this function. You can find it inside of the lib folder, user service, and in here you should have get user by username. And now what I wanna do here is I wanna go inside of the include and I wanna add underscore count like this, select followed by true like this. So I'm going to count all of my followers for this user and what I can do then is now you can see them in my creator page so in dashboard you username home page when I hover over this user so we have the stream yes but we also have a count so now in my stream player component let me go ahead and modify the user expected prop here right Because no longer are we just expecting this, we are also expecting a count.
So let me go ahead and collapse this element like this. And I'm going to add underscore count, followed by is a number like this and you can see that in my Dashboard you username home page. I have no errors regarding that because it is a correct type I have a count here and I am rendering the count here. So just make sure that you've added this underscore count inside the include in get user by username in the user service and make sure that you've modified the user property inside of stream player props inside of the stream player folder index.tsx and now what we can do is we can go ahead and replace this number zero well with the actual value right so user.underscorecount.followedby like this great and now what we have to do is we have to go ahead and create the about card so I'm going to go inside of the stream player folder and create a new file about-card.vsx. Let's mark this as useClient and let's export const aboutCard and let's return a div aboutCard.
And while we are here I'm already gonna go ahead and prepare my interface about card props. And let's go ahead and make it accept the host name, which is a string, the host identity, which is a string as well, viewer identity, which is a string, bio, which is a string or null, and followed by count, which is a number. And now I'm just going to go ahead and assign the props, so about card props, and I should no longer be having any errors here and let's go ahead and import about card and there we go. So just make sure you've imported about card the same way we did with info card, chat toggle, chat video and header. Now let's go ahead and focus on the about card here.
So I'm gonna go ahead and extract host name, host identity, viewer identity, bio and followed by count. And now we're gonna go ahead and do that quick check to see whether we are the host or not so const host as viewer open backdex whoops is host dash and then host identity and then const is host is going to be viewer identity equals host as viewer like this and let's go ahead and write const followed by label to be followed by count equals one in that case we're gonna say follower otherwise followers great and now let's go ahead and give this a class name of PX4 Let's go ahead and write an inner div and let's give it a class name of group rounded extra-large bg background padding of 6 lg padding of 10 flex flex dash col gap y3 then inside we're gonna go ahead and open a div with a class name of flex items center and justify between and then inside we're gonna render a div which is going to say about hostname. And it's also going to hold the verified mark which we created. So import verified mark. I'm going to change this to use at slash components like that.
And let's give this div a class name of flex item center gap X of 2 font semi bold text large and on LG text to Excel. Like this. There we go. So let's go ahead and take a look at this now. There we go.
It looks okay on mobile and on desktop. There we go. It's an even bigger screen, right? And this will not, this edit your stream info will not be visible for the viewer right so the viewer will see this presentational bio right all right now let's go ahead and continue developing this so we have this verified mark and now what I want to do is go outside of this div which holds this text in the verified mark and we're gonna go ahead and only if is host we're gonna go ahead and for now just write a paragraph here and write edit. Later, of course, it's going to be an actual button.
So now, outside of that div, let's go ahead and open a new one and give it a class name of TextSmall and TextMutedForeground. And text muted foreground and let's write a span followed by count and then a space followed by label like this and now what I want you to do is just give this span a class name PondSemiBold and text primary like this There we go. So now it should look something like this. So you can see it says zero followers and the number of followers should be bold. Like that.
Great, so we resolved our followers here. And last thing we have to do is go outside of this div here and simply render our bio or we're gonna give it a default of this user prefers to keep an air of mystery about them or you can just write no description, right? And give that paragraph a class name of text small, right? There we go, so let me just, can I improve this a little bit? I don't like that it's like this.
There we go. So just make sure you have this space next to span because if you don't then you can see that the text for the followers will be joined like this zero followers. So we don't want that. So just make sure that you add a little space here or you can even add it here. There we go.
So now we have the presentational component here for our user's bio, right? And now we're going to go ahead and create the actual model which will open, which will allow us to modify the user. So what I want to do first is I want to create the server action to allow the update of our bio, right? So make sure you've saved everything in the stream player, make sure you have your about card and now inside of the actions let's create a new one called user.ts. Let's not forget to mark this as user server at the top otherwise it will not work and Now let's export const update user to be an asynchronous function, which accepts the values, which are a partial of our user from Prisma client like this.
And now I'm gonna go ahead and open a try and catch here. And inside of here, I will simply throw new error, internal error. And let's go ahead here and first let's do const self to be a way to get self from the auth service. So let me just align these inputs properly. Now that I have self I'm gonna go ahead and define the valid data for updating this user so that's only gonna be bio and values.bio So I will not allow the change of username here because we have clerk to handle that right?
We handle that inside of our webhook, but bio does not exist in clerk So that's why we update that ourselves and That's why that is the only thing we allow to be updated using this server action. And now let's go ahead and write const user to be await db. So make sure you add db from that slash lib db. .User.update where id is self.id and data is simply a spread of valid data like this and now let's call revalidate path from next cache so make sure you've added this So we're gonna go ahead and we're going to revalidate the path, open backticks, you slash self dot username. And we're also going to revalidate directly slash username because the owner of the stream is going to be able to modify their bio from both of these routes so we want to make sure that they can immediately see the changes and let's return the user like this and I actually believe we don't need to wrap this inside of try-catch at all, right?
I think this is even simpler, yeah, no need to do that. Okay, now that we have this we can go ahead and create our bio model. So inside of the components stream player, I'm going to go ahead and create a new file bio-model.tsx. I'm going to mark it as use client, and I'm simply going to export const bio model to be our return div and let's just say edit. So it's going to represent that button that we have.
So go back inside of the about card and now what I want you to do is replace this paragraph with a bio model from slash bio model like this and let's also go ahead and give it an initial value of our current bio which we also use to render here. Great! Now let's go back inside of the bio model and what we have to do is go create an interface so you can accept those props so bio model props has the initial value which is a string and let's go ahead and assign those so bio model props and there we go initial value of its string or null so string or null and there we go now make sure you have imported bio model from dot slash bio model Make sure you've added it here and passed the initial value, which in our case is empty because we don't have any bio by default. And now I want to go ahead and import everything we need from at components UI dialog like this. So we need the dialogue itself, dialogue close, dialogue content, dialogue header, dialogue title and dialogue trigger like this.
Great. So and we are of course also gonna need our Button components. So let's add the button here as well And now let's create this little dialogue here So it's gonna be dialogue like that, and then it's gonna have a trigger. Let's give it an as child property because it's gonna be wrapping a button component, which will say edit. Let's go ahead and give it a variant of link, a size of small and a class name of MLAuto.
And now it should look exactly as in your item above. As you can see here it should be a big edit button. Perfect! But only visible if you are the host, right? And now in here let's add the dialog content.
Let's add the dialog header and the dialog title which will render edit user bio. And when you click on this, there we go, a model which says edit user bio perfect so I want to go ahead inside of my terminal now and I want to add a package from Shazian UI called textarea So let's go ahead and add text area. All right. And let's go ahead and import everything else we need from here. So we're also going to need our hint component we are going to need this newly added text area from dot dot slash UI text area so let's go ahead and replace those to come from components instead all right and let's go ahead just below our dialog header but still inside of the content and let's add a native form element.
Let's go ahead and give it onSubmit of an empty arrow function and the class name of SpaceY4. And now inside let's render the TextArea component, which we imported. Placeholder is going to be UserBio. OnChange is going to be an empty arrow function value is going to be our value or an empty string so let's go ahead and do the following let's extract the initial value and we're going to store our in a state value set value use state from react initial value like that or we can we can do it like this initial value or an empty string here and make sure you've imported use state from a react. And because we do this pipe pipe operation here, we don't need to do it here.
I believe. Yeah, there we go. All right. So we have the value. Disable prop is later going to come from the pending state and class name for this will be resize none because I don't want it there to be a little button that you can resize the text area.
So now you should have a text area here which says user bio like that. Now inside of the form below the text area let's add a div with a class name of flex justify between and inside of it let's add our dialogue close component with a Button component inside which has an explicit type of Button so it doesn't accidentally submit our form and a variant of Ghost and it will say Cancel outside of the DialogClose we're gonna have another button which will say save. And let's go ahead and give it a disabled prop of false, a type of submit and a variant of primary like this. Great. And what we have to do now is we have to create our simple onChange() function here so we can do that here in this text area.
Let's get the event and let's simply call setValue to be event.target.value. Right? No need to be more complicated than this. And now let's create this on submit function. So in order to do that, I'm going to already going to go ahead and prepare the use transition and we're going to need to use ref and element ref.
So make sure you imported this stuff from React. And now let's go ahead and create our onSubmit function. So const onSubmit here. And before we do it, let's go ahead and just do isPending and startTransition from useTransition. So I don't forget to do that all right so the event is gonna be a type of react dot form event HTML form element like this let's make sure to do event prevent default and now let's start the transition here and What we're gonna call is update user from actions user So what we created just a couple of moments ago so let me move this here just make sure that you have the update user import and I don't think we need the hint so just make sure you have update user from actions user which we created a moment ago.
Alright now inside of the bio model inside of this update user we're gonna go ahead and pass in bio to the value which we control using our input and it has the initial state as well. Great and now I want to do .then and I'm gonna go ahead and call the toast from Sonar, so make sure you add the Sonar package. Let's do toast.success, user bio updated we can do that.catch those.error something went wrong like this and let's go ahead and apply this on submit right here on this on submit. And I believe we should already be able to change your bio. So right now it says this user prefers to keep an air of mystery about them because that's what we hard-coded if there is no bio.
So now if I change to my bio and click save, let's see if that's gonna work. And there we go. Now it says my bio. Perfect. So now we have to add some loading states, some disabled states, right?
So we're gonna go ahead and use this is pending right here and we're gonna disable the text area if it's pending. We're gonna go ahead and disable the button to save if it's pending. And one more thing that I want to do is I want to add my closeRef to be useRef elementRef button with a default value of null. So make sure you have both useRef and elementRef imported from React. Then assign this closeRef to the dialog close, which should also have as child property because it's wrapping a button.
So go ahead and give it a ref of closeRef and then inside of here I'm going to do closeRef.current click like this and that should close the model So if I go ahead now and modify my bio and click on save this should save close the model and save my bio and there we go we've wrapped up our stream player component. What we have to do now is we have to apply the StreamPlayer component to not only be visible in the Creator dashboard but instead I want it to be visible when I click on some user in my recommended tab so I can actually see them streaming instead of this right here. So we're gonna go ahead and see if we have any outstanding bugs with our StreamPlayer component.