The collaboration feature in our project is starting to shape pretty well, but we are still missing a few features. So far, we were able to see the other person's cursor, but we didn't see their name attached to the cursor. We also didn't add the avatar stack, which displays the images of all users who are in the room with us. So let's implement that in this chapter. First, Let's modify our canvas component.
Then import panel from XY flow react. Next, import avatar stack from live blocks react UI. And the last thing we have to do is render that panel with a position of top right and avatar stack inside. And once you save and revisit any of your workflows, you will now see an avatar stack at the top right corner. The problem is there is no user information.
All users will be presented as anonymous. In order to fix the issue in which all of our users in the room are scoped as anonymous, I would like us to go and search for the component which we are using. So avatar stack. In here you will find information on how to use it, which we just did, but scrolling a bit down will also instruct you on how to add the user information. So the prop which we need to use is resolve users.
Let's run the following prompt. Add a post endpoint at API live blocks users route.ts that takes user IDs payload and returns their display info which is name and avatar in the same order. And put null for unknown ones. Resolve them from clerk. Require an outlet user and organization.
Only develop the route.ts file. Do not modify other files. And I've included Liveblocks best practices and clerk backend API skill. Let's review what the agent has created. So I have a new route inside of my LiveBlocks folder inside of the API folder called users.
Now I'm going to read through the description of what the route does. First we have an out gate which reads out and returns 401 unless there is both user ID and organization ID. Next we have input validation. So this is something that sometimes the agent will do sometimes it won't. So if you don't have a snippet like this but instead maybe you just have user IDs extracted from request.json that's honestly okay.
Next it appears that we iterate over those user IDs we check if they are empty and then we return back an empty array so we don't unnecessarily compute or initialize any clients. Then we initialize clerk client and we get the user list using the IDs which we got from our payload. We also scope the organization ID to ensure that this endpoint cannot be abused by someone just wanting to get a list of users using their IDs. We also assign the limit to the ID's length limit and then we map each user by ID in this exact format and then we resolve each user's information. So I can't really guarantee that your agent will produce the exact same code but I assume it's going to be something similar to this.
What's important is that you in the end have something resolved which produces an object like this. Basically an object with a name, with an avatar inside of an array. And if it wasn't able to resolve it, it falls back to null. Basically it matches the user info which we extracted from our global LiveBlocks interface, user meta info. So that is generated in LiveBlocks config.ts.
Now just in case your API route looks wildly different from this one, remember that you can always go ahead and find chapter 13 and go inside of app API and find the exact route. So now we have to wire in that new endpoint into the resolve users prop. We can achieve this with a simple prompt. In the room component add a resolve users prop to the live blocks provider in line in the prop. It should post the user IDs to our API live blocks users endpoint and return the parsed response.
If the request fails, return undefined. And that should produce an output similar to this. You should now have a resolve users prop attached to your live blocks provider, which is an asynchronous function, which extracts the user IDs who are in this room. It opens a try and catch block, and inside of the try block, it attempts to fetch the API live blocks users and passes the user IDs as the body payload. If the request fails, we return undefined.
If something else fails, we also return undefined in the catch. Otherwise, we simply await response.json. And now go ahead inside of your localhost 3000 and refresh your workflow. And if we've done this correctly, we should now see the user avatar here and the name of the logged in user. And if you try joining with two different users, you will now see the actual name of each user next to their cursor.
And in the top right corner, you will see the name of users inside of this room. So after all of these prompts you should have one new route and two modifications to existing components, the canvas and the room. Let's go ahead and commit stage and push these changes.