
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Â Up next, we want to make sure to have this nice looking title and it has this little icon that turns it into an input field,
00:00:09Â which is pretty cool as well.
00:00:10Â And then once you click outside, it will actually edit the title and we'll also add a share button.
00:00:16Â Even though this input at the top might seem simple, I'll show you how to do it properly.
00:00:22Â That means editing it properly, turning it into an input, and even finishing the editing process by clicking outside of the input.
00:00:29Â So let's start by adding some useStates at the top.
00:00:33Â We already are on a useClient component, which is good.
00:00:36Â So we can say useState.
00:00:39Â And the first one will be editing.
00:00:43Â So are we currently editing?
00:00:45Â At the start, it'll be set to false and we can import useState from React.
00:00:50Â Right below, we can create another useState.
00:00:55Â And that one will be called loading.
00:00:58Â So are we currently loading or not?
00:01:01Â And at the start, it'll be set to false.
00:01:04Â We also need to have one for the document title.
00:01:07Â So we can say use state document title and set document title at the start equal to roommetadata.title.
00:01:17Â So we can have a default value.
00:01:20Â We'll also need to work with some references to some elements.
00:01:24Â So we can declare them right now by saying const container ref equal to use ref of a type html div element.
00:01:35Â And the value at the start will be null, but we do have to import use ref from react.
00:01:41Â And we can duplicate this over and create an input ref as well.
00:01:45Â Now we can head down and go inside of this first div that is inside of the header.
00:01:52Â And we can give it a ref equal to container ref so we can target it later on.
00:01:58Â Next, I'll remove this share p tag and open up a dynamic block of code that checks whether we are currently editing.
00:02:07Â And if we're not loading, in that case, we want to show an input.
00:02:13Â And this will be a ShadCN input.
00:02:15Â So let's install it by saying mpx shadcn-ui add latest, add input.
00:02:25Â And then as soon as it is installed, we'll be able to import it from UI input.
00:02:30Â We also have to close our ternary operator by returning an empty react fragment with a p tag.
00:02:38Â That's just going to render the document title.
00:02:40Â So if we're not rendering, it'll be a static p tag with a class name equal to document-title.
00:02:49Â So let's quickly check that.
00:02:52Â As you can see, it simply says untitled right now, which is what we're getting from the database.
00:02:58Â Now let's modify this input by giving it a type equal to text.
00:03:05Â a value equal to document title, a ref equal to input ref, a placeholder equal to enter title, an onChange equal to a callback function where we get an event,
00:03:23Â and we call the setDocumentTitle and pass the eTarget value, which is the value of the input we want to add.
00:03:30Â We're also going to add an onKeyDown property so we can register every key press.
00:03:36Â And for each one of these key presses, we want to call the updateTitleHeader function.
00:03:42Â So let's declare it right here.
00:03:44Â const update title handler equal to A callback function that accepts an event, which is going to be a keyboard event like so.
00:03:57Â And we can now just call the update title handler.
00:04:03Â Oh, looks like we'll have to modify the actual type.
00:04:06Â It's not a keyboard event, rather a keyboard event, specifically the HTML input element keyboard event.
00:04:16Â And we might need to say react.keyboardEvent.
00:04:19Â And now it's good.
00:04:21Â Also, we'll disable this input when we're not editing.
00:04:26Â So it'll be disabled and we can give it a class name equal to document-title-input.
00:04:33Â But now how can we switch between the states of editing and not editing?
00:04:37Â Well, let's do that right here below.
00:04:41Â Not outside of the div, but just outside of this dynamic block where we can check if current user type is equal to editor.
00:04:52Â So this will be a special variable depending on whether the user has the access to edit the room.
00:04:58Â For now, I will just hard code that at the top.
00:05:01Â const current user type is equal to a string of editor.
00:05:06Â If the user is an editor, and if we're not currently editing, then we can return an image.
00:05:15Â Of course, we need to import the image from next image and we have to add and, and right here.
00:05:22Â Then this image will be of forward slash assets, forward slash icons, forward slash edit dot SVG with an alt tag of edit.
00:05:33Â A width of 24, a height of 24 with an on click, which is going to do only one thing, set editing to true and a class name of pointer.
00:05:46Â So we know that it is clickable.
00:05:48Â On the other hand, if the current user type is not editor, we can copy this part and paste it below.
00:05:57Â and say something along the lines of current user type is not editor and we're not editing.
00:06:05Â In that case, we're going to return a p tag that'll have a class name of view only tag.
00:06:13Â And it can say something like view only.
00:06:15Â Finally, we have to have the loading indicator.
00:06:18Â So we're going to say if loading.
00:06:20Â Then we're going to render a P tag with a class name equal to text dash SM and text dash gray dash 400. That's going to say saving dot dot dot.
00:06:34Â Great.
00:06:36Â Now, if we go back, you can see that if we click on untitled, it is not editable, but if we click this icon, then we can start typing into it.
00:06:45Â But now if we exit out of the screen, which is the typical behavior on these great applications.
00:06:54Â It is not properly saving it.
00:06:56Â It doesn't end the input.
00:06:57Â We're still there.
00:06:58Â So that's something we have to still do.
00:07:00Â And we can do that by creating a new use effect right here.
00:07:06Â So let me create a new useEffectHandler that's going to have a dependency array right here.
00:07:13Â And we have to import the useEffect from React.
00:07:16Â In it, I'll create a new function called const handleClickOutside that'll accept one param, which is an event of a type mouse event.
00:07:27Â And we simply want to check if container ref.current exists, and if no container ref.current.contains event.target as node,
00:07:43Â Then that must mean that we're no longer editing.
00:07:46Â So we can say set editing is false.
00:07:49Â And we can add and remove this event listener.
00:07:52Â So document that add event listener on the mouse down.
00:07:56Â We're going to call the handle click outside.
00:07:59Â And also we want to clean that event up, remove event listener on mouse down, handle click outside.
00:08:05Â So now if we go back here and start typing something, and if I click outside, you can see that we're back on the not editing rather viewing tag.
00:08:17Â But if we reload the screen, it will just clean up again because we didn't actually update the title.
00:08:23Â It is still untitled.
00:08:24Â So to implement the full functionality, we'll do that in the update title handler, where we're going to check if the E.key is triple equal to the enter key.
00:08:35Â Then we're going to set the loading to true and open up a new try and catch block.
00:08:41Â In the catch, we'll have a console log of error.
00:08:45Â And at the end of both the try and the catch, we'll set the loading back to false because we're done with the procedure.
00:08:52Â But now we have to actually update the title.
00:08:55Â So first, we're going to check if the document title, the new one, is not equal to the current title, so RoomMetadata.title.
00:09:06Â That means that it's different.
00:09:07Â And if that's the case, we want to call the updateDocument function.
00:09:13Â So this is a new function which we are yet to create in the Room Actions.
00:09:18Â So let's quickly head over to Room Actions and let's create a new action.
00:09:23Â Export const updateDocument is equal to an async function that accepts a room ID of a type string and a title of a type string as well.
00:09:36Â We can open up a try and catch block.
00:09:39Â In the cache, I'll put a error message saying error happened while updating a room.
00:09:46Â And we can simply try to update the room using the built-in Liblox functionalities.
00:09:52Â So const updated room is equal to await.
00:09:58Â LiveBlocks.UpdateRoom where the first parameter is the room ID.
00:10:03Â And the second one is the options object where we can provide a metadata and then provide the new updated title.
00:10:13Â Once we get this updated room, I will call the revalidate path so that things actually change on the front end.
00:10:20Â And it'll point to documents forward slash room ID.
00:10:25Â And finally, we want to return parse stringify the updated room.
00:10:31Â So now if we go back right here, we can update the title for real by saying const updated document.
00:10:40Â is equal to await update document coming from room actions, where we pass the room ID and the new document title.
00:10:50Â And then if we have access to an updated document, we will stop with the editing.
00:10:56Â In this case, we also have to make the update title handler an async function as we're using await.
00:11:03Â And there's one small tweak that we have to do as well, which is add another use effect below the existing one.
00:11:10Â It will have one thing in the dependency array, and that is the editing part.
00:11:15Â So it's going to be recalled every time when we're editing or not editing.
00:11:19Â And here we want to do an if statement and say if editing and if input ref.current, then we want to focus it.
00:11:28Â So we want to focus on the input so that the user knows that they're typing.
00:11:32Â Great.
00:11:33Â Moving back, we can actually give it a shot.
00:11:36Â I will click right here on the edit and I will type something.
00:11:40Â And if I exit out of it, you can see that it remains saved.
00:11:46Â If I enter in it and I type something like, this is a new title and press enter.
00:11:53Â It says saving.
00:11:54Â It saves it.
00:11:55Â And when I reload, you can see that we have a new title.
00:11:59Â But what would happen if we change it again?
00:12:01Â So I'm going to say test.
00:12:03Â I will exit out of it and I will reload the screen then.
00:12:09Â It looks like it didn't save it.
00:12:11Â So to fix that, we just have to call the update title handler on this use effect where we click on the outside.
00:12:19Â I'll just call the update document action and give it the room ID and the document title whenever we click outside of the function.
00:12:28Â So we can put that right here in this if statement.
00:12:31Â And I think we have to recall this user effect whenever the document title changes or whenever the room ID changes, which shouldn't be often.
00:12:39Â If I go here right now and I just say new title and exit out of it, you can see that the new title remains, which is great.
00:12:48Â And that's it.
00:12:49Â Now we have a much better header for our document and it allows us to change and update our title for real.
00:12:56Â And it sends the updates over to LiveBlocks to store it in their storage.
00:13:01Â We won't add the share button just yet because we'll do it later on once we decide to implement full sharing and permissions functionalities with the model
00:13:11Â that will allow you to enter specific email addresses in it.
00:13:15Â But for now, I want to give some love to our homepage as well, because if you lose this ID in the URL or if you mistakenly exit out of it,
00:13:23Â it'll be very hard to come back to this special document.
00:13:27Â So if you go to the homepage, it's still pretty empty, right?
00:13:31Â It says start applying document and we cannot see a list of the existing documents.
00:13:36Â So next let's fetch all the documents we have created so far and display them right here so we can very easily get back to them.