
No Comments Yet
Be the first to share your thoughts and start the conversation.
Be the first to share your thoughts and start the conversation.
By logging in, you'll unlock full access to this and other free tutorials on JSM Pro.
Why? Logging in lets us personalize your learning experience, track your progress, and keep you in the loop with new workshops, coding tips, and platform updates.
You'll also be the first to know about upcoming launches, events, and exclusive discounts.
No spam—just helpful content to level up your skills.
If that sounds fair, go ahead and log in to continue →
Enter your name and email to get instant access
00:00:00Â To start adding live features to our application, let's head over to our editor component.
00:00:07Â And if you scroll down, you'll notice that here we have the initial editor config.
00:00:13Â What we'll do is we'll wrap this initial config object with the LiveBlocks config.
00:00:20Â The way that works is pretty simple.
00:00:21Â You just say LiveBlocks config like this, and then you wrap this object.
00:00:28Â And where is this LiveBlocks config coming from?
00:00:31Â Well, it's coming from a new package, which we have to install by running npm install add LiveBlocks forward slash react dash lexical.
00:00:41Â So they have a special integration with the lexical editor so we can make all of that work very easily.
00:00:47Â And then we can import it at the top by saying import liveblocks config coming from at liveblocks forward slash react dash lexical.
00:01:00Â Other than that, we also have to figure out if the text in the form is editable or not.
00:01:06Â For that, we have the editable property, and it's going to be true if the current user type has the permissions of an editor.
00:01:14Â So for now, I will leave it hard-coded, but now it's up to us to figure out the permissions.
00:01:21Â And we can do that right within our page.
00:01:24Â where we have the documents ID right here.
00:01:27Â As you can see, I even left a small note that says assess the permissions of the user to access the document.
00:01:33Â So right here at the top, we're getting the room, but we can also get access to the user IDs that have access to that room by saying const user IDs is
00:01:44Â equal to object.keys to get only the IDs from room.users accesses.
00:01:53Â Like this, make sure to spell it correctly.
00:01:55Â There's a lot of double letters.
00:01:57Â Once we have the IDs, we can fetch actual users by saying const users is equal to await get clerk users coming from user actions to which we pass an object
00:02:11Â with the user IDs like this.
00:02:14Â Next we have to extract the user data by saying const users data is equal to users.map where we get each individual user of a type user and we automatically
00:02:32Â return an object.
00:02:34Â So to do an automatic return, you have to wrap an object inside of parentheses.
00:02:38Â Otherwise, it will just be a basic function block.
00:02:42Â First, we want to spread out all of the properties of that user, but then we also want to give it a user type by saying room.users,
00:02:54Â accesses, and then enter that specific user ID, or in this case, email.
00:03:01Â Question mark dot includes.
00:03:03Â So if it includes room right, then we want to give that user a type of editor Else we want to give it a property of viewer.
00:03:14Â Okay.
00:03:15Â So we have taken everything regarding the user and we have appended the additional user type property.
00:03:21Â This is for all the users, but now we can discuss the same thing for only the current user.
00:03:27Â So we can say const current user type is equal to room dot users accesses.
00:03:35Â Enter the specific clerk user dot email addresses, zero dot email address.
00:03:41Â Question mark dot includes room right.
00:03:44Â And if that is the case, we can give it the editor, else we can give it the viewer.
00:03:49Â Now that we have those properties, we can pass them into a collaborative room by saying users is equal to users data.
00:03:57Â And the current user type will be equal to current user type.
00:04:02Â Moving into the collaborative room, we can now destructure those props at the top by getting the users.
00:04:09Â as well as the current user type.
00:04:12Â And we no longer have to hard code it right here at the top.
00:04:15Â And we can scroll down and pass it over to our editor by saying room ID is equal to room ID.
00:04:25Â as well as current user type is equal to current user type like this.
00:04:33Â Now let's head into the editor and let's accept those props right here at the top, such as the room ID and the current user type.
00:04:43Â And those will be of a type room ID of a type string and current user type of a type user type.
00:04:54Â Great.
00:04:55Â So now we can properly adjust the editable property by saying if current user type is triple equal to editor.
00:05:04Â Well, in that case, the form will be editable.
00:05:08Â That is great.
00:05:09Â And now we can slightly update the editor layout so that we can have the comments on the right side and the editor on the left side.
00:05:19Â First of all, we have this div with a class name of editor container size full.
00:05:24Â Then we have the toolbar plugin, which we'll put inside of an additional div.
00:05:29Â So let's put it right here, indent it properly, and let's give this div a class name equal to toolbar-wrapper flex min-w-full and justify-between.
00:05:48Â Let's save it.
00:05:50Â And if we check it out, this is how it looks like.
00:05:53Â Let's continue with the rest of the layout.
00:05:55Â I'll just make a quick note for myself here and say if the current user type is triple equal to editor, then they should also be able to see a delete model.
00:06:09Â like this, to which we can pass the room ID so we know which room to delete, but we'll code out this delete model later on.
00:06:19Â So for now, I will just comment this line out.
00:06:22Â Next, going below, we can create another div.
00:06:25Â This div will have a class name equal to editor-wrapper, flex, flex-call, items-center, and justify-start.
00:06:39Â Here, we can also check out the status of loading up our editor by saying if status is triple equal to not loaded, or if status is triple equal to loading.
00:06:55Â In that case, we can return a loader component, else we can return an additional div.
00:07:02Â Of course, in the meantime, let's import the loader.
00:07:06Â And let's declare this status right above by saying const status is equal to use editor status coming from LiveBlocks editor.
00:07:19Â because now we have to actually hook it up to our room.
00:07:22Â So now, within this inner div, we can put the entire editor by moving this div above.
00:07:31Â So one, two, three, a few lines above and indent it properly.
00:07:36Â And I don't even think we need this additional div that I created.
00:07:40Â So we can just do this and indent it properly.
00:07:44Â Let's also style it further by giving it a property of editor inner, min-h-1100 pixels, relative, margin bottom of 5, h-fit.
00:08:01Â max-w of 800 pixels inside of square brackets because we're providing a specific value, shadow-md, and enlarge devices margin bottom of 10. Within it,
00:08:15Â we have the rich text plugin with the content editable, a placeholder, and the error boundary.
00:08:21Â Finally, we have some plugins like the history plugin and the autofocus plugin.
00:08:26Â Here we'll also add one plugin belonging to LiveBlocks and we'll only add it if the current user type is editor.
00:08:34Â So I'm going to say if current user type is triple equal to editor, in that case we'll render a floating toolbar plugin,
00:08:49Â like this.
00:08:50Â And this floating toolbar plugin is something that we get directly from LiveBlocks, specifically their example with the editor.
00:08:59Â So I extracted it for you right here in the snippets.
00:09:02Â I do believe that this is the last thing that we'll have to copy.
00:09:05Â We'll see.
00:09:06Â And here you can see the entire file, which you can copy.
00:09:09Â And we're going to paste it within components.
00:09:13Â editor, plugins, and then here you can create a new file called floating-toolbar-plugin.tsx.
00:09:24Â And you can paste it right here.
00:09:26Â It is a longer file, but you'll notice that there is an MIT license right here, which means that we can freely use this code.
00:09:33Â So now we can import it at the top by scrolling up and saying import.
00:09:40Â floating toolbar plugin from that slash plugins forward slash floating toolbar plugin.
00:09:49Â Great.
00:09:50Â So let me show you what that does.
00:09:52Â And at the same time, you'll be able to see all of the other changes that we implemented.
00:09:58Â If we can get rid of this loading.
00:09:59Â I think that loading is there because we haven't actually used the LiveBlocks plugin yet.
00:10:04Â So it's waiting for us to initialize it.
00:10:07Â And right here below this dynamic block, we can add the LiveBlocks plugin, which wraps all of the other functionalities like comments or the collaborator
00:10:19Â list or the floating composer that you can put in here.
00:10:21Â So for now, let's just put that here and later on we'll add all of these additional functionalities as well.
00:10:27Â So if I go back, finally we can see our editor.
00:10:31Â I'm just going to reload one more time for good measure.
00:10:34Â And would you look at that?
00:10:35Â We can enter some text right here.
00:10:38Â And now when you hover, you also have this little icon that will soon enough allow you to add comments to each specific character,
00:10:48Â word, sentence, or a paragraph.
00:10:51Â So to make this little button do a lot of work, we can go back to our code and then add the floating composer component from LiveBlocks lexical.
00:11:03Â We can give it an additional class name of w-350 pixels.
00:11:11Â And below it, we can also render the floating threads, which is another plugin, which needs to have access to all of the thread comments.
00:11:20Â And how can we get all of those comments?
00:11:23Â Well, it'll be pretty simple.
00:11:24Â Just at the top, say const, destructure the threads.
00:11:29Â And say is equal to use threads like this.
00:11:33Â And that's coming from LiveBlocks React Suspense.
00:11:37Â So now we can just say threads is equal to threads right here.
00:11:42Â Let's check it out.
00:11:43Â Going back to the browser, if I select and click it, now you see this nice looking interface that allows us to write a comment.
00:11:51Â Instead of test, write something good.
00:11:57Â And we just left a comment.
00:11:59Â Now, if you click on this.
00:12:03Â You can see that JavaScript Mastery left a comment just now.
00:12:07Â And there's many different things that you can do on this comment, such as mark it as resolved, add a reaction, like a smiley face right here,
00:12:16Â even edit it and delete it.
00:12:18Â So we have full CRUD functionalities.
00:12:21Â Of course, I don't even have to mention that other people can also reply to that thread saying something like, nah, I don't feel like.
00:12:31Â it.
00:12:31Â Okay.
00:12:32Â I'm just messaging myself here, but you get the idea.
00:12:34Â You have floating comments, which you can expand into threads and reply to different comments and even emote to them.
00:12:42Â This was super simple to implement, but you can see how much functionality does it immediately bring to the table.
00:12:49Â And to exit out of it, you can just double press on the word and then click outside of the screen.
00:12:54Â That's it.
00:12:55Â And if you just want to open it, you go somewhere else right here on the, on the text.
00:12:59Â And I think that is Good.
00:13:01Â Perfect.
00:13:02Â Now let's add sticky comments to the right side.