for the last one month I have been working very hard to create the most complete realtime messaging application built with only PHP and L the application which would not use any other technology for websockets and realtime data exchange after one month of work I finally had the result which was beyond my expectation we have realtime messenger built with only Lal PHP MySQL and deployed on production environment under SSL with custom domain the application is meant for small and medium organizations for internal Communication in file sharing once the application is installed you will have access as admin user admin users can add new users block or unblock existing users give and remove admin user permissions sending and receiving messages in real time is the most important feature of the application if sending plain text messages is not enough you want to share code or format the message you can use markdown syntax emojis are crucial part of every messaging application and ours is not an exception we have full integration of Emo's with ability to customize color schem as well the application has easy and fast way of sharing images but you can also attach and send arbitrary files or even record and send audio file system has a list of pre viewable file types which can be viewed on full screen the list contains all images videos audio and PDF files but you can easily extend this list older messages are loaded on scroll if you want to remove your set messages this is also possible in sidebar you will see all users and groups which you are member of sorted by latest message date every user can start conversation to another user or create the group and add users for group chat group is only visible if the user is a member of the group every member can see the description of the group and the list of the users but the owner has permission to edit and delete the group group might contain multiple users thousands of messages and even more shared files deleting the group can be very timec consuming operation when you try to delete the group the action will immediately return the response and the background job will be started to delete the group with its messages and attachments once the action is completed all users will be notified using websockets and the group will be gone from the user interface that seems great but the entire application would be useless if it would not be compatible for different screen sizes our messenger is fully functional even on very small smartphones so you or your colleagues can easily use it from your phones and finally we are going to learn not only how to build this awesome project from start to finish but also how to set it up on VPS server assign custom domain to it and set up Lal Reverb websocket server on production with Enix you are going to see every single step of it hello everyone my name is Zur and I'm the cic I have 12 plus years of experience working as a professional software developer this particular project was a great challenge for me I spent about 100 hours of research preparation Development building editing so that finally I could create a single 12 hours the best tutorial on topic how to build realtime messaging application on leral I tried my best from the very beginning of this tutorial to explain every single line of code what I have written in this project even though the tutorial is 12 hours long you cannot come complete this in 12 hours you are going to need much more than this maybe one week maybe two weeks maybe one month but the most important thing is that stay patient and follow with me this project is not something you're going to watch and you're going to know everything no there are some complex Parts in the project as well we're going to use react and Lal on the back hand side on the back hand side we're going to have complex cues we're going to also create uh background jobs we're going to create uh socket events we're going to understand how broadcasting works on the lateral side you're going to learn how to create migrations how to define c data and we're going to Define pretty complex C data okay for all the messages groups users and the users which are parts of the groups that's going to be pretty exciting you're going to learn how to create middle you're going to learn also how to work more advanced STP like we're going to learn what is broadcasting and how it works and how we're going to use this in our project we're going to use Lal Reverb for this we're going to understand what is Q and how that Q system works and we're going to also create background jobs which are running by separate queue we're going to also learn how to publish mail templates from vendor folder and how to customize them for sending emails in our project on the react side you're going to also learn a lot of things like how to work with the components how to use hooks how to create event bus how to set up a web circuit connection from react to lateral Reverb server how to receive the data through this web soet how to send the data and there's a lot more than this building such types of projects require a lot of time and energy the single best thing what you can do right now is to hit the Subscribe button and like the video Additionally you can provide a positive comment which will help also a lot the channel to grow at the end of this tutorial we're going to deploy this project on production environment assign custom domain to it and we're going to also set up on production the Lal Q system system and Reverb managed by supervisor as a choice of Hosting provider we're going to use hostinger who is very kindly sponsoring this entire massive project hostinger is one of the best hosting providers out there they provide sheared hosting Cloud hosting VPS hosting or even email hosting for this particular project we're going to grab VPS server on hostinger and we're going to set the entire project with VPS but we're going to install a specific version on VPS with Cloud panel which gives us user interface in browser from which we can manage a lot of things we can create databases we can configure the PHP settings and even engine configuration files we can have a look entire file manager we have possibility to install SSL certificates assign custom domains to our project or even add new websites and that new websites can be not only PHP but no JS and python as well in our case when we install this VPS with Cloud panel we have chosen lotel so with just couple of clicks we had VPS server running with the latest version of Lal on it which we replaced with our messaging application what I really love in hostinger is that their prices are very affordable and servers are really fast actually so here you can grab the VPS server for as low as $4.49 and that includes 4 GB of RAM 50 GB of dis space and 4 TB of bandwidth in my opinion this the first version KV M1 is even enough for most of your personal or medium size of projects but if you want to choose stronger VPS you obviously can and you can see that you can get 32 GB of RAM and 400 GB of NVM space dis space for as low as $20 per month if you decide to grab the hosting VPS hosting and follow with me and build this project or maybe another project with me but use the instructions how to deploy your lateral projects on production environment well obviously you need to add the VPS server into the cart then you're going to choose the period for which you want to choose the hosting 1 month 12 months or 24 months the longer period you choose more savings you're going to make we selected KV M2 plan and if you scroll down below we can see the total price this is $67 and this is for two years so basically when you pay this once then you are free for two years so if you scroll scroll down below and you're going to find a coupon code section right here hostinger also provided a coupon code for my viewers and if you provide right here the code hoic and hit the enter this will give you additional 10% discount on already very good price so finally you can get two years off very good good hosting very good VPS server for as low as $150 then you can obviously provide your personal information fill up your credit card details and submit the secure payment which will redirect you to the age panel hostinger Cloud management panel and when you have this access at the end of this tutorial I'm going to also access my AG panel and we are going to together deploy the project on production to check hostinger services go to the website hostinger.com theod holic or click on the link in the video description all right no further Ado let's start building this awesome project before we start working on the project let's just make sure we have all the necessary programs installed the programs where I'm going to mention right now are my personal preference and you can have other programs if they serve the same purpose and if you have decent knowledge to follow with me on other programs that's totally fine generally if I don't develop my projects on Docker I use exm which is a package and it contains Apachi Marb and PHP just make sure you have PHP and MySQL programs installed and working on your operating system and that's going to be totally fine we're going to also need composer which is a package manager for PHP make sure you have composer comment globally available we're going to also need node.js try to download the latest stable version of nodejs generally for YouTube tutorials I try to stay as close to my audience as possible in and previously I was using PHP store but I can understand that most of you don't have that luxury to have that paid product so most of you prefer to develop on vs code so I'm trying to develop my projects on vs code for my YouTube tutorials if you are on vs code that's great if you are on PHP storm and have decent knowledge to follow with me on PHP storm that's going to be totally fine you are welcome but on V code I have separate dedicated video how to set up V code for LEL development which includes all the extensions uh settings my personal um like keyboard shortcuts and even my personal theme so you can check that video and just set up yes code for LEL and follow with me and I also recommend to download and have Kit and especially I need git bash which is more intelligent terminal on Windows rather CMD or Powershell and I'm going to create my larel project using git bash once you have all these programs installed we're going to open uh simp control panel we don't need Apache because we're going to start with lal's uh phps buil-in server and but we need a MySQL because we're going to connect to mySQL database so I'm going to close this then I'm going to open G bash which is the following terminal let me zoom in slightly and we're going to create a Lal project I'm going to navigate into desktop and I'm going to create my project right here composer create Das project laravel SL laravel and I'm going to provide the local folder name to which I'm going to call my project and I'm going to call this Lal 11- react D messenger let's hit the enter it's going to download the latest leral version the latest stable version which is 11 something at this stage whenever I'm recording this it's going to install all the dependencies and set the project for us as soon as the project is installed where we're going to open this using vs code I'm going to type code L 11- react D messenger let's hit the enter and the project is opened that's cool uh let me try to zoom in if it is necessary yeah I think we can zoom in even more now let's connect to the database I'm going to open file let's scroll down below find this DB connection section and I'm going to uncomment this by default Lal 11 comes with the default connection to SQL light but I'm going to change this and install my SQL as a choice of the database so I'm going to change this into MySQL the host is going to be Local Host we can leave this 1271 as well the port is the default the database let's call this llore react uncore messenger the username is rud and the password I'm going to leave this empty username route and password empty is the default username and password for um for MySQL running with exm and I'm going to also open right now let's close all all the tabs and I'm going to open PHP my admin and we're going to have a look what databases are available there uh PHP my admin is not open because we need to also open when to also start Apache so we're going to click Start Right Here here we need this to access to that web based client reload this and we should be able to see phpi admin here we go and these are all the database that is available right now okay but the connection happens through the username route and password is empty and right now I'm going to execute migrate command which should create the following database in my uh my SQL let's bring up the terminal I'm going to actually switch to git bash right now I'm going to select the default profile to be git bash I'm going to close that I'm going to reopen the terminal and now I'm using gitbash every time let me execute PHP Artisan migrate let's hit the enter the database level react messenger doesn't exist in MySQL connection would you like to create it just make sure your current user has the permission to create new database in my case root user definitely has the permission so if I type yes right here yes it's going to create the database for me and it's going to execute the migrations as well now if I reload my phpi admin I am going to see LEL react Messenger application right here okay and if you open that not the application but the database and if I open the database we're going to see all the tables available right there so we have the project almost set up let's execute PHP Artisan serve let's hit the enter it's going to start The Local Host Port 8000 let's click right here it's going to open this in the background and here we have this Lal 11 application up and running now let's open second terminal and I'm going to install Lal Breeze which is a starter package for authentication in profile management let's execute composer require ll/ Breeze d-d to install this as a Dev dependency once the requir comment complet completed and Breeze was required we're going to execute PHP Artisan Breeze colum install this will ask us a couple of questions which stack we would like to install and we have a couple of options the default one which is just Blade with alpine JS we have the Live Wire we have the live wire with the functional API we have react View and API only this particular tutorial I'm going to do on react so I'm going to provide right here react and by providing react right here internally behind it's going to also include additional packages like for react the package is necessary for react and it's going to also install inertia okay but let's finish this survey and we're going to provide additional uh information like what additional features we want and I'm going to provide I want dark mode and which testing framework do you prefer and I'm going to leave this uh default we can choose P PHP unit need whichever you want but this particular tutorial I'm not going to uh write tests any tests uh but still let me just provide pest right here which has index zero because I see pest is getting much more popularity and if I decide to write tests in the future for this project pest will be already installed let's hit the enter and now pay attention to these logs internally it's going to include pay attention right here inera Jes it's going to also install um this Ziggy package which is necessary for declaring roads inside react it's going to also install react related packages and update also package Json we're going to see all that packages installed right here and the package the command is almost finished the composer Json has been updated the command was finished and now we have the fulls application with react and Lal working so so if we open right now the browser and reload we're going to see login and registration buttons right here when I click on the register or login it doesn't matter we don't see that the reload icon moves because we have this single single page application running uh with Lal and react and if you want to see full course how to create such type of single page applications I do have separate dedicated video I think it's a 5 hours tutorial on YouTube it's a kind of inertia course as well for LEL and react now what we're going to do we're going to install a couple of more dependencies for this project which we definitely going to need okay and let me try to show you which dependencies we're going to uh use we're going to use headless UI which is uh the unstyled accessible UI Library so we have dropdowns we have list box we have models and couple of other things but the package only provides the functionality it doesn't provide pretty def styles for Styles inside here inside code T when CSS is used the exact same framework we are going to use in our project so we're going to use that headless UI I'm going to also Install hero icons which is my favorite uh lightweight um SVG type of um icons library and we're going to use this I'm going to also use Daisy UI in certain cases which is also tyin CSS component Library we're going to need to install Daisy UI as well and then we're going to install um its um components we're going to do this and we're going to also use react Emoji ker the following package and we're going to install this one as well uh for pro putting emojis in our chat so we're going to use this as well and I'm going to also use uh react D markdown package to render mark down um messages inside our message layout nicely okay so this is the package we're going to do okay now let me bring up the terminal and I'm going to execute mpm install at headless uh UI SL react we're going to also Install hero icons SL react uh Daisy UI Emoji peer- react and we're going to need react D markdown and I'm going to also install uid package to Generate random uids in my project if I don't make any mistake I'm going to hit the enter and it's going to install all the packages okay the packages we are installed let's clear this up and now we're going to install broadcasting we're going to enable broadcasting for realtime communication which is going to install internally Lal Reverb now let's talk about what is broadcasting and how we're going to use this in our application alongside with Lal Reverb broadcasting is a method in LL whenever our client our browser connects to the server to the socket server and receives that realtime updates regarding certain things for example imagine that we have browser right here which connects to server that server is a lateral server okay and let's imagine that the browser requested for example to generate a zip so we want to generate zip but generation of the zip is a very long process and server needs one hour to generate that zip obviously the user cannot sit right there and wait until that server generates this ZIP and wait for that request to last it for one hour right instead that user right here continues to use the browser normally for different purposes the server receives that request returns response immediately to that user that okay we received your request and we're going to now start working on zip generation and you're going to get an update in real time when the zip is generated okay and the server works right here so it's going to do all that work generation of zip and as soon as that zip is generated then in some way so in our case using Reverb Lal Reverb which is a separate server running on different port so the server will tell Lal Reverb to tell the user that the zip generation is actually done okay and that Reverb connects to that user using web socket okay now as soon as Reverb gets that information that sends uh the user zip generation and after 1 hour in real time user gets this information okay so we're going to do something similar right now and we are going to uh do this real time messaging in the same way so we have user one user who wants to communicate to another user so we have user one and we have user two and we have laral server right here which connects to the data datase right here and we also have l Reverb running now user one sends this request to laral um server let's call this laral and this is a message so this is the message I want to send to user 2 so this is whole package now Lal gets this information puts that in the database okay so message one goes into the database and then L will also tells Reverb to notify user 2 about that information and now Lal Reverb takes this message and sends that message that same message from user one to user two and user two is going to receive that message in real time that is very simple demonstration of broadcasting and with Lal Reverb previously Lal Reverb didn't exist and thew other services like Pusher for example who was one H extra Pusher which was responsible to send that messages to the users because the Reverb didn't exist and The Pusher is actually third party package which I think also is not free and um Lille lille's awesome Community actually created that Reverb first pay party package which is actually really awesome so let me remove this Pusher because we have only Lal Reverb in our case and we're going to use Lal Reverb in reality that messaging actually that channels right here um that exists that can exist into multiple forms like we're going to have also group chats so there will be a group right here let's call this group one and there will be user one user two and maybe user three also joined in the same group okay and somebody let's say user one might decide to send a message to Lal server um okay let me try to create new page I'm going to demonstrate this so we have this group one inside we have user one we have user two and we have user three okay and now we have the lateral server right here as we had and which connects to the database now user three sends the message inside that group now Lille saves that message in the database tells Lille Reverb to update other users about this and lateral Reverb now pushes that information in certain channel so Reverb pushes this information always in a certain Channel okay it doesn't directly push to any specific user it pushes that into certain Channel and there are users listening to that channel to receive that information okay so this is message one pushed into some Channel and user one user two and user three all are listening to the same channel and all of them will receive that information even in our case user three who sent that message whenever Reverb pushes that message back to the group one channel we're going to display that sent message to user one two and three all of them so this is how we are going to implement the whole realtime communication and and we're going to use also Reverb for other uh realtime things such as whenever user three requests to delete the group so that group might be very large with multiple users and hundreds or even thousands of messages and attachments deleting that group with all the messages and attachments is going to take some time several minutes maybe so this user uh three will send the request to delete the group now l will take this return response immediately okay we took your response and we're going to delete the group okay and LEL will do that in the background process so that's a different thing in as synchronous process and whenever that asynchronous process is finished it's going to tell now Lal Reverb to delete to notify basically all the users in this channel to in this specific channel uh to uh tell those users that the group has been deleted okay and once L will Reverb we'll receive that message from LEL or from some asynchronous process that the group needs to be deleted to notify other users that Reverb will uh tell everybody that the group is about to be deleted and that group should be gone from the user interface okay this is the whole idea how we're going to implement that asynchronous flow that real time um not the asynchronous but the real time data communication between the client and between the server we're going to install this broadcasting by executing PHP Artisan install broadcasting and it's going to install Reverb as I mentioned let's execute PHP Artisan install colum Road casting let's hit the enter okay it's going to uh publish the broadcasting configuration file it's going to publish the channels Road file and now it asks ask whether we want to install Reverb or not if we decide to develop with an other for example Pusher with a third party provider we can type no right here but I'm going to type yes by default it is also yes you don't need even to type anything we're going to hit the enter and it's going to install leral Reverb and we're going to build this whole messaging application with leral Reverb now here we have another question would you like to install and build nodejs dependency required for broadcasting I'm going to hit the enter which means yes and it's going to install and build the node dependencies as well once this is done let's open file scroll down below and we should see Reverb related Keys which we are added because of the commment we executed right now so we have the re uh Reverb app ID we have the Reverb app key and um everything what you see right now and we also have V Reverb related Keys as well and if you open right now package Json for example we're going to see a couple of packages like inertia react is installed headless UI we're going to see lateral Echo as well which was installed because of we need this realtime communication we have Pusher JS also installed and react and react Tom we are installed because of inertia and in the package ason we're going to see um inertia Reverb and um that's basically uh all we have other packages like sanctum which is also installed with Lal Reverb now let's talk about database schema what tables we're going to have and how those tables will be connected to each other so we're going to have one which is going to be users table we're going to have one groups table we're going to have a junction table between users and groups we're going to have obviously messages table okay let's going to be users there going to be groups there going to be user groups table this is going going to be messages table and we're going to also have another table for conversations okay so the groups will have groups and users will have many to many connection so one user can be part of multiple group in one group can contain multiple users there will be also conversations table as I mentioned so let's move this right here and the user and the conversation table will have also many to many connection the conversation will have inside there will have user one and user two so the conversation can only happen between two users okay and this message table right here will have group ID it's going to have group uncore ID and it's going to also have conversation ID so the uh message can be sent either inside the group or it's going to be sent inside the conversation that message will also have sender ID who send that message and it's going to have also receiver re receiver ID so and this is going to be the whole thing and we're going to also have attachments table separately and the message will have one to many connection uh to this attachments table and also every group and every conversation will have last message identifier last message ID so that's going to be for the conversation and for the group as well this last message we can probably this last message ID and based on that information the conversation and the user always knows what is the last message sent into that group or into that conversation and we're going to use that last message information to sort the conversations as well as the groups by the last message ID okay whenever we're going to display that the whole thing in a uh user interface in the browser we're going to have conversations on the left side and the message is right here okay like this and the conversations needs to be sorted by the last message ID descending so the latest uh the group or conversation inside which I sent the latest message that should be always at the top so that's why we're going to need that last message ID to um later to sort these users and groups uh right here in the sidebar by by that last message ID now let's generate models and migrations PHP artisan make model first I'm going to generate the group model and I'm going to generate this with the my ation file as well let's hit the enter then I'm going to generate conversation model conversation with its own migration as well then I'm going to generate message model message with its own migration and I'm going to generate message attachment with its own migration so now we have just generated four models and four migrations let's go into dat database migrations let's expand this and now I'm going to work right here and generate the actual tables inside these migrations I'm going to open create users table migration create groups table conversations messages and message attachments let me collapse the left side and I'm going to start with this create users table I'm going to add Avatar to the uh users table so I'm going to provide right here table string Avatar and I'm going to make this nullable okay so other than Avatar I also need is admin and blocked it so I'm going to add on the table um Boolean column which is going to be is admin by default it's going to be false and I'm going to also generate um time St column one time St column and I'm going to call this blocked Ed I'm going to make this also nullable okay so I added avatar for the image is admin flag which by default is false and blocked it uh whenever I want to block the user I'm going to provide that blocked add column right here now let's open groups table migration and we're going to add the following columns we're going to need uh the name on the group we're going to add the nullable description column we're going to add the owner ID who created that group we're going to uh set this constraints to users so that's going to be the foreign key to the users table the owner ID we're going to um also set the on delete to be Cascade so whenever the user is deleted for some reason uh its groups can also be deleted so in the same migration we're going to also create table for group users which will have ID it's going to have the foring ID to the group ID column now constrainted on the groups it's going to have also Fring ID to users and it's going to have its own time stamps as well and I think we need to implement the down right here as well so let me just duplicate this yep drop if exist group users now let's open the next migration uh which is going to be the conversations and right here we're going to add the foring ID the user ID to constraint to users and user ID too okay so this going to be the conversation next let's open Messages table and we're going to add the message which is going to be long text and nullable I'm making this nullable because sometimes we just want to send attachments send files without any text message so in this case the message will be now okay we're going to provide the sender ID uh which is going to be foreing key to users we're going to provide provide also receiver ID uh which is going to be nullable so the user the sender ID is basically mandatory some somebody is sending the message but the receiver might not be a single user but it might be a group so we're going to provide the group ID which is going to be also anable but basically either receiver ID or the group ID should be should exist in the record um to consider that record a valid record okay and we're going to also have conversation ID which is kind of related to the receiver ID because the conversation has user ID one and user ID 2 okay and the conversation is going to be also nullable if we are sending this message inside the group the conversation ID will be also nullable okay and we're going to also down below create add last message ID columns to the groups table and we're going to add this to the conversations table as well the foring ID to uh messages constraint and messages but in both cases uh the last message ID column is nullable because if there are no messages in a group what should be the last message ID it's going to be now okay so and this going to be the messages and finally we're going to have attachments we're going to have the uh message ID to which message the attachment belongs we're going to have the attachment uh name we're going to have path of the attachment M and we're going to have also size of the attachment now let's open models and I'm going to Define feelable arrays protected arrays properly let's open User Group um all models basically and let's start with user so inside user inside fillable I'm going to Define Avatar let's put Avatar right here we're going to need name email we're going to also put verified at actually this is called email verified at I'm going to also put is admin right here because we're going to need uh to set those properties um with the mass assignment um later in the controller okay so let's go in the group and here let's define uh protected avable uh we need name we need a description uh we need owner that was generated by co-pilot we need owner ID uh and we need I think last message ID last message ID and that's it so inside group we have only four columns let's go in the message and let's define protected fillable right here so that was autogenerated so here we need message the actual content message we need sender ID we need group ID and and we also need receiver ID receiver ID okay cool let's open message attachment and generate protected fillable right here and here we need message ID we need name we need path we need mine and we need size and I think we need to also open conversation model and we have to generate um user ID one and user ID to I think that's all what we need here and yeah we also need last message ID on the conversation now let's open user model again and we're going to Define relation to groups so uh let's put this below costs so we're going need public function groups and we are going to return from here uh this the user belongs belongs to many uh and let's autoc complete this okay so the user belongs to many groups and the Rel the relation happens through the group users table and those columns user ID and group ID is the default one is okay so I think we can remove them and just like this we have this belongs to menu relation from the users to from the user to group let's go now into group here we're going to define the relation to users in the same way which has men to men relation so we're going to need belongs to men user with the table group users we're going to need messages relation and the group has many message we're going to need the owner relation because group is owned by a user and we're going to also um I think in the group yeah that's that's all what we need so let's go in the message and here we need a sender we need the relation to user we need the receiver and we need the group and we also need relation to attachments because message might have multiple attachments in the message attachments I think we don't have any relations and finally in conversation we need relation to last message we need relation to user one and we need the relation to user two now we're going to create factories and generate seed data so that we can use that c data on the front end whenever we start working on displaying messages or displaying conversations but first let's generate factories I'm going to generate two factories one's going to be the group Factory and second second is going to be the message Factory PHP artisan make Factory group we need Upper Keys G group Factory let's hit the enter and we also need message Factory let's hit the enter okay so let's open group Factory and let's open message Factory as well okay let's scroll down below and here we're going to generate the fake data for name and for description the group has these two main things for which we need to generate that fake data at the moment let's open message Factory right now and here we have we're going to do much much much more work okay so we are going to first of all generate this um return type of array but I'm going to define the variables I'm going to use the variables which doesn't exist yet so let me let me show you and just be with with me you're gonna uh understand everything just stay with me and be patient okay we're gonna need the sender ID right here we're going to need the receiver ID and we also need group ID but as you see those three variables yet don't exist we're going to Define them we're going to also need some random fake message this is what we need and we also need create dat when the message was created and there going to be some fake date between now and minus one year so I'm just generating generating a random date from now back to one year now the task is to properly generate the sender ID receiver ID and group ID the idea is the following that whenever we're going to generate we're going to execute to create messages using with the help of factory the users and groups should already exist in the database so we're going to select some random user inside the sender some random user inside receiver ID and random group but we are going to do this by following certain rules we cannot have the sender ID and receiver ID to be the same also it doesn't make sense to have receiver ID as well as the group ID right we need to do some validations the message is either for receiver for to a specific user or it is just for the entire group so this is exactly what what we are going to do so I'm going to generate the random sender ID but that sender ID can have only two values zero and one obviously sender ID cannot be zero this is I'm doing this only to just um do the check basically so if the sender ID is zero okay this is what I'm going to do in this case I will take the sender ID to be a random element from the database of the user's table okay any random ID from the user's table this is going to be the sender ID if it was if it happened to be generated zero okay however and I'm going to just convert this into an array because this plug method gives me collection okay and I'm going to set the receiver ID to to be one okay so one if you if you notice right here we have a small issue basically we're selecting all the IDS from the user Table and there is a there's a chance that the following code will take the ID sender ID one in this case sender ID and receiver ID both can be one okay just bear with me we're going to update this line and we're going to make sure that sender is not one if the receiver ID is n is one so next we're going to do an lse statement as well and in else if the sender ID is actually one we're going to select the receiver ID to be a random element from users table okay so if the center ID is happened to be zero I need to make sure that okay sender ID was selected as zero so I'm going to select the sender ID to be anything uh but one and this is what I'm going to do I'm going to modify this line and I'm going to do okay okay down below we're going to do also a group ID we're going to handle the group ID properly so I'm going to set the group ID to be null and then I'm going to check if um I'm going to select the random Boolean value but with the probability of 50 to be true so basically there's a 50 chance 50% chance to uh select true and 50 person to select it false okay so in this case I'm going to select the group ID randomly either it's going to uh satisfy this if or Not by 50% um probability and I'm going to select the group ID to be a random element from the database from the groups table okay if the group ID however is selected I'm going to make sure that the receiver to be needs to be null so here I'm going to selecting the entire group group because I need this and I'm going to select the uh sender ID to be uh any so I I need this group for the following reason the sender ID needs to be any user inside this group the sender ID cannot be the sender ID selected right here because that sender ID can be cannot be it might not be part of that group okay so the sender ID needs to be one user from that group that's why I'm executing the following line and the receiver ID needs to be null okay and just like this we actually have prepared the um message Factory with the proper data however what we need right now is to make sure that on this line we don't select this sender ID one so we can provide right here where oops where sender ID does not equal to one or we can provide an integer here as well then we're going to execute plug on this so you might think that this is like a a lot of work just to generate CED data and maybe you are right maybe this is a lot of work just generate the C data however before I actually start working on functionality and sending messages I want to have a look how my application will look like on an actual data that's why I'm generating this C data okay now let's open database CED and we are going to generate the data right here first let me delete everything from this run method and now we're going to generate two users I'm going to make the first user to be admin user and I'm going to make the second user to be regular user one is John do second is Jane do pretty creative names okay so then we're going to make things a little bit complicated again because I want to generate right now uh random users I want to generate um groups and I also to want to generate messages okay so let's start with the users besides those two users I'm going generate 10 more users okay then I'm going to generate five groups I'm going to regular do a regular four Loop and I'm going to create five groups with the owner ID to be one okay however I'm going to also put random users inside that group so I'm going to select random two to five users like users I'm going to select them in the random order I'm going to limit them from 2 to 5 I'm going to choose a Rand number right here it's going to be either two three four or five and I'm going to take just IDs of these users and then on that group users I'm going to call attach method and I'm going to provide right here in Array and I'm going to put user one inside this group and the selected users but it might be the case that this selected users can also contain ID um one there so we want to uh basically don't repeat users in the group so we don't want the ID one to be duplicated so I'm going to use array unique right here okay so we just make sure that we're we are't going to have the same user twice in the group even if it is the owner of that group okay so once we have groups prepared we are going to then work on the messages okay this is the most interesting part so I'm going to generate 1,000 uh random message then I'm going to select the messages which we are not generated for group which we are generated for um the receiver for onetoone messaging okay and the group ID has like 50% probability uh to exist on the message based on the message Factory like we did right here okay with a 50% chance there will there will be group ID or there will not be okay so I'm only selecting messages which do not have um messages the group ID right there okay so then I'm going to create conversations okay and the conversations should be uh between those messages uh I'm going to take the center ID and the receiver ID and I'm going to create kind of um I'm going to create new file while I'm going to show you what I mean okay just make this very clear let's say that we selected five uh messages like we sender ID 1 receiver ID 2 let's duplicate these several times sender ID and those are with one toone messaging sender ID one receiver ID three then we have sender ID 2 receiver ID one and then we have uh send ID one and receiver ID two again okay so here we can detect two peers um and we need to create two conversations okay so inside the first conversation like conversation one we're going to have user one to be one and user two to be two okay it can be vice versa as well two and one doesn't matter okay but okay let's let's just type one and two in the second conversation will be one and three okay so B basically I want to identify all those peers like this is the same for me so this this and these all these three message is the same for me and it is a single conversation for me so I want to create a single conversation for me so what I'm going to do take those messages and create a group okay I'm going to group those messages by sender ID and receiver ID but I'm going to sort this sender ID and receiver ID in ascending order so that this message and this message to be in the same group so after my grouping this is what I'm going to have so I'm going to have a group with one two this is the uh conversation and it has the following messages like this is its message this is going to be its message and this is going to be its message this is the first group and we're going to have second group 1 three and this is my this is going to be my second group so I'm going to do the following grouping because I'm going to sort the sender ID and receiver ID by ascending order and um whenever I sort them first I I'm going to have one and second I'm going to have two okay let's continue right here and I'm going to work on this group bu method I took those messages those messages I'm going to group bu what I just mentioned so I get the message and I'm going to use a collect method to create collection and I'm going to pass an aray right here and I'm going to pass sender ID and receiver ID okay then I'm going to call sort right here just to make sure that um if sender ID is two and receiver ID is one when never ever I sort this I'm going to have one and two this is obvious okay and then I'm going to implode them by underscore so that my final key of the group will be 1core two so this is going to go right here key of the group okay imployed by underscore and now I'm going to have the following type of um conversations right so then what I'm going to do since I have those groupings I'm going to continue and I'm going to create a map of this grouping okay I get this grouped messages so this is an array I got all these messages okay cool then I'm going to return a new array for and this is going to be the array which is going to be inserted later in the conversation table okay so then I start iterating over this so my grouped messages will be this okay so I take yeah and I'm going to call finally after map basically because this map will uh return um associative array type of object we we will have keys and we will have values but the values will be whatever we return from here and I'm just interested in the values so I'm going to take these and return something from this I don't C the key i c the value and this is what I'm going to return from here I'm going to return the user ID one which is a column of the conversation and I'm going to take the first group message which in my example will be this first group message is this and I'm going to take its sender ID so I'm going to take this and finally when I take that part let's put this down below I'm going to convert the following uh into something okay let's just remove this area so I take the first messages sender and I'm going to return this inside um user ID one so this is going to be one then I'm going to have user ID 2 which is going to be the first message receiver ID so this is going to be user ID 2 and I'm going to return last message ID which is going to be the uh last messages ID so the ID of that message so this is obviously sender ID this is receiver ID but there's an actual ID which I haven't mentioned here I haven't written here but obviously it exists in the database so we're going to take that ID as a last message ID and I'm going to also have created it and updated it to be new date um inside the conversation table once I have those conversations prepared I can already execute conversation insert or ignore and I'm going to provide this conversations which is collection to array okay I know maybe I'm again complicate over complicating things um but this is in my opinion also a very good challenge to understand how to work with collections how to work with group pi and map methods um in laravel okay again the creating such type of complex data uh in the database s is not mandatory definitely but it is simp thing um which I wanted and I also wanted to share with you and maybe you get some benefit out of it but finally we're going to have very nice uh structured nicely structured data prepared which uh we can use just to render um on the front and side okay also make sure that at the top of the file you include the proper classes like we need carbon we need up models message we need conversation and group models right here once we execute this once we actually have these prepared we're going to execute this so I'm going to open the terminal and I'm going to execute PHP Artisan migrate okay and uh let's actually execute migrate fresh with Das Das seed to execute seed data as well it's going to drop all the tables execute migrations and then it's going to execute City now let's see column not found unknown column sender ID in we CL CL yeah uh the mistake is right here when I select the user I select those users which has sender ID doesn't equal to one this is actually wrong I should have selected where ID doesn't equal to one this is the problem so let's bring up the terminal and I'm going to reexecute that part okay now let's have a look it's going to sit the database we are sitting one 1,000 messages so it takes 2 3 seconds and the seing database completed successfully now I'm going to open browser and I'm going to go in PHP my admin reload the table and we're going to see a few more tables right here we see messages we see message attachments we see migrations um what do we see we see groups and group users but let's open these messages and we have bunch of messages with sender ID and receiver ID uh some them has this group ID as well this conversation ID is something which uh we don't use at the moment maybe we can use this later um but we don't use this at the moment and if we check uh conversations we're going to also see um several conversations added right here and I think this is awesome actually there are totally I think 12 conversations um here we see the conversation ation from user one into um user one again which is not actually correct but again um that's not very big issue because we have this data for only seeding purposes we're going to take the data right now and display in our application I'm going to start with the web PHP where we have all the roots defined and I'm going to modify these roots so first of all I'm going to remove them um function right here and on slash I'm going to render home controller which doesn't exist at the moment home controllers we can use home method and I'm going to give this also name dashboard the dashboard root name already exists right here but this is something which I don't need so I'm going to remove this however what I need is this middle wear part okay so accessing the homepage right here should only be allowed if the user is authenticated and verified so I'm going to use right here rot middle year we don't need Arrow here then I'm going to call group passing function and let's take this root and put this right here okay so just like this now let's bring up the terminal and I'm going to create home controller PHP artisan make controller home controller let's hit the enter home controller was created so let's open this let's create public function let's call this home and I'm going to return inertia passing oh we have to create however that um home react component in the web PHP I'm going to remove this application and inertia Imports it is not necessary and I'm going to import this home controller right here now let's open our application and try to access home it uh redirects us to the login page because we cannot access homepage without without authentication and I'm going to use now uh the user which I created in the database Cedar John example.com with password let's let's try to login I think I made a mistak in the password okay let's have a look in the database C John at example.com with password okay h what's going on okay something is wrong uh what is wrong I think uh home jsx doesn't exist and that might be the reason so let's go and find dashboard jsx because I want to modify dashboard jsx that is not necessary anymore I can completely delete this uh welcome jsx uh I'm going to lower the volume so okay let's rename this home jsx uh let's call this home here okay now let's try to log in okay we must have some error okay now this makes sense let's open the console and we're going to start the V server so we have the Artisan server running and we should have the V server running as well mpm run Dev okay awesome now we should be able to log in okay here we go so this is our homepage and we are able to access that um dashboard we see errors in the the console because uh that happens because somewhere in our code we have uh the connection to websocket server which um and our websocket server the Reverb server is not yet started okay we're going to come back to this uh but this is what um we can leave this we're going to now create new layout which is going to be I'm going to call this chat layout and I'm going to have in this chat layout on the left side all the active conversations like the users and groups and on the right side I'm going to have the M main area for messaging section so uh let's leave these developer tools and we can also find the place where this connection is made I think this connection is made in upjs uh let's find it or in bootstop Js here we have this Echo right in JS we have this Echo JS and this makes the connection to the webset server and at the moment we don't need that so I'm going to completely uh let's actually completely comment this Echo okay we don't want this okay good now let's create new layout under layouts I'm going to create this a chat layout jsx okay uh const export um default const chat layout just like this uh actually we should create cons CH layout export default uh chat layout okay and from here we are going to return jsx okay and we're going to use that chat layout then in the home jsx but in this chat layout we're going to get couple of more things um not not only uh this header and children and uh user so instead of getting the props from here this is what I'm going to do um I'm going to just accept children here and I'm going to use the inas use page so const page equals use use page and not user page but use page and that use page needs to be imported from inertia JS react okay then I'm going to Define a variable called conversations and that conversations needs to be uh users as well as the groups combined together so I'm going to take this from page props conversations okay and I'm going to also print this right right here conversations is the following conversations okay I'm going to also take what is the currently selected conversation okay selected conversation which might be null if we open the homepage nothing will be selected okay when we select one conversation from the left side then the selected conversation will be set selected conversation okay and let's just print this selected conversation as well just like this okay from here we're going to return that jsx at the moment I'm just going to change this into div and here I'm going to have just chat layout and here I'm just going to put another div with children right here then let's go into home jsx and here I am using this authenticated layout and I want to use my chat layout instead of authenticated layout however the authenticated layout should be parent layout of chat layout basically whenever I open the homepage I'm going to see the chat layout but the authenticated layout will be used whenever I want to check my profile page so this is going to be the authenticated layout which which will contain the header as well okay but for the homepage we're going to see the chat layout so let's go into chat layout again and I'm going to use authenticated layout authenticated uh layout just like this okay I think we can take that import from here authenticated layout and put this right here but the authenticated lay out expects uh user and header props okay the header is basically this area which we can remove we don't need the header however we need to pass the user because that user information display is displayed um right here however if we go in the authenticated layout where the user is expected right here we can even change that section okay and what we can do is just write const uh user or let's just take the page const page equals use page then const user and that use page needs to be imported from inertia JS react just like this and the user will be page props out user and how do I know that the that the user is Page props out user so whenever we open handle inertia JS request so here we have this share method and whatever is passed right here inside this return array is a shared prop for every page so in this case props every page will have prop out and that out. user will be the current user and just like this we have this user and we can remove that user from here completely now if we go uh back into let's close this handle in your JS request if we go into chat layout we can use this authenticated layout we don't need to pass the user we don't need to pass the header we can save this and let's just reload the page this is the dashboard okay uh we need to change that authenticated layout into chat layout just like this however that chat layout needs to be imported from layouts okay let's remove this chat layout doesn't need the user prop it doesn't need header it doesn't need that head as well which is basically a inertial JS title tag we can remove this now let's have a look okay as you see we have this authenticated layout we have this chat layout area and we have this Ur logged in which is the current homepage area Okay so this is going to be the area where we will render the messages the selected area will be all our messages I'm just going to remove this and just type messages like this and the chat layout will be the left sidebar where we're going to have all our conversations now I want to demonstrate something very important to you so that's going to be very very important so be uh attentive right here so we navigate between Pages the homepage and the profile page page changes and the authenticated layout is every time recreated okay if we go right now in authenticated layout let's remove this unused statements I don't like them so if we go in the authenticated layout and I'm going to use right here hook unmounted use effect we need unmounted hook and I'm going to type console log authenticated layout um we can print the user as well but doesn't matter so authenticated layout mounted we don't want the user okay so now the authenticated layout is used let's pay attention Let's uh change these logs into info okay if we go on the dashboard authenticated layout mounted is called then we go on the profile page and the authenticated layout mounted is called again even though that the authenticated layout section doesn't change okay so the header the authenticated layout contains the header area right so why it is mounted why it is recreated every time when I open the page which has the exact same layout so in inertia JS uh inertia JS there is a section uh called persistent layout section you can find this in the documentation now but what this gives you possibilities to create persistent layouts across Pages like if we open for example profile uh I think edit js6 this is is which has this authenticated layout okay what we need to do in in this case to assign that layout to the edit page slightly differently so in this case we can remove that authenticated layout from here collapse this we can introduce fragment like this uh and we don't need to export that function okay we can just create on edit uh or we we can just create normal function doesn't matter and then on that edit layout we need to Define our own layout so now this um co-pilot uh suggests me the following code which is very close what we want so in our case we are going to return the following layout okay so in this layout we're going to accept page and we're going to take that page and pass it as children to the authenticated layout right here or we can re render it even right here so in our case the user that needs to be passed to this authenticated layout will be page. props out user however the actual profile information what we see right here that needs to be rendered right here so in in this case we're going to pass children to this authenticated layout to be age so let me save this and we're going to have a look in the browser and um here's from the inertial documentation if you go in the pages okay and scroll down below creating Pages creating layouts you're going to find right here persistent layout section and and I always recommend to read more about the specific things from the documentation okay now if we open our application reload this page we are going to see an error I think what we missed is returning the edit export not returning but export export default edit let's have a look here we go okay now let's check this so we have this authenticated I I think we need to Define that in the same way on the homepage to make that persistent layout properly working so in this case the authenticated layout mounted was called if we go in the dashboard it is still called if we go back into profile it it's going to be still called because we need to open home jsx and we need to Define that persistent layout right here as well so let's create function then on home layout we are going to return we are going to return um something similar let's just take these put this right here we take instead of profile we don't need actually the header so this is the homepage and I'm going to what I'm going to do is just take that chit layout and I'm going to put this chat layout right here okay and the children needs to be the uh past in chat layout not in the authenticated layout we can show we can put this on a single line just like this and the chat layout is something that is going to be that's going to accept the children let's remove this so let me just create fragment messages save everything and let's have a look okay authenticated layout mounted let's go into dashboard and we have an error we need to yeah we need to export default home let's see that authenticated layout is not defined we have to import that authenticated layout let's duplicate this sometimes visco doesn't import things for for me so I have to do this manually let's reload the page now I see the layout twice why do I see this because in the chat layout we are using this authenticated layout and we don't need to do this so I'm going to replace this with fragment okay and I'm going to remove this authenticated layout right here now we see the Chet layout and the mounted is called let's go into profile and you see the mount it is not called anymore that preserves this default layout State however pay attention to the thing that the profile dropdown stays open so there's an advantage of having the persistent layout for example in the layout in the persistent layout we might want to have uh some process running like the player music player might be running okay and every time when you navigate between Pages you don't want that player to stop that's why the persistent layout will help you and will keep that player uh running however in this case we see that the profile stays opened okay so yeah sometimes it's it's you need to know where to use this so in this case if the profile gets uh opened we have to close this manual okay so in this case I don't really I I just wanted to show to you that persistent layout because this is very important and if you have that ability to use these persistent layouts this is going to be really good good and you you should use it if you have that ability however in this case I'm going to return that persistent layouts for the profile page so I'm going to basically undo the things which I made in the profile page but I'm going to leave that for the homepage because this is how I want it okay on the homepage I'm going to have different um roots for different user and every time when I select a different user different conversation that's going to trigger the uh layout R rendering if I don't have these persistent layouts and I want to explain the everything uh what I mean so bear with me just follow with me okay so what do we have here so now we don't have personent layouts for profile we have that for the uh main page homepage um and now I think we need to focus on rendering the conversations however I'm going to make the entire chat to work in the dark mode okay so I'm going to provide right here dark mode to be based on class and I'm going to open up blade PHP which is the layout and I'm going to provide right here class to be dark and just like this we changed our application to work in dark mode and we have the text dark right here as well even before that I'm going to already use now Lal Reverb and show you something really cool let's expand this area because I'm going to need this so I'm printing certain things I'm going to remove this authenticated layout mounted because this is something I don't need let's remove this but I'm going to use now um LEL Reverb with Echo and show when the user connects to our website when the user goes online and when the goes the user goes offline okay so first we need to start the Reverb server let's remove this use effect let's bring up the terminal and I'm going to open another terminal so we have the Artisan server running here we have the mpm Run Dev running here and I'm going to bring a new one which going to be PHP Artisan serve not the serve Reverb colum start and I'm going to provide d d debug as well too which will give me more information uh about the messages so on Windows this asks me the following security Windows security alert because it's going to start listening to New Port I'm going to allow access right here now this is listening on Port at8 okay and now if I open that um bootstrap JS and uncomment this Echo from here by go in the network and go into w this is the web circuit connection and we see uh the green right here and 101 which means that the connection actually has been successfully established okay so if I simply turn off my Reverb server with control and C right here okay now the connection is not successfully made it is in pending and it's going to be failed and we're going to see the errors right here okay so by executing this Reverb start we have the websocket server running and by default if you open File by default all the configurations right here are correct okay we're going to leave this the host is Local Host Port is at8 schema is HTTP on production we're going to change these things when we deploy the project but right now we can leave everything as it is and that's going to work perfectly okay let's close this en I'm going to close this bootstrap we're going to have this imported and we need the sucket connection so we're going to leave this imported I'm going to close these unnecessary files at the moment uh I'm going to close the authenticated layout as well close this one basically what I need is the chat layout okay in the chat layout I'm going to have these conversations and I'm going to listen when the user goes online and when the user goes offline so here I need use effect to uh listen on the component Mount and whenever this happens I want to listen to a specific Channel and for this I'm going to use echo. join that join uh joins basically to a specific Channel with uh a presense Channel or it might be pronounced as presents Channel um I'm not sure but we're going to provide some key the channel name on which I want to connect okay so in this case I'm going to choose a very simple name online okay so I want to join line um I want to join online Channel and I want to listen to certain things okay and I'm going to just autocomplete right here whatever is suggested by co-pilot and I'm going to explain because this is actually correct so I want to listen to three things like here which means that whenever the other users are connected all the other users will come right here we have joining when a user joins um to a specific Channel when somebody joins to a specific Channel okay let me I'm going to re explain things so here is whenever I connect to channel I'm going to get all the other connected users right here joining means whenever somebody connects to the channel That user will come right here and leaving means whenever somebody leaves um that channel I will get the information right here so we need all three and based on all these three we can control who is online right now and who is online offline and we're going to um properly update the uh user interface so now I'm going to use this I'm going to catch every error basically right here here and let's just type console error okay this is actually good but this is not going to work let's bring up the um browser and I'm going to reload the page and we're going to see this 403 Forbidden so this happens because every time whenever I want to join to a specific channel uh I need to pass the proper authentication mechanism to be able to join that specific Channel okay and we I have to write that authentication logic in the on the backand side uh so that user to be able to join that specific channel so in this case I try to access to that specific channel the payload includes presents which is the channel type and the channel name online okay but the server returns with 43 because we don't have permission now now let's give that permission for this I'm going to open channels PHP which is located under rules so right here and that Channel's PHP is created whenever we install broadcasting so this is the default uh Channel which I don't need we can we can remove that but what I need is um actually I'm going to just change this broadcast channel I'm going to provide the channel name which is going to be online right here okay I'm going to get the current user authenticated user okay and what I'm going to do is simply return that user okay so the thing is that if I return uh true from this callback right here it means that the authentication is actually passed successfully and we are going to see 400 uh actually 200 right here so the connection to the channel happened successfully if I return false right here so then we're going to save forbidden if the user is not authenticated we're going to receive null right here and then we're going to return null right here and null basically means that the user uh doesn't have permission okay so as soon as I'm returning something not falible even if I return one it means that the authentication is passed successfully so I'm going to return if the user is available the user itself I can return true as well but no I'm going to return user because whatever I'm returning here then I can get this in the chat layout right here in the joining in leing and in users as well so if I return true for example from here then whenever I join to the website and right here I get right here I get already connected users I'm going to have uh array of Boolean true okay so I'm going to have like if there are 100 users connected I'm going to get a array of 100 True Boolean values inside there which is not what I want right so I'm going to have true not true but I'm going to return user from here and then I'm going to get the users right here in the user right here so now let's go in the console and have a look if I reload the page I get here and inside here I have this array and the array contains that one user and this is exactly what I want however if again if I return true then we're going to see array of true okay so actually in this case the best will be if we just not return the user but return the user resource okay for example the email verified that is something kind of sensitive information which we don't want to display to the user to other users because right here I'm going to get all the information I'm going to bring up the second browser as well I'm going to start now guest session I'm going to connect to this and I have the second user Jane example.com with the same password okay here we go let's bring up developer tools right here as well as well and now let's reload the page and here we see now two okay one is John second is Jane and now I am Jane but I'm able to see John users some sensitive information okay like email verify dat for example obviously you can always hide that information from user PHP uh hidden right here but it is always very good practice whenever you are EXP posing that information on the client side through API through inertia or through websockets just put this in the resource okay so I'm not sure if we have at the moment user resource but um let's create this I think we don't have yet created user resource but let's create user resource right now and return it from here I'm going to open another terminal PHP artisan make resource user resource let's hit the enter now let's open that user resource and right here I'm going to define the properties which I want to return okay so I'm going to return the ID user ID I'm going to return the Avatar URL so the Avatar in the database is a path which should contain where the user's Avatar is uploaded and we're going to basically generate URL out of it and we're going to return turn although at this stage we don't have the Avatar upload implemented but we're going to do this on the profile page right here so we're going to put the upload right here so because I'm preparing the user resource I'm just going to put this Avatar URL right here I'm going to return also name I'm going to return email I'm going to return the date when the user was created and updated dates as well uh and you can decide which information you want to return if you think that specific information is sensitive and that should not be visible to others like for example create that okay you should not return that information okay so I'm going to return also if the user is admin or not I'm going to return also last message and last message date so pay attention to these two properties these are two properties that doesn't exist in the users table in the database okay those will be dynamic properties which will be added later so because I created user resource that user resource will be used into multiple different places okay and I am just providing right here all the necessary attributes we're going to need into different places okay does this make sense great uh if if there if you have any questions please feel free to leave them in the comment section down below okay and we have the storage facade which we need to import at the top as well right here so so now we have this user resource and we can go into channels and we're going to check if the user exists and then we're going to return new user resource passing that user and we need to go into user Resource as well and we need to disable public um I think it is static wrap we need to disable wrap with false okay what what actually does this R do let me just comment this so whenever whenever we reload we're going to see two users connected and hold on yeah the file is unsaved um I I haven't saved that so as soon as I save this right now uh you see now I'm returning user resource right and I don't have R so I expect different type of response H okay anyway no problem if you don't need this wrap I'm just going to still have this wrap because I need this into a different places but in our case now what is more important is that we don't have email verified yet for example which previously was there but now we don't have because we are only returning properties what we have right here in the resource okay this is good and now whenever we have two users connected at the moment and let's just open now two tabs this is one oops this is one one user and this is Jane user Jane and this is user John okay so let's reload right here we're going to see two users connected and we're going to see right here different type of output so we see leaving right here and then joining so whenever where is developer tools I want to show this to you very clearly okay so this is user Jane on the right side this is user John when I reload right here the connection to John is lost so we see leaving John and then John is joining again okay so we have all three uh call backs executed all three when I say all three I mean here joining and leaving okay if I'm going to clear the network if for example John logs out then okay we don't see right here leaving because we don't disconnect to the channel we haven't done this and we need to do this right here when we join to that chat layout uh online right here then we need to leave whenever uh we we do the component unmount so in this case we're going to return Echo leave online okay let's log in like John is going to log in uh let's just save that password cool here is uh Jane and let's do this for Jane now here we see two users now I'm going to log out and you see leing so leing is actually called twice but that's not big issue um and but the leaving is called for Jane and basically if we just keep tracking on the users who is joining who is leaving we're going to have all the information who is online and who is not online in the chat layout okay now I'm going to start keeping track on who is online who is offline and I'm going to Define right here a couple of States okay so const uh online users and with um set online users but this is going to be use State and this is going to be object not an array but an object online users okay good whenever somebody joins to the channel right here we're going to put this in the online users so I'm going to call set online users but right here we're going to get uh previous uh online users okay so we're going to get this previous online users uh we are gonna uh let me actually create right here up updated online users or updated users let's just create new object previous online users then inside updated users for the user ID I'm going to put the current user and finally I'm going to return from here updated users okay so in the same way whenever somebody is leaving Le I'm going to call set online users okay so I'm going to get this previous online users then I'm going to get this updated users I'm going to delete that updated users for the the user ID who is leaving at the moment and I'm going to return this updated users and I need also to handle this here properly okay so in this case I'm going to create online online users object okay and this is going to be object from entries and I'm going to pass right here I'm going to convert the users um into a map so here I get the users and I want con to convert this into a map so from entries users. map and I'm going to get this user and I'm going to return the user ID let's put this down user ID and the user object itself and I get this online users object which is an object instead of an array okay then I'm going to call set online users and I'm going to pass uh online users object right here or we can do the following as well just um whenever here is called there will be no other online users right so here the users will be the only online users they will not be any other the users so actually having this set online users object is totally fine but just make sure that we are fully safe we can get right here pre online um users as well and what we can do is return the merging of previous online users in the current online users okay just in case if for some reason there are other users also connected who was not for some reason received right here again this is not probably not going to happen and in 9.99% cases these previous online users in this specific case will be an empty object but we are not just damaging our application okay so we are just trying to be an extra safe in in this specific case okay so now we have this online users State uh right here which is going to contain always the object of the users who is online right now and this is awesome I'm going to create right here um is user online simple function and simply I'm going to return from online users user ID if that exists okay and whenever I need to call in my template if the user is online or not this is going to return either user object or null and based on this I'm going to just display the like small Green Dot if the user is online or not now I'm going to Define two more States right here next to the selected conversation I'm going to Define one is going to be local conversations okay and second is going to be um sorted conversations okay so the local conversations I'm I'm just going to change this into an array so the local conversations will be updated whenever the conversations is received so I need to uh listen let me collapse this side okay so I need to listen to whenever the conversations is changed using use effect actually that is correct just like this okay whenever conversations is changed we're going to take that conversations and put this in the local conversation okay but I'm going to also listen to local conversations so I'm going to duplicate this and right here I'm going to listen to local conversations and whenever this is changed I'm going to do something okay and that something is going to be set sorted conversations this is where I'm I'm going to do but I'm going to take my local conversations and I'm going to sort this with multiple criterias and I'm just going to uh put this right here okay so let's take these local conversations and I'm going to call sort on this okay we get two objects A and B then I'm going to compare and first of all um if the user or the conversation basically the uh conversation I mean the group doesn't get blocked right the user might get blocked and I want that blocked users to be shown at the very top at the very sorry at the very bottom of the conversation list also that blocked users will only be displayed for the admin users regular users will not be able to see other blocked users only admin users will be able to see blocked users just in case they want to unblock them so so that they can interact with them okay so first of all I'm going to check if the if both like the user a or the conversation a is blocked or if the B is blocked uh whichever is blocked um later I'm going to put this um at the bottom okay so it it so this is a basic comparison if the blocked a is more than Block B then we're going to return a which means that it should be at the at the bottom so if the uh not both of them is blocked then we're going to check if the first one is blocked then we're going to return one if the second one is blocked then we're going to return minus one okay okay after we compare that blocked then we need second comparison and that's going to be the last message date each conversation will have last message date when somebody sent a message in this conversation so if the conversation uh when I say the conversation I don't mean necessarily the record from the conversations table I mean a conversation by its meaning so the conversation might be Ono one or group conversation okay so when the conversation like one to one or group uh when they when there's a new message in this conversation that's going to be the last message and the last message date will be the date of the last last specific message so what want to do is that the last message conversation will be always at the top so here I'm doing that comparison so I'm am comparing if both have date then I return um the result of local compare um of the B compared with a okay and if the uh not both of them have the last message then if the first one has last message date that's going to be at the Top If the second has um the last message date that's going to be um at the bottom and we have also other case in which case we're going to return zero okay and just like this now we have this set sorted conversations right here and in the template I'm going to use that sorted conversation to render it right here now let me remove this part before we work on this template let's return the conversations properly so that we can get those conversations right here so for this I'm going to open handle inertia JS request and um as I mentioned right here this out is returned on every page so from here I'm going to return conversation so that this is available on every page on our main Messenger application okay so I'm going to return conversations but I'm going to check because that is returned on every page so the conversations key right here will be returned even on the login page on register page so what I need to do is to check if the out ID exists if the user is authenticated okay then on the conversation model I'm going to call a function called G conversations for sidebar and I'm going to pass the currently authenticated user object to that get conversations for sidebar if the user ID out ID does not exist then I'm returning the uh empty array obviously that conversation get conversations for sidebar doesn't exist and this is something we're going to create okay but let's also import at the top the conversation model and we're going to import the um out facade as well okay now since we have this ready let's open conversation model scroll down below and we're going to implement that method right here get conversations for sidebar okay so let's define this static function and I'm going to accept right here user and the whole idea is that I'm going to return the conversations for the sidebar but I'm going to exclude the current user so I want all the conversations uh for the for that user but I'm going to also exclude that current user okay so here I'm going to get the users from the user model get users accept user and I'm going to pass that user okay I'm going to also get the groups all the groups and together users and groups I call them conversations okay so we're going to have get all the users for that except that user and we're going to have um all groups for that specific user that should probably called just user not the accept user in this um in this variable but uh it should be called right right here in this method accept user um I will probably change it later okay so now once I get those users and groups I want to merge them together because I want to show all groups and users together and um let's say if I get a recent message in a certain group that groups goes at the Top If I get one to one message from my wife for example then the conversation to my wife gos at the top okay so they I'm going to show them together so for this I'm going to take my users and I'm going to call me because that users returns a collection of database models I'm going to convert them into a plain array with the properties what I need okay so in the map I'm going to pass a call function I get user and I'm going to return uh the result of these two conversation array the two conversation array again is something that doesn't exist we are going to create that okay so when once I get now this the result which is going to be the uh array of conversation users array of this type of arrays okay and then I'm going to call concat to concatenate this with the groups map and I'm going to do something similar right here I'm going to pass a call back in the map and I'm going to return uh to conversation array map to conversation array so now I have to implement these four methods this one this one this and this so let's go into user model scroll down below and let's implement the first method and that's going to be uh get users accept user and here we get that accept user okay so then we're going to do something very very interesting but also complicated challenging SQL quer so again stay with me uh if you need you can Poe and Rewind that specific section we're going to get all the conversations okay for the except the current user okay so I'm doing um first of all let me get the user ID of the accept user and then I'm going to select everything from the users model and I'm going to also select and later I'm going to do the join uh to messages but I'm going to select the messages message as the last message okay and messages created at as the last message date so I'm selecting everything from users and those two columns last message and the last message date okay and if you remember when we created user resource we are returning that last message and last message date okay this is something what we might need okay cool now we are going to select all all the users except current user so basically where the user ID doesn't equal to the given user ID okay after this um I'm going to do additional we okay if that user is not an admin user okay I'm going to do additional we and exclude all the blocked users so if that user the given user is the admin user okay then we need blocked users as well well so this will be only executed if the given user is not an admin user okay so then we're going to do left join to conversations in the database that conversations is a table which contains the user ID one and user ID 2 okay and we're going to do a pretty complex join so we get the user ID right here which is the uh accept user ID okay then on the qer we're going to do on we are the conversations user ID one equals to right here selected users ID so we are joining the current users to conversations by user ID one and the if the user ID one is that user's ID then this means that the second user needs to be the given user except user ID right here okay so again if the conversation user ID is uh the user ID we want to select then the second user must be me okay let's let's call this me so I'm getting those users for me for myself so I am should be at least one user ID of the conversations okay so this is the one part so if the user ID uh one is the user's ID then the conversations user ID two needs to be the user ID given user ID variable or second we okay and again we need Q own the second case will be if the user ID 2 here we had one but if the user ID 2 is what we want to select the user's ID then user ID one must be the given user ID so in this case I have the user ID I I will change this I that should be the given user ID but in 99% cases or I think in 100% cases we are just selecting the uh that everything for the currently authenticated user okay I will replace this after I finish this method okay cool now after this we have the conversations okay we have the proper conversation and that conversation uh is the conversation between the user I want to select and between me the given user okay then from the conversation I join two messages with the last message ID that conversation might have it might not have as well like if the conversation is empty it might not have um but in 90% cases or I think in all p in all cases the conversation will have should have the last message ID because it's up to us so whenever somebody Whenever I send a message very first time to somebody to my wife let's say at this stage we can create the conversation so when the conversation exists we can be sure that it has last message ID okay kind of uh but if there is a one message in the conversation between me and my wife for example and then I deleted that message then the conversation can stay in the database but there are no messages in this conversation okay theoretically this last message ID can also be not but we really don't care because we do left join okay once we have the conversation we join to messages with a messages ID equals conversations last message ID then we're going to do already order by so first we're going to order by if the users are blocked or not okay we are doing this type of order by in the chat layout as well right here but both is necessary necessary okay and um I'm going to explain why or maybe on the back hand side we don't need but we definitely need this on the front and side because whenever I block somebody I want that blocked user to move to the bottom okay maybe we can remove this from the backand side but we need this on the front and side so we order them by the blocked Ed we also order by the message this latest message created at in descending and we also ordered by user's name okay uh I think that user's name doesn't make or it makes sense it makes sense so let me explain this order by section also very clearly so blocked users should be at the very bottom okay at the top we are going to have those users who has the conversation with the latest message so recently communicated users at the top but if there are some of the users to which I have not had any type of conversation let's say in the organization there are 100 users I communicate with five of them I don't communicate with 95% 95 users at all so those users uh to those users I will not have any type of communication so those users will be sorted by the username which is kind of necessary right okay once I have that select okay it's pretty interesting and then I'm going to just uh return the result of that query okay and here I have this um quer tosql which I was testing and we can have a look at what the quity looks like okay this is pretty cool uh let me change that out ID into the given user ID as well we need to just take that uh or let me actually undo this I will do this later okay okay cool um if we go back into the conversation so we implemented this method we have to implement that um group method and we have to implement the users and groups to conversation array as well okay I think we can Implement now these two conversation area right here I'm going to return the ID I'm going to return the name I'm going to return whether uh this is group or not and obviously because I'm returning this uh from the conversation this is not group if this is user or not then this is user true uh if this is admin or not I'm just going to return the P Boolean result of this is admin created it updated it when the user is created updated when the user is blocked um what's the last message and what's the last message date um right here okay uh I think uh we don't need anything right here let's just go to the group model let's scroll down below and let's Implement now these get groups for user we're going to select everything from groups we're going to do join uh to the messages we're going to get the message uh last message and we're going to get the last message date the select right here is is going to be slightly simpler okay we're going to join to group users with the inner join and we are going to do also left join to the messages with the last message ID but we're doing left join because there might be group but there might not be message sent in this group at all this can happen 100% uh so uh in this case the message last message and last message date will be simply empty so we're going to do a we the group users user ID is the given user ID uh we're going to sort support then by messages create that in the sending and we're going to also order by group's name and finally we're going to call Q get right here and also let's implement this two conversation array we're going to return the group ID name description if this is group or not if this is user or not uh who is the owner of that group we're going to also return all the users of this group this is interesting we're going to display that we're going to also return the user IDs of that same users okay and we're going to return created it updated it the last message uh and last message date once we have those methods ready I think we we will be able to see the conversation so let me go back to user and now I'm going to change this into user ID okay and um we can call this just user cool and we can go into conversation again and I'm just going to call this user okay awesome now if we open the home undefined variable user ID right right right here because we need use user ID we reload the page and look at this we see the conversations if I reload again we see the conversations is executed couple of times um and uh those are groups and users combined together so is group false is group false uh but now this is group this is group this is group okay now it's time to render those conversations properly so we need to go into chat layout let's scroll down below and we're going to implement this template okay so I'm going to create I'm going to use tallin CSS classes completely right here so I'm going to create this div and it's going to have the flex one okay and it's going to have also with full it's going to be stretched on full width it's going to have flex and overflow heated so that div will be rendered right here in the following area okay and I'm giving it Flex one because uh the pend will have flex and flex column and I want it to be stretched on the full he okay this div then I'm going to split this div into two parts I'm going to have the first part which is going to be the left sidebar for this chat layout for the uh conversations and I'm going to have the right side right here inside which we're going to render all the messages and I'm just display children right here and in our case we're going to render the homepage content inside here in the children okay because this the entire component has display Flex uh and this one is going to have fixed width so this one will have Flex one and it's going to have its own flex and flex column and overflow hidden inside okay now let's focus on this because this is the most important for this component okay so let's give it transition I'm going to also make this responsive so that we have this sidebar of conversations right here but on small screens that sidebar will uh will be completely hidden okay and it's going to be displayed on certain actions so we have this uh transition all it's going to have the width full it's going to be it's going to have full width however above small screens so it's going to have full width on small screens but above small it's going to have width of 220 pixels and above medium it's going to have 300 pixels okay and it's going to have background slate 800 darker color it's going to have flex and flex column and it's going to have also overflow hidden okay and here we're going to have additional Dynamic classes added to it so if the conversation is selected at the moment uh and this happens for mobile okay so if the conversation is selected basically I'm going to on small screens whenever we go on the homepage I'm going to display all the conversations on full screen once I click any of them and the conversation gets selected then the whole conversation section will jump to the left side will will be hidden so in this case it's going to have minus margin left 100% which means that it's going to be completely gone outside of the visible area Okay however if we are talking about larger SC screen sizes then it's going to have uh margin left zero okay always always it's going to be stick always on the left side it's going to be only hidden for very small devices okay cool next we are going to uh render the um actual conversations and search so here we're going to have additional div which going to have Flex items Center justify between it's going to be the heading of the conversations it's going to have justifi between some vertical ping horizontal pading um text and font and let's just type my conversations right here and next to my conversations we're going to have a div uh with a button to create new group this is going to have uh create new group and the class name right here tool tip and Tool tip left are these are classes from Daisy UI okay and uh we we haven't talked much about Daisy UI or we haven't seen any components for Daisy UI but no problem we're going to have a look so this is the button on the button we're going to add text Gray 400 and we're going to on Hover change it into 200 inside button we're going to have pencil Square your icon and that icon is coming from hero icons if you search for pencil pencil is not WR like this what solid pcil okay per pencil uh this is it this is going to be it outline or s or solid uh I think we're going to use it from the um outline okay so this is going to be the icon we are going to use uh I'm going to give it also some tile 1 CSS classes with hate it's going to have inline block and margin left at two okay and we're going to have now the main section this is going to no this is going to be the search section with some padding inside we're going to have text input on key up we're going to call on search method the placeholder filter users and groups this text input by the way is um component created which comes with the breeze installation with inertia so if we go in the components in resources JS components we're going to see right here text input and this is the component which is used in the login page in the profile page and this is what we're going to use as well however we're going to give it also uh some classes overwrite some classes like with full uh and now down below let's render our conversations okay so here we're going to have um at the at the top right here or uh wait so this is the title this is the search section and this is the actual conversations and this one has Flex column with overflow hidden so this one has Flex one with overflow Auto so that's going to be the scrollable area so now inside there if the sorted conversations exist then we are going to iterate over sorted conversations and render them them properly okay so I'm going to use a component conversation item which at the moment doesn't exist but we're going to create that so on conversation item I'm going to provide a key this key is going to be uh not a pretty simple key it's going to be composite key of two things like the conversation might be uh user or it might be group and if I just use the conversation ID as a key right here it might be duplicated because I might have uh user in my conversation with ID3 and I also might have a group with ID3 so I choose right here composite key uh and if the conversation is group then I use group underscore if the conversation uh is user then I use user underscore and then after underscore I just type right here the conversation ID so finally this key right here will be unique it's going to be group underscore one and user uncore one if the IDS are duplicated okay to this conversation item I'm going to also provide the current conversation which it needs to Rend render and I'm going to also provide if the the current conversation is online or not and that online offline is basically more relevant to uh user not for group uh and we're going to handle this inside the conversation item properly but this is online function is something what we have created right here based on this online users state so we can use this directly and we are just using double exclamation right here just to make sure that we are passing Boolean okay so is online is user online is going to return either null if the user is not online or it's going to return the user if the user is online and we are converting this into a Boolean variable okay and I'm going to also pass selected conversation what is right now selected conversation to apply proper CSS classes to that specific conversation it okay and I think what what do we need um I think yeah that is all inside here in the chat layout and we should be able to see the result except that we don't have this conversation item which we're going to create right now and we also don't have um that function on search function which we're going to Define um right here so here I'm going to Define on search function I'm going to accept an event right here uh then I'm going to get the um search keyword uh is this correct this looks like to be correct okay I'm going to get the even Target value as a search keyword and I'm going to convert this into L case Okay uh then I'm going to filter the local conversations actually I need to filter the conversations and I need to set set local conversations I'm generally setting the local conversations because then I'm watching on the local conversations and updating the sorted convers ation okay I don't want to set sorted conversations because I will lose the Sorting that should happen right here that sorting is important so I will get the search and in the uh set local conversations I will set these um conversations and I'm going to filter the conversations by I'm going to only filter them by name because groups don't have email and this will give us an error so if the conversation name to lowercase includes the search keyword then we're going to include this okay so this is our own search and now let's create conversation item let's go into components and I'm going to create new folder right here call this up and inside up I'm going to create conversation item js6 so inside app folder I'm going to create all the conversation sorry all the components which I create for my specifically for my application because there are some of the components which comes with a breeze and I don't want to mix my components created by me with the components with the breeze okay so here I'm going to have that um now let's let's work let's implement this component but actually before I implement this component I'm going to create few more components because I'm going to need them right here in this conversation item okay one of them is going to be user Avatar because I'm going to display this and I'm going to call this I'm going to ex extract this into its own component like the user Avatar image user Avatar jsx I'm going to create also group Avatar js6 and I'm going to also create um user options dropdown js6 and what is this user options dropdown that's going to be uh the drop- down options for the admin user which gives admin user ability to block certain users unblock them or make them admin users okay and this going to be the drop down for which going to be rendered only for admin users not for the regular us users and it's going to be also rendered only on users not on on groups first let's import um certain things in the conversation item so we're going to import the link and use page from inertia JS react I'm going to import use Avatar I just created I'm going to import group Avatar I'm going to import user options drop down then let's create the component conversation item I'm going to export that conversation item and I'm going to add a couple of props to this conversation item like conversation selected conversation and online then I'm going to create the instance of the page using use page I'm going to also get the currently authenticated user and now I'm going to define a couple of CSS classes which I'm going to dynamically add to my conversation item so I'm going to check if the selected conversation exists in this case I'm going to add some CSS classes to my conversation item so so I'm going to check if the conversation uh selected conversation exists and then I'm going to check if the selected conversation is not for group okay and the conversation is not group and if the selected conversation equals to the given con conversation okay then I am adding um classes like border blue 500 um and PG black um with 20 basically I'm checking right here if the selected conversation is for the user and if the uh conversation is also for the user and if their ID matches then I'm adding additional classes okay and I'm going to also check the uh for the group if the selected conversation is group and if the conversation is also for group and if your IDs also match then I'm going to add the same classes then then let's create actual template so I'm going to create a link and clicking on that link should open the messages so I'm going to provide right here hre and I'm going to check if the conversation is group then the root will be chat group passing that conversation however if the conversation is for user then the root will be chat user and still passing that conversation I'm going to also add preserve state that is important that's the inertia property and we're going to pass this to preserve the state um and just uh don't look like a completely new page open okay because if we have one um like the chat user opened and we're opening second one we want to preserve the state as much as possible so in the class name we're going to provide the conversation item this is the custom class I'm going to provide the flex and the item center with gap between ping text transition all I'm going to provide cursor pointer and Border left four I'm going to also provide on Hover p with transparent um 30% and I'm going to add uh those classes what we just described right here okay based on if what is selected at the moment okay I'm going to also add additional classes like if the conversation is the user uh and if the current user is admin then I'm going to add this pting uh rights too and why I'm do I'm doing this because if the conversation is the user and the current user is admin later down below I plan to render the user options drop down what uh what we just created this component user options drop down component so in this case I want to give that um pting right four just to make uh the layout nicer so to say so otherwise I'm going to add pting right four okay then down below I'm going to also check if the conversation is user I'm going to render user Avatar if the conversation is group I'm going to render group Avatar okay and down below I'm going to also create another div and I'm going to check if the conversation is user and if the conversation is blocked then I'm going to mark Mark that div uh with opacity 50 so I'm going to make it like a half transparent okay so inside there I'm going to display now the content of the conversation like the name and the last message and date so here I'm giving it display Flex with small Gap justify between in item Center let's give it the conversation name with some Talon CSS classes I'm going to give it also overflow hidden text no WRA and text ellipses to show three dots if the conversation name is too long and I'm going to also check if the conversation last message date exist we're going to display that with uh text no WRA the last message statee and down below I'm going to also display the last message if that exists with uh the following CSS classes text small extra small actually text no wrap overflow hidden text ellipses and very down below we're going to have this uh current user if the current user is admin and the conversation is user this is the same case we are adding pading two right here then we are displaying this user options drop down right here now let's open user Avatar component and we're going to uh write the following component in the group Avatar and the user options drop down just to finalize this thing okay so let's define this user Avatar we're going to export that and I'm going to accept the propes user online and profile we're going to also uh create the online class uh actually let me explain this a a little bit further let's open the browser and let's go into Daisy UI and if we go to components and I'm going to search for Avatar yeah so this is the Avatar and this is the same avater component what I'm going to use but there's also chit bubble okay this is what I'm going to use this one more specifically and it also has I think the user online offline status actually if we search for Avatar back right here we are going to find that online offline right here okay and this is as simple as adding online and offline classes to the Avatar component in the daisy UI so this is what I'm going to do at this online and offline classes based on whether the user is online or offline but here I have three states if the online is true I'm going to add online if the online is false I'm going to add offline but if the online is null for some reason okay I'm going to leave this as empty I don't add this at all okay I'm going to also accept right here a profile false H I'm going to use the same user after component in the profile section as well when I implement the profile upload okay so if it is for profile I'm going to give it very like a much larger width than I'm using in the conversation okay so this is going to be the size class now let's move on the template and the if the user Avatar URL exists we are going to render this as an avatar and we're going to render it in a similar way like it is displayed right here okay but there are the Avatar placeholder approach as well where we just provide the uh text the first letter or first two letters and we can display Avatar like this so we're going to handle both so if the Avatar URL exists we're going to add this chat image we're going to add Avatar and the online whether this is online or offline U inside here we're going to have this round full basically I copied uh some of the classes from the daisy UI this rounded full for example this this whole template you can get this and you can customize this okay round it full and with eight in our case and we're going to provide the image right here as well however if the Avatar URL doesn't exist we're going to get the placeholder we're going to assign this placeholder class inside here we're going to have the background gray text Gray 800 round it full and inside here we're going to have this first letter of of the Avatar okay uh and and we're going to import the what do we need to import I think we don't need to import anything okay sorry okay let's go in the group Avatar okay and here we're going to import the users icon and then let's generate this group Avatar it's pretty straightforward we're going to have this Avatar in placeholder class we don't have online or offline here we have something very similar and inside here we have this us users icon okay we don't accept any props right here let's go in the user options drop-down and this is a bit more complex and for a drop down we're going to use uh the Headless UI drop-down component if we go in the uh headless UI and if we go into menu drop down this is it okay so this is the drop down we're going to use something similar but the power of headless UI is that we can have any styles we want so if we go in the code so we're going to see there are a couple of headless UI components like menu transition uh we're going to import something from react like fragment and um this is something from hero icons so the main idea is that we're going to create this menu component okay inside here we're going to have this menu button and we're going to have transition with menu items and the menu items contains menu item one or two or as many as you want and then we can style them will with the tiwi CSS classes okay I'm going to do something similar right now okay so I'm going to create menu with some tyon CS classes inside here we're going to have menu button okay the menu button will have its corresponding um Talon CSS classes we're going to have this ellipses vertical icon and you can find this icon in uh this hero icon section ellipses vertical icon this is the icon okay so then the uh we're going to uh create this transition with the proper props that is described in the documentation I just copied them okay you can copy this from the same headless UI documentation from here then we're going to display menu items and the menu items needs to have position absolute right and it has margin top and fixed with rounded and so on okay with Z index as well inside here we're going to have menu items we're going to have two menu item the first one is going to be for the blocking users or unblocking and here based on whether this menu item is active or not we're going to add additional classes to this and it has group and flex and with full and item Center inside here we're going to have um icon as well and text let's put them and if the conversation is um blocked at so if this is a user and it has this blocked Ed we show this lock open icon with unblock user if you search for lock lock open icon this is it so unblock user however if the conversation is not blocked we show lock closed icon this icon to block the user okay let's focus on the second menu item here we're going to have again two cases if the conversation is for admin users um sorry if the conversation um is already admin which means that if the current user is already admin we're going to make it as a regular user okay and if the conversation is not admin meaning that it is a regular user we're going to show make admin with different icons obviously okay and I think um the template is done we're going to focus on the uh functionality like because we have two functions right here on block user and we have a a change user role so let's define this change user role first and we're going to send the axio post request to change the user role and show notification so let's just um check first if the conversation is not for user so if the conversation is for group for example we immediately return okay if not then we're going to execute axio post we're going to send a wrote user uh change rooll passing the conversation ID and in then we're going to Simply print the response and we're going to also catch the error in the same way we're going to Define unblock user we're going to check if the conversation is not for user we're going to return then we're going to send user block unblock this is going to be the rout we're going to Define so these roots right here don't exist yet we're going to Define them and then we're going to print the result data and we're going to catch the uh error as well and we're going to obviously import certain things we're going to import menu and transition we're going to import fragment and we're going to import the following hero icons okay so I think we missed probably we missed something but we're going to see the result so far far we have been only developing um without seeing actual result so in the browser if we check errors we're going to see several errors like pencil Square icon is not defined and this is coming from the chat layout I think yeah this one so we have to import this and uh like react the vs code might import this for you so just delete few letters and hit control and space uh this is it that was imported let's reload and we probably have other errors like text input is not defined that's correct so we have to import that text input as well what else conversation item is not defined we have to import that conversation item as well and now we have different error chat user is not in the root list that's totally fine so the Imports right now actually are done but if we go in the conversation item we are using right here this um chat group and chat user okay and those roots yet they don't exist so let's open web.php and I'm going to Simply Define Dam Roots at the moment root get this is going to be user like this and I'm going to provide Arrow function at the moment simply doing nothing like this or let's just change this into normal function okay and let's duplicate this for group but we need to provide proper name names as well name uh chat for user and chat for group we need uh group right here okay let's have a look okay we don't have any more errors which is good uh these styles are not properly applied especially to Avatar uh and I think because we don't use this Daisy UI properly okay we haven't use that let's go in the daisy UI um documentation section in the installation we're going to scroll down below we're going to require that da UI in the tnd config JS so let's open tnd config JS we're going to put this right here and we have couple of more example examples I think we don't need them if we go in the config section we are going to see uh the more details about the configuration how we can configure which the day UI supports multiple themes which theme we want to choose for example and so on so in this case I'm just going to copy this entire configuration for Dy and I'm just going to paste this right here uh I'm going to set themes to be true I'm going to set this dark theme um by default let's leave this base styled uh utils I'm going to leave everything as it is right now so we're going to have dark theme let's now check in the browser and uh this is not bad okay so we have this conversations on the left side we have these dropdowns we have the avatars and all the users at the moment M are offline okay uh let me actually try to make that user online so Jane Jane at example.com I'm curious okay now look at this so we have John do which is online right here and Jane do is also online so I think this is good however I'm going to make that uh bullet uh green bullet a little bit larger so for this I'm going to open up. CSS and I'm going to put provide some CSS classes so I'm going to provide the CSS classes on the conversation item inside here on Avatar uh which is online basically on before I'm going to provide the width to be 8 pixel uh he needs to be 8 pixel as well top 1% and right 1% let's save this okay so I enlarg this a little bit um and I think yeah I think we can leave this as this okay not a big deal so I think we haven't done anything to this offline so I think we should increase that offline size as well so maybe we can provide for offline to increase its size as well okay now we have this conversation and uh the color right here is not white and we're going to fix this and also the scrolling uh happens on the whole homepage which we don't want and we're going to fix this as well so to fix that we are going to open up blade PHP and we are going to add HH screen class right here to assign it to the the whole He of the screen and I'm going to provide that H screen class and also overflow hidden and H screen to HTML element as well okay now we don't have this scrolling right here but we have to make that scrolling on that sidebar so let's observe this so this is the body I think no this is the div uh which has I think it has large width it shouldn't have that large width so let's reduce the hate actually hate so the body has 278 pixels okay so this one should not have um 980 whatever pixel so that's too much and I think what we need to to do is open up CSS and on up we are going to give it hate to be 100 VH let's save this okay now this one has 278 which is good this is how we want scroll down below now this one has Min AG screen this one has very large he and we are going to change something on it okay I'm going to search for this in the project so this is inside the authenticated layout and I'm going to give this one also H screen let's have a look okay now this one has also small size then we have this n in the main section and that main section has the large hate and it should not be like this so where's that main okay children are inside the main so I'm going to remove children from a main and I'm going to leave this as it is because its children is actually chat layout okay and the chat layout has this overflow hidden and Flex one so now this one is 200 something this one is still okay I think on the authenticated layout right here we are going to give it flex and flex column now this is it okay pay attention Okay we have scroll only on the left side for these conversations and this looks good okay I'm going to add two things I'm going to make this conversation color into white or maybe gray so let's go in the chat layout here we have this my conversations let's assign it like text Gray 200 Maybe okay not bad and I'm going to also change the border with or outline width on these bullets so outline width we go in C CSS I'm going to set the outline with to be one pixel okay so this is the online okay that's much better and now we have this left side bar bar which opens on full screen and as soon as we click on the mobile uh that should move to the left side okay and that should bring the messaging area this is small this is on large and this is actually good so this is the drop down options for the users block user Make ad me and these are groups now I'm going to start working on displaying messages and first I'm going to focus on the back hand side and create the controller with the request resources and all the necessary things and then I'm going to return those messages right here in the following area whenever we select the conversation and we're going to display those messages right here with the load older messages capability as well let's open vs code I'm going to bring up the terminal and I am going to execute right now uh command to create message controller here HP artisan make controller message controller let's hit the enter we're going to generate also store me message request class so we need make request store message request we're going to also generate message resource PHP artisan make resource message resource okay and I'm going to also generate um an event which is going to be the socket message event and that event will be used to push the information to the browser okay HP artisan make event socket message okay let's hit the enter and first we're going to start with the controller now let's open message controller and we're going to define a couple of methods right here but I'm going to also open web.php and I'm going to define a couple of methods right here and let's just remove those methods which I defined temporarily at this stage I'm going to Define five methods right here the first one is going to be to load messages by user I'm going to Define second one to load messages by group we're going to have the next one to load older messages in which case we're accept message as a parameter so we're going to load Aller messages all the messages not all but like a limited number of messages which are older than the provided message okay I'm going to explain whenever uh we create that method whenever we Implement that method okay we're going to also Define store method to store message requests and we're going to define the method to destroy the message okay I'm going to go in web PHP and I'm going to also Define The Roots like I'm going to Define get for messages by user and we're going to provide the following root we're going to provide a message controller by user and the name is going to be chat user I'm going to Define to get messages by group and the name is going to be chat. group I'm going to also Define a post rout to create a single message that's going to be store method and the name will be message. store we're going to Define also delete and this is going to be um destroy method and the name will be message. destroy and we're going to Define to get root to get older messages message older and the provided message load older is going to be the method name and message load older is going to be the rout name and we're going to import right here the message controller as well now let's go on the message controller and let's start from the top and Implement these methods okay to load messages by user yeah we have the destroy as well yeah so to um load messages by user we need to select all the messages where the sender ID is the given the authenticated user ID and receiver ID is the provided user ID ID or sender ID is the provided user ID and receiver ID is the authenticated user ID okay I'm selecting all the messages where the current user the authenticated user is either in sender or in receiver and the second user is in the opposite either in sender or in receiver okay I'm going to order these messages by latest so I want the recent messages first and I'm going to paginate and return only 10 messages at the moment okay then I'm going to return the view home jsx component through inertia helper method and I'm going to pass the selected conversation which is going to be user to conversation array we Define that to conversation array and we are using this in other place in handle inertia request middle we to render all the conversations so here I'm returning this to conversation array result and I'm going to also return all the messages which I'm going to pass into message resource um as a resource and I'm going to return the collection okay so now we're going to implement this message resource which we have already created let's now open message resource and we're going to return the proper things right here so I'm just going to return the ID of the message the message itself I'm going to return the sender ID receiver ID I'm going to return the sender object as well which is going to be this sender the user passed to user resource I'm going to return the group ID and I'm going to return the attachments but I'm not going to return like this instead I'm going to create new resource message attachment resource and I'm going to pass the attachments to message attachment resource just like this okay then I'm going to return created that and updated that as well so now we're going to create this message attachment resource but before this here I'm going to Define public static W equals null or false so that we don't have that um data um inside our resource okay now let's just create message attachment attachment resource I'm going to execute PHP artisan make resource message attachment resource hit the enter let's open message attachment not attachment but we need attachment resource okay this one I'm going to delete this and I'm going to return an array with ID with message ID with the name attachment name m type size the URL which is going to be to take this path of the attachment and convert it into a new URL we're going to return create that and update that as well we're going to also import that storage facade make sure you have that facade imported right here and we also need that public static uh WP equals false good okay if we go in the M message resource that is automatically detected because they are in the same namespace we don't need to do anything okay I'm going to close this message resource and let's go in the message controller and continue so we have this by user method ready and now we're going to implement this by group it's going to be pretty similar okay we're going to find a message where group ID is the given group ID we're going to find the latest we're going to paginate and return 50 and actually this should be again 10 so we're going to set this um the same value and then I'm going to pass this into uh the home jsx the selected conversation to conversation array whatever is the group okay and we're going to pass messages as well um message collection message resource collection okay let's scroll down below and inside load older we're going to get message okay and if that message has group ID then this means that the load all needs to happen inside the group okay so in this case we're going to select messages considering the group ID as well so in any case we're going to select messages which has created at older than the given message okay I'm going to do loading older messages by create that okay great so where the group ID of the message is the given message group ID okay I'm going to order them by created at latest and I'm going to page inate and return 10 okay in the same way if the group ID doesn't exist then we're going to find all the messages which has again create that less than the given message created that but we are going to have that different typ type of where where the sender ID is the message sender ID and the receiver ID is message receiver ID or sender ID is me message receiver ID and receiver ID is the message sender ID okay we're going to sort them by latest and get and Page n um to return 10 okay and finally we're going to return this message resource collection of all the messages okay now here we are in this store method which is kind of more most interesting so we're going to get the validated data and that from inside that data we're going to push this sender ID to be the authenticated user the receiver ID from the data we're going to take this out but it might be null as well if we are sending message inside the group right we're going to take out the group ID as well okay and we're going to also take out the attachments from the request then from the react we're going to send all that information to the server okay now we have files as well and I'm going to already create a message object providing all the data validated data plus the sender ID okay I have the message now I'm going to create attachments array okay if the files exist if that was uploaded and the files exist we are going to iterate over our files okay we're going to iterate over our files right here and populate this attachments array and finally we're going to set those attachments to message model okay let's iterate our files we're going to uh create the new directory for every attachment for every file and there going to be under attachments uh some random string I'm going to make this the directory okay then I'm going to define the model data which will have message ID which will have a name that's going to be the uploaded file get client original name we're going to have mine which is going to be the git client mine type we're going to have size which is going to be the get size we're going to have path which is going to be the public the the following directory inside the public okay and finally we're going to create that attachment okay we are storing that file to the following directory we are saving the path inside file attachments and finally we're saving that attachment in the database and we're going to put that attachment into attachments area right here then finally we have this message which contains all the message related things plus the attachments you finally what we're going to do is to put that message inside the socket but before this we are going to also update the conversation and model and uh group uh depending on whether that message was sent into onetoone conversation or into group okay so if the receiver ID exist it means that the message was sent into onetoone conversation so we are going call a method called update conversation with a message okay so that doesn't exist but the idea of that method is to update last message ID in the conversation model we're going to pass the receiver ID we're going to pass the currently authenticated user ID and we're going to pass the entire message object okay and we will implement this method um in a few moments okay however if the group ID exists we're going to update group with that message and in this case we're passing the group ID we're going to implement this method as well okay and once we did all the database related things we are going to call socket message dispatch and that socket message is the event which will tell the Lal Reverb to uh push the information to browser and tell that the message was received okay let's just return that just created message from here through message resource and let's Implement destroy as well and then we will focus on those methods as well as on this socket message because it's going to be the interesting one okay so in the destroy which is going to be the easiest one is that we're going to first check that we only are able to delete our own messages so we will check if the message sender ID is not the currently authenticated user ID we will simply return forbidden with for 403 otherwise we will call message delete and we will also return um response with empty response with status code 204 but when we delete this message we actually leave the files on the file system so what we are going to do also is to create an observer class and actually delete all the attachments which exist on the file system when the message is deleted okay so we need the message Observer so I'm going to actually create uh the message Observer so PHP artisan make Observer message Observer let's hit the enter okay that message Observer was created um and we will we will come back to this once we Implement those methods like the conversation um update conversation with message actually let's go in the conversation scroll down below and implement this method right here so we're going to define the starting method which accepts three arguments user ID one user ID 2 and message we're going to find the conversation where the user ID one is the user ID one and user ID 2 is user ID two or where user ID given user ID one uh right here it should be vice versa where user ID one is user ID two should be vice versa let me finish this uh and if the conversation found okay let's say that we find this conversation if the conversation is found then we're going to update its last message ID if the conversation doesn't exist we're going to create new conversation okay and we don't need to do anything else right here except that right here we find with user ID one to be user ID one and user ID 2 to be user ID two but here we need op opposite so user ID one needs to be user ID 2 and user ID 2 needs to be user ID one okay once we find that conversation then we updated this properly cool let's go in the message controller and we're going to open this group model and we're going to implement this as well let's scroll down below and we're going to Define that static method update group with message with the group ID and message then we're going to call update or create and we're going to pass first the search condition so we're going to update or create group which has ID the following ID and we're going to pass the last message ID of this group needs to be the message ID and that's all right here now let's go back to message controller and have a look so I think we have that message controller ready and we need to focus on two things this socket message right now and and we're going to do this message Observer properly okay so that to delete the attachments also from the file system when message is deleted and also not only attachments we're going to also update the last message ID from the group and from the conversation if that message was the last message um before delete okay this is what we're going to do I'm just trying to have a look if I missed anything I think I haven't missed uh here or the only thing what we can do is that we are loading 50 message right here we're loading 10 let's just set this to 10 and then later we can increase okay I'm going to set this to 10 to easily test the load older functionality as well now let's open socket message and this is going to be very very interesting one first of all we're going to accept right here public message message okay so we have now this um public property and we are going to Define in which channel we want to push this message okay and I'm going to Define two types of channels and let's open channels PHP two channels actually and um this is what I'm going to do okay I'm going to Define channel for onetoone communication in the following way so message. user dot then user ID 1 Das user ID 2 however just to make sure that we don't have one channel duplicated twice what I'm going to do is I'm going to make sure that the user ID is always less than the user ID one is always less than the user ID 2 okay but the channel name has the following format message. user do user id- second user ID okay then let's accept a call back and pass the validation so we're going to always accept user right here then we're going to accept those two variables user ID one and user ID 2 so I get those two variables and to validate if the current user has access on this channel we're going to Simply check if the current user ID matches either user ID one or user ID 2 so if the user ID equals user ID 1 or if the user ID equals to user ID 2 we're going to Simply return the user so we are simply allowing user to access to the channel because the channel is his own channel right so now let's define second Channel which is going to be for group messages okay and the format of the channel will be the following message. group dot in the group ID we're going to accept call right here we're going to get the user in the group ID and here we are going to check if the user groups contains ID to be the given group ID so then we return user otherwise we return null so in this case we're checking we're selecting all the groups of the current user and we're simply checking if that provided group ID is one of the user groups okay and if so we are allowing user to access to that group and receive messages in this group and one last thing here we need to import that app um modeles user and actually we can also change uh right here user like this okay awesome now let's go in the socket message and we're going to um Define channel for us so what I'm going to do is to Define channels to be um I'm thinking channels to be an array okay let's define this to be an array the very first element of this channel channels array needs to be okay so uh let's go back a little bit so here we have this private Channel and I'm going to also explain uh talk a little bit about private Channel present Channel and we also have public channel uh which is just called Channel okay so we have three channels import by default in the socket message public channel is something which doesn't require authentication so if we are about to create something and publicly notify all the users about this we can use a channel okay private channel is a private channel for which we need authentication and the P the difference between uh the private Channel and the present channel is that for present Channel we also get the information about the uh user uh whether the user joined to the channel leaving to the channel or um here as well so if we open chat layout and search for here so we have that here joining and leaving so that is common for um for present channels not for the private Channel and we also join to private channels with private method right here okay so in our case we don't need the US user information like who is joining who is leaving to that specific Channel we just need private Channel okay so now let's redo everything and here we have channels and I'm going to create new private Channel and I'm going to provide Now message key okay let me create M variable which is going to be this message okay then here we need message dot user Dot and then I'm going to take the message this message sender ID and receiver ID so we're going to concatenate this I'm going to use collect method to create collection okay I'm going to provide sender ID receiver ID and I'm gonna sort this collection okay so that whichever sender ID or receiver ID whichever has lower value goes to the first position okay I'm going to sort this and then I'm going to call implode to combine them with Dash and just like this I will have a channel which I described right here okay awesome so this is going to be uh private Channel and we are going to also we're going to need only one channel but we're going to also check if the message has group ID like this then inside channels we can push the following new private channel so it doesn't happen um actually we don't we don't need like this actually let's just return channels that's good we're almost done what we need to to do instead is create empty channels right here and if the group ID exist we're going to push inside this channel if the group ID does not exist it means that we we have a receiver ID and then we are going to push to the following Channel okay and we have to provide one additional method right here what information needs to be pushed by default if we don't provide any information all the public properties will be pushed inside socket so in our case we will push that message which is fine exactly what we want but I want slightly differently so I want let's define public function roadcast broadcast with okay and we are going to return array from here and we're going to return message but I'm going to use new message resource and pass this message okay so I don't want to return the database model message instead I want to push this into message resource and return this message resource okay this is really good I think we are done on the backand side and now we can focus on the front end side but one last thing right here so the socket message needs to implement uh one of the interfaces so let's use implements either it should Implement should Pro broadcast and that should broadcast contract basically uses Q so whenever we emit that or dispatch that event it's going to be inserted in the Q and then it's going to be uh basically pushed to the clients uh with some delay or we can use should broadcast now okay and make sure that the should broadcast now is also imported which will immediately emit this message to the browser and we're going to have much less delay as well okay depending on what type of application you are building either you can use shoot broadcast now or you can use shoot broadcast as well which requires Q you need to have q started in order to be able to use that should broadcast okay uh I think now we can move on the front end side and we can render the messages and we can also listen when we receive new message let's open home jsx and we are going to um work right here okay so we're going to accept messages here and I'm going to delete this completely and um or maybe I can just create like this so first I'm going to Define uh local state const local I'm going to call this local messages okay and then I'm going to listen on messages using use effect and whenever messages is changed we are going to put that in the local messages like this then let's work right here we are going to check if the messages do not exist we're going to show a placeholder type of message okay let's define div with flex Flex column with some Gap justify Center item Center I'm going to put this text in the center and inside here we're going to have addition text with two Excel so it's a little bit large text on above medium screens it's going to be even larger with large poding and text slate 200 please select conversation to see messages and here we're going to also have chat bubble Left Right icon this is the Talon CSS icon sorry hero icons uh this is called uh chat bubble Left Right icon this is it okay I'm going to display that icon with the text to select the conversation okay however if the messages exist we are going to display a couple of things so I'm going to display the conversation header so at the top um so right now we have some error but anyway at the top of the messages we are going to display the conversation header with the um username and user Avatar if it is group with the group name group Avatar and maybe some other details of the group such as group description number of uh users in this group and so on so we're going to pass the selected conversation to this conversation header and we're going to create that conversation header soon down below we're going to create the main scrollable area which will have the reference the messages container ref we're going to have the class name with the flex one uh with overflow yo and some pading okay inside there we're going to check if the local messages length equals zero then we display the following message no messages found okay otherwise we are going to check if the local messages length is more than zero then we are going to start iterating over our messages okay we're going to use local messages map we get the message and we're going to use the message item component which we're going to create right now and we're going to pass key and we're going to pass the message itself okay and down below we're going to render this message input so what's going to happen right here is that that section this one is going to be the scrollable the conversation header and message input will stay frozen at the top and at the bottom but this is going to be scrollable okay now we need to Define that messages uh container reference and we're going to create this using use ref so right here const messages container reference using um equals use ref and we have to import that use ref from react just like this and the user State needs also to be imported from react let still like this now let's check in the browser what error do we have why we don't see anything use effect is not defined uh that's right we have to import use effect as well let's reload the page check bubble Left Right icon this is not imported let's import this here we see that and now we don't see any errors let's enlarge this okay and here we see that please select conversation to see messages okay as soon as I click one of the conversations now we have something we have some error okay let's check the error says conversation header is not defined that's correct that's good okay so we don't have that conversation header and we also don't have message item and we don't have message input and this is something what we're going to create and we're going to put them under up folder inside components so let's call this conversation header jsx the next one is going to be message item JS X and the next one message input jsx right let me double check that I named all of them correctly message input message item conversation header let's first open conversation header and implement this so we're going to import a couple of things from inertia JS react and from hero icons and we're going to import user Avatar group Avatar and then let's define the component so we're going to accept selected conversation as a prop we are going to return the following template so we're going to check if the selected conversation exists okay then we're going to uh create the following div with these toan CSS classes with some pting and flex and justify between item Center and inside here we're going to have uh another div and Link that link will be only visible on mobile screens and this is going to be go back link like when you open the application on uh application homepage on mobile you're going to see only conversations when you select a conversation you're going to see then messages okay and from messages you need to go back to the conversations so this is going to be go back to conversations link which will have this road dashboard and it's going to redirect us to the homepage where no uh conversation is selected then we're going to check if the selected conversation is user we're going to render user Avatar that user Avatar is the same user Avatar we use in the conversation item component okay if the selected conversation is group we render group Avatar okay and then we're going to uh render the selected conversation name and if it is group we are going to also render the number of users in this group so for example if this is a group for example JavaScript developers we're going to display um 100 members okay just like this okay then at the top we need um to include this page okay okay we don't need this page yet okay hold on stay with me okay we don't need this page at the moment so we have this conversation header let's go in the message item and right here we need use page we need react markdown component okay because we're going to render the markdown as well in the message uh let's import react and user Avatar and format message date long so this is something which doesn't exist yet and um I imported this still because I am going to create this right now okay then we're going to create the component and we are going to uh get the current user from use page props out user okay and then let's start rendering this message item so for message item we are going to use uh something from this dais UI if you search for chat bubble we are going to find the following um components and we have this chat and chat start and chat end classes if we scroll down below we're going to see chat end right here as well so anyway so we're going to have this chat and depending on who is the sender so if the sender is the current user then the class is chat end we add second class if the sender is um is not the current user then the uh class will be check start inside here we're going to have user Avatar we're going to also uh display the chat header and we're going to here again check if the sender is not the currently authenticated user we display the sender name but if the sender is me if it is currently authenticated user there's no need to display the name of the sender because this is me right so then down below we're going to display time so here I'm using that function which doesn't exist yet format message date long so this is going to be a function which formats the message dates into different types and we're going to create that then down below I'm going to create another div with chat bubble uh with class relative and if the sender ID is the current user ID again then I'm going to add second class chat bubble info and again if I scroll down below we have different Vari ations for colors and one of them is info right here chat bubble info so basically the messages which are sent by me I want to be in the info color and messages received I want to stay in this color in the like default color so to say okay so if the center is me I'm adding this chat bubble info to this class and down below I'm going to use a chat message in side here I'm going to have chat message content which I need to style message properly and inside there I'm going to render my message but I'm going to pass this into react markdown component okay and just like this we have this a message item ready and before I do this uh message input let's create this um helpers file and create that format message date long so I'm going to create that helpers file inside JS folder directly so let's call helpers do jsx we can call this JS or jsx let's define the function format message date long we accept the date right here and I'm going to create a now date okay so this is the message date which might be in the past so I'm going to create now date and I'm going to also create the input date with the given date okay then I'm going to check if the input date is today I'm going to return uh input date I'm going to format the input date with the following uh format with our two digits and minute two digits so basically if the message is sent or received today I'm just going to display hour and minute nothing else no date no month no year however if the date is yesterday I'm going to return yesterday plus display the outware as well so yesterday 1435 let's say as an example okay so in else case if the input uh is this year basically if it is not yesterday um I don't handle last week or last month such type of things so in El's case if the input is this y so the same year the current date is in this case I'm just going to return day and month so as simple as that if right now is in April um of 2024 and I received the message in January 2024 we're going to Simply display 25 January that's it okay good and in else case we're going to Simply return the input date in the full format local date string with year month day hour and me in the same way I'm going to create second function a format message short date short which is very similar but with small difference so if it's today we're going to display our one minute if it's yesterday I'm going to Simply display yesterday uh without um this um hour and mint additional hour and mint and I need this in different place and I will use that and um in this case if it's a year I'm going to Simply do the same thing return day and month of the date and U in El's case I'm going to return this uh so it's it's very very similar these two functions and you can even combine passing an extra function like short or long that's going to be properly the best like if we accept right here um short and but we can call this format message date without long and right here we can accept additional argument short and we can use the same code basee just based on that argument we can provide um upend that extra hour and minute to yesterday or not uped and we can just save some time maybe I will refactor this later okay and here we have two functions is today and E yesterday which do not exist yet so let's just create them so is today except a date date uh we create the actual today and then we do the following so if the given date has the same day same date as today same month as today and same year as today then this is today in the same way for yesterday uh we set on yesterday to be um so this yesterday right now is actually now right now the current time so we set on yesterday yesterday minus one so this which right now at this stage is now now minus one day so this is yesterday then we check if the date has the same date as yesterday same month as yesterday and same full year yesterday it means that this is yesterday so we have this helpers already ready we are using this in the uh format message date long here we have this function function which actually should not be here yet we're going to obviously Implement attachments but we don't need this right now so I'm going to remove this at this stage and we have to implement this message input but that is pretty large component so for now when I'm going to do is just comment this message input and I want to render the messages and have a look so let's open the browser and see okay let's click on the first conversation we have some error we have to follow and fix that conversation header okay we have to import that conversation header let's scroll up delete two letters hit control and space the conversation header was imported scroll down below we're going to find the message item and we're going to import that as well message item was also imported now let's reload and click on the first one and we have another error selected conversation is not defined on home js6 okay here we are using selected conversation but it looks like that the selected conversation is not defined uhhuh so I think we need to Define that selected conversation on the homepage um let's just accept this right here selected convert Sation because uh we are passing them from message controller right we are passingly selected conversation from here and from here so we should be able to get them now let's just reload the page again click on the very first link we have another error uh cannot read properties of find find reading length this is coming from local messages right here local messages okay let's have a look local messages okay let's provide default values to be null for both selected conversation and messages both of them to be null so here we get the messages and put them right here if the messages exist then we render them right uh what is the issue okay local messages local messages is null messages exist right here but the local messages do not exist right here that is actually strange Okay so here when I'm setting these local messages um let's actually first print what is messages let's go in the console and let me just print messages text as well messages is actually null whenever I select something I should get messages and here I get the messages and we have the data inside messages okay because we are returning a collection uh the data contains those 10 messages and it has links and it has the meta information for the pagination as well this is good what we want is to right here instead of putting the messages we should put messages data but on the backand side we are selecting those messages the latest at the top it means that when those messages will be rendered the latest message will be at the top and the oldest messages would be will be at the bottom so we want it vice versa so we want old message the very old uh message to be at the top and the latest message to be at the top at the bottom because this is a conversation we have the latest latest message at the very bottom but we cannot change this on the backand side we cannot simply get the earliest because this is going to give me the message which is probably sent in 2023 one year ago we don't hear this so on the backand side everything is good but on the front end side we are going to reverse those messages and put them in local messages in reverse order but for extra security let's also do a check if the messages exist then reverse otherwise let's provide an empty array so that we don't have that error now let's select the conversation and voila we have something awesome so we have right here messages let's reduce the right side we have here the messag is the conversation between two users what I don't like is the color color of these texts the reason of these colors is that at the moment we have white color chosen in our application so when themes is set to True only light and dark colors are used and when we provided right here Dark theme to be dark um dark actually the Dark theme so that indicates that whenever your browser's system preference is a set into Dark theme then it's going to use the Dark theme it is actually pretty interesting thing so right now my browser runs in white mode okay and because this runs in white mode it really doesn't care what theme I provide right here so there are a couple of other themes like cupcake for example or things like this but the theme stays the same because right now my browser doesn't run in dark mode and whenever I provide this I'm telling that for dark mode use the following theme so what we need to do there are two ways first is to add data theme attributes to HTML or to body or to any element inside which you want the theme to be activated and providing dark right here we'll start using that theme and now if you pay attention you're going to see that the colors are changed and you can choose choose other themes like cupcake as we had previously and oops that should be cupcake okay and as you see the theme was changed but this cupcake is I think for um White theme so we can choose synth wave or whatever you want like the any any um Dark theme forest for example let's choose Forest save this and have a look so this is how themes are working but what I'm going to do is we can leave this you can leave this as if you prefer but I'm going to undo this and go in the talent config JS and inside themes I'm going to provide an array and I'm going to only provide dark right here okay and for Dark theme we're going to leave dark as well so basically right now I have always dark theme um in my application I don't support switching to the right because I haven't developed this part and it's going to be like this and right here if you provide forest for example it's going to switch to Forest theme and you can use that okay this is great so now we have the theme properly activated and I think we can delete all these basic things I think we can remove all that okay cool we just rendered messages and this looks great we can navigate from one page into another and the scroll position stays the same but that's something we are going to work on because whenever I scroll up for example and I switch to another user I want the scroll position to go to down right and there are a couple of other things to consider now let's open home jsx and I'm going to watch on the selected conversation because whenever I hit on something else the selected conversation changes let's remove this console log for messages okay and we are logging the selected conversation I think from chat layout let's search for chat layout scroll up and conversations and selected conversation I'm going to remove them so I don't want so many logs right here okay good then let's go into home js6 and right here I'm going to use effect I'm going to use use effect actually and I'm going to watch on selected conversation and whenever this is changed I want to take this messages container reference and change it it's scroll position um to be at the very bottom so messages container reference so the uh co-pilot suggests me the following code which is actually correct so messages container reference current. scrolltop equals messages container current scroll HTE whatever is the scroll hate okay um but just to make sure that the component component is properly rendered and we should change the scroll position only after the component is properly rendered I'm going to use set time out right here putting this um and then I'm going to do this after 10 milliseconds of the um the change basically whenever the selected uh conversation is actually changed so now if I save this and scroll up and activate another conversation the scroll position goes down okay and whenever I open new message new conversation the scroll goes always at the very bottom and we have groups as well and the messages are inside groups also available and we also see right here the group name we see how many members are inside the group that's pretty cool okay so we have the basic functionality basic uh layer not and now we're going to focus on message input which we de um deactivated and hidden at the moment from the home js6 from here now let's open message input jsx component and we're going to work right here so I'm going to import user state I'm going to import a couple of icons from hero icons such as the paperclip icon photo face smile thumbs up and paper airplane then I'm going to also assume that I already have this new message input and I'm going to import that this new message import is going to be my text area component uh for sending messages but that text area component will dynamically increase its he depending on how many new lines I'm going to type there so that's going to be a separate component okay then let's create this message input we're going to accept the conversation right here and we are going to export that so here we need to define a couple of States like I want the new message state which is going to be the actual content of the input I need also input error message State and I also need message sending whether the message right now is sending or not and based on that information we're going to also disable the send button and we might need to show also loading indicator on the button itself okay now let's define this layout so now pay attention to CSS classes so we have the flex and flex wrap and we have items start and it has small border top and Border color as well and some vertical PTY okay so inside here I have uh I'm going to have like three main components but let's focus one component at a time so this component has order two okay but if above extra small devices it has order one so it changes its order so we have also Flex one right here which means that this needs to go on uh full width okay and then we have um then we have uh on extra small devices this one has Flex none okay so on above like this goes on this takes it's available with on very small devices but above extra small devices it doesn't take that much okay you're going to understand when when you see the final result so then we have this button with ping text hover relative that's not um that complex inside here we have this paper clip icon and inside here we're going to have a file upload input with multiple and we're going to have class names with position absolute left zero top zero so basically it is stretched inside that uh button which has position relative and it has opacity zero and cursor pointer okay so this is going to be the file upload then we're going to have something very similar but photos this is going to be for photos only with the same input type file but in this case we have accept only images and down below we have so this is the first div which has order two but above extra small devices it has order one now here we have second div which is which is going to be for the input so it has order one but above extra small devices it has order two so on very very small devices it is the first element but on slightly larger devices it is the second element and it also has Flex one which means that it always takes the available width and it has this pading and uh on extra small device above extra small devices we we uh reduce this pting completely okay it also has minimum withd 220 pixel and the flex space is full and above extra small devices again this bases is set to zero inside here we're going to have Flex we're going to have that uh new message input we're going to listen on its change and set this new message inside the state right here whenever we receive this onchange from this new message component we're going to have button info button with a rounded uh left none so it should not be rounded on the left side and whenever the message is sending we show this loading uh indicator and that the spinner or loader is a component from Daisy UI if you search for loading we are going to see couple of loading Spinners right here and one of them is what I'm using right here okay so and we're going to also have this paper airplane icon and then we're going to have this send text right here and that send text gets hidden above small screens actually uh below small screen so on very very small devices like before small it is hidden but it gets visible above small so we're going to also have this input error message and whenever that input error message is available and that input error message is going to be something that we're going to set when the user tries to send an empty message or when the user chooses more than 10 files because we're going to limit the number of files to 10 and in a couple of other cases okay cool um and we're displaying this in a red color okay this is the second div and the third div is going to be for the Emojis and for the thumbs up buttons so this one has order three and above um extra small devices it also has order three it has small pting and uh Flex so here we have uh smile Bas smile icon and we also have this hand thumbs up icon okay cool now let's go in home jsx and I'm going to uncomment this and let's try to import that message input now which was imported right here and now we are going to create new component which is going to be that a new message input component so under up folder I'm going to create new message input. js6 let's collapse the left side now let's import react use effect and use riff so then let's define the component and I'm going to accept three props value on change and on send okay and let's work on the state and on the template so I'm going to Define input reference and I'm going to define a couple of functions like on input key down for example so we get an event and we're going to check if the event key is enter and if the shift key is not pressed this means that we want to send the message okay if the shift key is pressed it means to move the cursor on the next line this is how I'm going to do okay and if the shift key is not press then I'm going to call prevent default and I'm going to call this on send okay and that's going to be all so this on send is going to be the call back which will be executed and the pr will handle this so on chain event whenever we type something right there so then we do the following so in set time out for 10 milliseconds we call adjust hate so that if we um if we uh hit for example shift and enter to move the C on the new line to write something so this means that the height of the Tex area needs to be increased so we need to adjust that hate and that adjust hate will be something we're going to create right now we're going to also so call onchange so that the parent component knows what is the current value of the input okay then let's implement this adjust HTE so here inside set timeout again for 100 milliseconds we are going to assign uh for the current input we're going to give it style heit to be Auto so whenever we're giving it style hate auto this basically means that that text areas hate will be defined by its content okay so if there are a lot of content it's going to have large hate okay and after this when we assign this hate auto then we determine what is the actual height of the text area and we're going to detect this using scroll heate okay so what is the input current scroll H and we're going to add one pixel just to make sure we we are fully safe and we are setting the input current style hate to be this whatever is the scroll hate plus one pixel so using these two lines we are increasingly text area actual hate based on the content and then down below we're going to listen on whenever the value is changed and we're accepting value as a prop right here the default value whenever the value is changed we're going to call adjust hate now let's create the template I'm going to Define text area provide ref provide value provide rules I'm going to provide the placeholder as well and on key down we're going to call the following function on change we're going to call the following function on change event and we're going to provide couple of Talon CSS based classes like input with border F uh full width and and rounded none and so on okay that uh right side will not be rounded okay cool and so just like this we have this new message input ready let's go in the message input and I am going to import that new message input that should be somewhere right here it is actually already imported now if we go in the browser and reload the page we are going to see the following text area component okay and whenever I start typing ping as soon as I hit shift on my keyboard then enter it goes to the next line and the text area hate is increased you see that and whenever I hit the enter then we have right now an error because on send function is not defined is not provided but so far this is great so we are adjusting the text area content based on text area hate based on its content also that text area basically is um right now on small screens so if I um remove the right side uh I expect to see this slightly differently so let's have a look so I want that text area and those icons to be like this on I think I know the reason so I want this text area and the icons to be like this on very small devices but I don't want them to be like this on larger devices because I want all of them to be on single line to make this working I'm going to open tnd config Jes and inside theme I'm going to Define different screen break points like inside screens I'm going to provide uh for extra small I'm going to provide a new break point because the extra small break point I think doesn't exist by default so this is where I'm going to what I'm going to provide for extra small break point we're going to have 420 pixel we're going to have a small break point which is going to be 680 uh pixel we're going to have medium break point uh 76 68 pixel we are going to have LG which is going to be 1024 pixel and we're going to have XL which is going to be 12 um 80 pixel and we're going to have 2 XEL which is going to be 1536 pixel okay once we Define those break points now let's have a look now we see that those components are on a single line how I expected okay as soon as I start resizing this is still on a single line but at some point for very small screens they move the this moves at the top and this is how we control the positions if we open message input and observe this again so we have this icon this is the input and this is the uh right side icon area so that one has order one for smaller devices but order two for larger devices and just like this we change the position and we have that responsive functionality which is I think awesome so now just like this we open this conversation select in conversation we have that full chat right here and we can type or we can choose we can trigger this file picker as well okay I think this is good now let's Implement sending messages for this is we're going to open message input component let's find this send button I'm going to actually search for send as a word here we have that and I'm going to add on click event listener right here on send let's create function uh I'm going to create that function right here Con on send or let's call this send click um let's rename this as well okay and then I'm going to check if the new message the content of the new message is actually an empty so if when whenever we trim this if there are only wi spaces we don't want to send the message okay so if the new message trim is empty then I'm going to set this input error message to be the message is required or maybe I can change this please provide a message because we're going to implement later attachments as well then we can just write or upload attachments okay let's let's do like this and I'm going to also hide that message after 3 seconds so what I'm going to do is set timeout and after 3 seconds 3,000 milliseconds I'm going to set this inputter message to be an empty string okay and here I'm returning so however if the message exists then I'm going to do a couple of things first I'm going to set the message sending to be true because I'm going to actually send the request okay but before I set this message sending to be true I'm going to create form data so I can actually send the Json but I'm going to create form data because later I'm going to implement uploading attachments as well so form data then inside the form data I'm going to append the message and now I'm going to check if the conversation is for user and then inside form data I'm going to append receiver ID receiver ID to be the conversation ID and the conversation is something we get right here as a prop okay second if will be if the conversation is for group then in the form data we are going to append group ID once we do this we have this form data ready and um except attachments which we don't handle right now so let's Implement sending a normal message text message at the moment so once we have that form data ready we can set this message sending to be true and I'm going to use exos post we're going to use this root message store it's called message store let's open web.php and this is the name of the route message store this is what we're going to use we're going to provide form data and we can additionally also listen to couple of events for example we can provide some headers but what I'm interested is to listen on upload progress event because whenever we upload attachments I want to keep track on the upload progress and show the progress bar as well so what right here at this stage what I'm going to do is just print console log progress event um loaded actually let's calculate the actual progress so that's going to be const progress equals we're going to round everything so progress loaded whatever is the total size uh loaded um actually whatever is already uploaded divided on the total size and Multiplied on 100 and this this is going to give us the percentage okay so at the moment let's just print this percentage of course we're going to listen when the message has been successfully sent so we need then right here we get the response I'm going to empty this new message and I'm going to set this message sending to be uh false as well okay and we're going to also listen to catch and whenever there is some error I'm going to set in any case this message sending to be false and I think um I think that's it so we don't have that progress indicator yet inside the state we're going to Define it when we do the attachments but we're going to also reset the progress later but for now I think this is good and we should be able to send messages and Save that in the database as well okay so first I'm going to open PHP my admin I'm going to go messages I'm going to open Messages table and I'm going to sort this by ID descending so the latest message ID is 1,000 now I am going to create my first message which should be saved in the database let's reload this we need to relog in reauthenticate okay so I'm going to send a message to Jane because then from Jane's account I'm going to check that message okay let's just send hello hit the enter okay there's some error the message was on send okay we have this eror on send is not a function okay that's good we know how to fix that but what's this scroll height uh scroll he doesn't exist okay messages messages container reference scroll head okay we are going to check this in home jsx right here maybe we need to do this if statement like this if the messages current exist then we're going to do this scroll top setting scroll top okay let's go back try to open another user if you see that's error again we don't see any errors let's go back and open Jane okay now let's go in network clear Network and I'm going to send Hello message okay we have this error second error which is on send is not a function okay message input let's search for this is button on send click okay on send aha I see the reason because we sent the message not by clicking on the button but by hitting enter from this new message input right here it expects this on send but we didn't provide this on send so on send is going to be on send click function okay reload the page so that the error is gone and let's go to network and send L1 probably last time hit the enter okay we got response if we preview the action is unauthorized and the reason is um I know what's the reason the reason is inside store message request we haven't defined the rules right here and the authorize is also set to false now here I'm going to set authorize true and if you scroll down below inside rules we're going to Define couple of rules right here let's set this to True um let me make sure we first Define rules okay so we are going to Define message which is going to be nullable because we might not provide the message we might only upload attachments right so the message is string and knowable we're going to define the group ID which is going to be required if the receiver ID is not required so we're going to going to do this required without receiver ID I'm going to also provide that this group ID is by default nullable I'm going to provide that the group ID must exist inside group's table okay then we're going to also Define the receiver ID in the same way that this is required without group ID it can be null and it must exist ins inside the users I'm going to provide attachments which can be null it must be an ARR and maximum 10 attachments is allowed and for each attachment I'm going to provide that it must be a file with maximum size of this is 1 Gigabyte okay that's too much maybe we should reduce it but let's just leave it as it is right now and now let's set these authorized to be true we Define rules we allow access and now let's go in the browser let's open developer tools let's send this hello again okay now we see 200 success code status code uh the message is gone it means that probably it was saved in the database and if we reload PHP my admin we see right here hello so this is new message sender ID is one receiver ID is two we don't see group ID conversation okay no problem the conversation probably should be set but I think we don't yet use this conversation so we can leave this no problem maybe um I don't even need this conversation um and maybe we can just drop it I'm not sure right now but it's not a problem that the conversation is not set um at this stage the conversation right now is more important to have the last message ID so if we go inside conversations right now right here and find the conversation between first and second user uh right here so this is the conversation its last message ID is 1,0 and this is hello as you as I Mouse over on this you see so this is great and also that message should be emitted in the socket and that is important if we check socket right here do we see emited messages [Music] um no this is not okay we don't see em messages right here but right now what I'm going to do is listen when the new message is received and I'm going to display that message and I'm going to display this message for sender as well as for receiver at the moment if I reload the page I'm going to say this latest message hello right here but obviously we're going to do this using socket now I'm going to open authenticated layout and I'm going to listen to socket uh message received event and we are going to listen in the following way so I'm going to uh get conversations right here okay and I'm going to iterate my conversations and I am going to listen to either onetoone conversation or group conversation so I also want to open this socket message and we are going to listen to the following channels message group and the group ID or message user and the combination of uh one user sender and receiver but that's going to be sorted and um combined with Dash so here what we're going to do is first listen when the conversations going to change and the conversations is something we're going to take this from props okay I'm going to do this right now after after I finish this use effect so I'm going to listen on the conversations so then I'm going to start iterating over the conversations and the ation if you remember are everything that is on the left side um right here okay so then I'm going to generate the channel name and it's going to be by default message group and the conversation ID but I'm going to check if the conversation is for user then I'm going to change this channel name and the channel name will be message user and here we need the combination of sender and receiver but sorted in ascending order and combined with Dash so what I'm going to do is create an array right here put right here user um user one and the user basically is the authenticated user okay because I'm loading messages um for the current authenticated user sorry I'm loading conversations for the current authenticated user I know that the current authenticated user will be either sender or Receiver right so I'm taking the current authenticated user ID second second user ID will be conversation um conversation ID okay that's going to be the user ID if the conversation is for user then the conversation ID is going to be the user ID so don't get confused with uh this conversation name right here to the conversation in the database because that conversation is a different thing that is just a record in the database which connects two users together and real Al have this last message ID okay but the conversations right here which is coming which should come um from the um prop if we just check right now chat layout for example we're getting conversations right here page props conversations okay we are going to I'm going to type the same thing right here after I finish use effect so we're going to get the conversations in the same way and those conversations let's just remember are coming from the handle injs request right here and we have this conversation get conversation for sidebar we always select users groups we are uh mapping and combining those arrays together so user to conversation array is going to be this is going to be an object inside our authenticated layout right here so this conversation is going to be this subject so this ID right here is the user ID okay if you didn't remember I hope now this is clear here so then we're going to sort those two element array by um the lowest number should be at the first okay and then we can call join to combine them now we have this updated Channel string with uh user ID and the sender ID and the receiver ID or it doesn't matter like user ID one and user ID two next we are going to listen to private Channel okay echo. private um so so in chat layout we were listening using presense if we find that uh you were listening with join which is actually listening to a presents Channel and in this case we have this here joining and leaving callbacks as well we can listen to them but in our private Channel we don't need them we just want to receive to a message and then we listen to a message by listen keyword right here providing the event name and the event is the class name whatever is the class name the in our case we call the class sucket message whatever we call the class name this is the event so we are listening to socket message and we're going to get event right here and event will have whatever we broadcast uh from broadcast with right here so in this case we're going to have message so we're going to get we're going to print the entire event which will have message inside but we're going to also take that message okay and then we are going to do the follow so I'm going to use event BS approach and event BS is a type of mechanism where we have central place which will uh using which we can listen and dispatch events okay event bus is actually really common practice to exchange data between multiple components so in this case my goal is that the authenticated controller is going to emit that message has been created and inside uh home jsx I'm going to listen when message is created and and I'm going to put that new message in my local message so I'm going to append this so I'm going to use that event bus approach and we're going to create event bus file um in a moment okay so we're going to emit this message next I'm going to check if the message uh sender is the currently authenticated user okay then I'm going to return and don't continue so Whenever I send the message I'm going to get this message back through socket so if I type hello right here then I'm going to get this message by the way where's the previous hello if I just reload here it is so if I send a message I'm going to get this message through socket as well so then when I get this message I will emit this home jsx will listen to it and append this right here and nothing will happen but if the center is a different user and then I'm going to also uh show a notification that you have a new message okay so this new message notification is something where I'm going to Emit and uh we're going to also listen to this new message notification in a moment but in this new message notification I'm going to pass the user who is actually the sender I'm going to pass the group ID if this message is for group otherwise this is going to be simply null and I'm going to also pass the actual message uh If the message exists otherwise if the message doesn't exist it means that we uploaded some attachments we're going to do attachment uploading in a few minutes and then we're going to show this message shared um and X number of attachments like uh shared the if the attachment length is one an attachment if the attachment length is more than one then X number of attachments okay then down below I'm going to write a return here on this return we're going to return return a function which will be executed um as a like unmount operation of the conversations okay so whenever we start listening to these channels right here then we need to stop listening inside the Callback right here okay so I'm going to iterate over the conversations again I'm going to get the channel name I'm going to check if the if the conversation is user then I'm going to update the channel name like I was doing uh right here whenever I was listening uh to the conversations and finally we're going to call echo. leave on that channel and just like this we are listening and we also are un listening on the corresponding appropriate places okay now let's scroll up and let's define conversations right here so page props conversations oops conversations okay and the only function what we don't have right now is this emit okay we're going to create this but before I do this I want to have a look if we receive messages right here if this is printed if we receive the message let's open browser if we don't have any errors it looks like we have an error use effect is is not defined okay so we need use effect imported here now there are no more errors and we're going to send second hello right here let's switch to info let's hit enter and we are going to see something okay let's reload the page again and test if the authentication is passed okay so there are a couple of out requests sent and all of them are for the authentication like for message group as well as for the user for onetoone conversation somewhere right here we will have uh the private message between user one and two and let's also check the websocket part okay so these are the messages and whenever we send the message I expect something to receive right here let's test this okay we don't receive this message and there must be a reason for this let's open socket message right here we have this should broadcast now okay we are [Music] returning hold on I don't push this here that's the problem I just create but I I need to put put this in the channels okay that was a bug now it should work another test hit the enter look at this now we receive this sucket message so we actually received just created message right here if we check the netork work reload right here clear this up let's clear this up and send another test hit the enter here we see that up event socket message this is the event we get the entire data and right here we also have the socket message which is printed from authenticated layout from here now we get this message and we need to implement event passs properly to emit this message and then we're going to listen in the home jsx and then we are going to put that message append this test message right here now let's open JS folder under resources JS and inside JS folder I'm going to create a new file called event bus JS or jsx it should be jsx more more precisely now first let's import react after this I'm going to create context new context and this is going to be I'm going to call this event bus context and I'm going to create this through react. create context after this I'm going to create event BS provider where we accept children as a prop okay and here I'm going to define the state events State and the default value will be an empty object and the whole idea of the event state is that it's going to have key which is going to be the event name and the value will be an array of callbacks for example the message created right here is going to be the event name and whenever we start listening to that event we're going to obviously provide a function callback function that callback function will be inserted inside an array of the events inside the events object for this message created key if this is not a let me re explain message created is going to be the key it's have its own array and right here we're going to have call back one call two and so on so if we decided to listen to that message created from different components let's say that we decided to listen from home js6 as well as from layouts js6 and other components then all those listeners will come right here and they will be different colic colic one colic two and so on okay and in the same we're going to have other event like new notification for example and we might be listening this from different places or maybe only from one place and that um callback um one two three whatever is going to be that function okay anyway I think this makes um sense so now let's go into authenticated layout no we need to go in the event pass and we're going to Define two functions Emit and on emit will will trigger that event listeners execute that event listeners and on will start listening to that event so inside emit we're going to get the data uh the name and the data which we're going to pass to that emit so then we're going to get uh we're going to check if the event's name exist okay and then it means that this is an array and we're going to iterate over event event name which is array of callbacks CB is going to be the variable of the loop and it's going to be each callback and we are going to execute that callback passing the data okay down below I'm going to create on function which will accept the name and the Callback function and first I'm going to check if event name doesn't exist it means that we are listening to that event very first time then we're going to create an EMP empty array under that key inside events and then inside that event name we're going to push that call back okay so down below we're going to return function from here and that function is necessary to stop listening to that call to that event so here's how we're going to do so we're going to take this event's name and we're going to filter by callback and we're going to return and assign this into event's name so whenever this callback function whenever what is returned from this on is executed let's call this unsubscribed function okay or we if we call this on we can hypothetically call this off whenever this off function is called it's going to remove listeners of that specific callback on that specific name so in this case we filter and return only callbacks which is not the given call back okay is this clear great now um you can imagine um just to give you more context on this again if we talk about like message created okay we have let's say col one let's convert this into a Col one and COL two okay from the place where I listen to callback two if we if I decide to stop listening with callback 2 after I call it off function let's call this off hypothetically then uh inside message create we are going to only have callback one the Callback two will be removed because we are actually doing this filtering right here and removing that specific call so I want to make sure that not all listeners of message create will be removed no but only that specific call back listener okay now we have that on function and down below we are going to return the event bus context provider and we are going to provide uh those on and emit functions right here so here inside the value we're providing an object okay and inside the object we have two functions Emit and on okay and then we're going to render all the children what we expect right here inside this event pass provider and this event pass provider is going to be used later um to wrap the entire application okay down below we're going to create an export con uh constant variable to use event pass and this is going to be very simple line react use context to use that event bus context okay once we do this we are going to open up jsx and right here we are rendering this application so what I'm going to do is create use right here event bus provider okay and the application will be inside that event bus provider and the event bus provider needs to be also imported right here it is not imported I think no it is not imported okay let's import this manual because vsod didn't import that for me or co-pilot will help me event bus provider was imported from event pass file this is the file okay so this is great so we have that uh event pass provider we wrap this around so we should be able now to use this in the authenticated layout let's close these files and this file as well so in the authenticated layout uh if we scroll up we are going to use now this emit const emit equals use event pass and that use event pass is something that needs to be imported from event pass like this now we have this event and let's uncomment now this line right here to emit message created and let's uncomment this right here also to trigger the new message notification now let's open home jsx and we're going to listen to the message created and I'm going to put this listening right here in this use effect whenever the selected conversation is changed um then we are going to listen to um the message created even if the selected conversation might be null we're going to still listen to this so here I'm going to use on and I'm going to provide message. created okay and we're going to pass the function message created okay so this will return the unsubscribed function const let's call this off created okay and from here I am going to return a function and inside that function I'm going to call this off created okay I can actually call this off created like this return from here but in this case um and that's going to work right now but in the future I'm going to also listen to when the message is deleted so and I'm going to have two off functions and then in this case I'll still have to replace this with this callback function and call this off created and later I'm going to have message deleted as well so let's leave it like this okay once we start listening to this message created we're going to Define that message created function so at the top I'm going to Define const message created we accept this message right here and we are going to do the following okay we have couple of cases so one case is that the message uh is not the uh there is no selected conversation like we are on the homepage let's open our application let's save this and reload we have an error on is not defined that's right so we have to import uh on using event pass and this use event pass needs to be also imported now we shouldn't have any errors and we don't have okay good let me enlarge this okay now if we are on the homepage we don't have any conversation selected right so in this case we just need to display a notification and we are displaying this notification from this authenticated layout from here new message notification we also need to update this latest message uh Tex and date right here which we're going to do um and basically from the home jsx we don't need to do anything so we cannot put this message in the messages section because there is no conversation selected also if I have conversation selected with another user and Jane do is sending me a message I should not also do anything so here inside this callback message created I'm going to do the following check so if selected conversation exists okay and if selected conversation conversation is group and if the selected conversation ID equals group ID okay then let's just remove this I'm going to format this so we have the three checks if the convers is selected if it is group and if it if its ID matches the message group ID it means that we receive a new message inside the group okay in which case I'm going to call set local uh local messages we're going to get the previous messages right here and we're going to put that message not in front we're going to put this message at the end of the previous messages okay so previous messages will be everything what we see right now we're going to get this new message and put that new message at the very bottom of the current messages array okay this is good now we're going to do something similar for user so I'm going to duplicate this and I'm going to check if the selected conversation exists and if this is uh for user and if the selected conversation um ID equals message sender ID oops sender ID or if the selected conversation ID equals let's use um let's use two equality because sometimes this message group ID might be string or this might be string and let's use two equality in all cases or if the selected conversation ID equals message uh do receiver ID and we're going to put that this these two checks in a parenthesis okay so if the selected conversation exists if it is for user and if the selected conversations ID which is the the user ID if the user ID is either sender or receiver then I'm going to get the message and put this inside local mage messages okay now what I'm going to do is uh bring up guest profile we are going to open Local Host Port 8000 and we are going to log in using Jan example.com with our password and joho is actually online so we're going to send message from Jane to John let's reduce this it doesn't reduce more okay so let's send message to John but we're going to uh select Jane right here so that we want that latest message to be immediately displayed right here okay so hello John let's hit the enter okay so right here in the John users console we receive an error set low low cut okay let's typle set local messages good okay let's reload make sure the message is gone um okay now let's send let's close this let's send this message hello there's one hello John we should receive a second hello John right now or just hello hit the enter okay look at this so we have that message message right here hello message and this is awesome one thing which you probably noticed is that the scroll position doesn't move to the bottom so if I send another message like test one then I send test two hit the enter so we see that these test one and test two messages are not visible because the scroll moves up and we have to manually scroll down okay so this is something we are going to fix and this is also something which is connected the managing the scroll position is also connected to loading older messages so we are going to implement load older messages and we are going to also take care of um putting the proper position okay let's test if the message is received from Jane uh from uh John to Jane like hello let's hit the enter the message was received here and we we see this message right here here as well now let's Implement loading older messages okay whenever we scroll at the top we're going to hit at some point the edge of the scrolling area and in this case I want to load messages okay and how we are going to do this what is the whole idea of it if I move this scroll a bit down like in the middle okay so we have couple of variables and we want to calculate those variables and uh then save it and then restore it so basically what we want is to calculate the scroll he okay let me show this on on whiteboard so basically we have the following D container okay so this is what you see right now here we have this JN do written and here we have these messages some of them is sent some of them is received and that basically has this scroll uh part somewhere right here okay the actual message content is much larger so behind the scene what we don't see there are messages above and there are messages below okay but imagine that we have that small scroll right here and if we move this up then the top size will appear top messages will appear if we move this down then the bottom messages will appear okay good so what we want is to calculate what is the entire scroll heit so this is going to be scroll he we also want to calculate what is going to be that area which is the scrollable area Okay so this area and this is going to be like client hate or offset hate let's call this offset hate okay and we also have have how many pixels it is scrolled at the top right here okay so this is going to be scroll scroll top so we have those three variables which we can get from the Dom element and what we want to calculate is this scroll bottom area okay we can call this scroll bottom and this is something that has a question mark this is some something what we don't know because uh everything else like the scroll hate uh that scroll hate or offset hate or even the scroll top is something which can be easily detected not detected even we can ask a Dom element which is it scroll he and we can easily get those things okay why do we need this scroll button because whenever we have um scrolled at the very top and when whenever we load more messages okay uh let me show this to you right here okay so we have a very long list of messages let me actually do it smaller so L this is a list of messages but only visible area is this part okay and the scroll reaches right here the edge in which case basically this moves down and this image what you see right now is converted into something like this okay so there are more messages below here and this is the scrollable area now as soon as the scroll hits right here there are no more messages visible at the top somewhere right here no more messages there so it means that we're going to load more messages whenever that scroll position hits that the top Edge the scroll top position position what we just described right here that scroll toop position right here is basically almost zero so that scroll top right here is going to be almost zero somewhere let's write in almost zero okay in which case we are going to load more messages and as soon as we load more messages uh the scroll toop position if the scroll toop position stays zero then the scroll area will immediately jump to the top and it's going to load even more messages so what we want is that whenever that scroll position hits that top uh Edge whenever the scroll position is almost zero we want to calculate what is the scroll bottom position how much is this okay and whenever there are new messages loaded and these are new messages what you see right now whenever new messages are loaded did based on the scroll button based on this based on the offset uh scroll hate or uh offset hate of this element and based on the entire entire scroll he we are going to calculate what should be the scroll top position because we're going to update the elements scroll top position and we are going to make sure that the last message what we see right here the last message is going to still be the same one and whenever we that scroll bar hits that edge and new messages are loaded right here it does not change the view right here the messages will be loaded at the top and we can obviously scroll up to see those messages but that scroll should stay right here and all the messages should stay as they are okay and we have to do this it doesn't happen automatically so we have to take care of this now let's open home jsx and I'm going to scroll at the uh area where we render these local messages and here I'm going to put a div with reference load more intersect so this is going to be a div and whenever our scroll position hits to that div we're going to observe that then we are going to load more messages okay let's scroll up and we're going to Define so right here load more intersect okay good now we're going to Define load more messages function as well so whenever the scroll hits that to that position we're going to load more messages so this is going to be a standalone function and here I'm using this use callback so this use callback caches this function and it's going to recreate that function every time local messages is changed so without this use callback I actually had issues because of closures and the variables used inside that callback function was actually preserving their older State especially uh local messages we we are preserving older State and I was not getting the result I wanted so now local messages is the variable I need to watch and whenever local messages is changed I want to recreate this function and load more messages will be recreated every time whenever local messages is changed cool now what we are going to do we're going to find the first message in the local messages area and the first message is going to be the message at the very top the very first message when we scroll up right here this is going to be it the very first message then we are going to uh make the axus request to load Aller messages and pass that first message ID okay the back end route is actually ready we had already done we can find this in the message controller if we search for uh load older here it is we accept the message and if it is a group or if it is a conversation we select older messages limit with the 10 and return them good we're going to also listen whenever we get the result and we're going to get the data and that data is a collection and it's going to contain uh that data will contain data links and meta as well so what we are interested is the data so what I'm going to do is check if the data data length equals zero it means that the load older actually did not load any new messages okay it means that we hit very first message of this conversation there are no more messages to load in this case I set this no more messages true that's a state local state and I'm going to Define this local state right here but let's assume that it is already there and defined okay as soon as there are no more messages uh we set this and we also return and don't continue however if there are messages we are going to now calculate what I just described okay how much is scrolled from bottom and we're going to save that scroll from bottom a position in the state as well so where we're going to get the get the messages container um scroll hate which is going to be a large hate so in this case it's going to be all the messages what we see right now what are above the visible area and what are below visible area all those messages and it's going to be pretty large if we inspect this right now or maybe we can inspect the second browser like this one if we inspect this uh let's go up this is 400 this is not uh let's let's have a look this is 200 which is actually the visible area and inside here we have this 900 pixel okay so this is the um scrollable area and this the the scroll he in this case even though that the Overflow y Auto is added right here the scroll he of this element will return that 900 something okay on this element okay great so we get that scroll he we're going to get also scroll to position how many pixels it is scrolled at the top we're going to get this okay and we're going to also get what is the client HTE which is what is the right now the visible area Okay this which is 286 pixel so once we have those three variables as I described we are going to calculate scroll uh from bottom okay and this is going to be entire scroll he minus the scroll top whatever is the scroll at the top minus client hate so the entire hate minus whatever is scrolled at the top and whatever is the visible area so that going to leave us what is at the bottom so we have this scroll uh from bottom and I'm going to also save this in a state set scroll from from bottom and we're going to Define this right here okay so here I'm printing this scroll from bottom and I'm also setting the same value inside this set scroll uh from bottom actually I could use this TMP scroll uh from bottom and put this right here maybe I I I can refactor this later okay so I'm going to also set local messages because if the data is length is more than zero then it comes right here we save the scroll position and we're going to also put there are more messages so in set local messages uh we're going to get the previous messages we're going to get the data data which is going to be the loaded messages and we're going to reverse them because those messages will be returned by the latest message at the Top This is how we return from backend latest at the top and whenever those messages are loaded we want the latest always the latest mess message to be at the very bottom so we get those messages returned reverse them and put in front of the current messages and just like this loading more will display more messages now what we're going to do is uh create a proper place from which we're going to call this load more messages okay so the at the bottom of this use effect we are going to create new use effect we're going to listen to local messages okay whenever these local messages are updated this is what I am going to do so first of all I'm going to check if the messages container reference exists and if I have to restore my scroll position basically right here whenever we set these set local messages right here this will trigger the local messages update and the following function will be executed so I need to take this scroll from bottom which I saved right here okay and I'm going to recover that scroll from bottom obviously if the container exists okay so in this case we're going to calculate calculate the scroll top because the scroll top is how we can control the scroll position we cannot control uh with scroll from bottom we we have to calculate the scroll top and this is going to be the entire scroll he minus the offset head and minus scroll from bottom okay we have this and we updated the scroll um scroll top coordinate okay down below we're going to also check that if there are no more messages okay we don't need to do anything else but if the no more messages is actually false so it means that if something is returned from here if nothing was returned from here no more messages will be true but if something was returned from here no more messages will be false and that if will not be satisfied so we have to continue down below okay whenever this happens and we get new messages we are going to listen to the um scroll position and whenever we hit that div what we described right here load more inter then we are going to Lo call this load more messages so in this case I'm using this intersection Observer okay and which we we get right here entries and on that entries we're going to iterate and we're going to check if the entry is intersecting okay then we're going to load more messages but to what this entry entry is intersecting um we're going to also provide additional options right here the margin uh coordinates uh we have to provide coordinates right here and the 250 pixel is kind of edge that when we are scrolling uh so it is not necessary to hit a zero pixel right here so it is going to start loading even earlier and that offset 250 pixel is kind of uh that okay but uh what is this entry is intersecting this is something so to what element we're listening so this is what what we're going to do if the load more intersect exists then we are going to listen to that element observe to that element so if Observer um The Observer is listening to that load more intersect current element and whenever each entry basically whenever the entry is intersecting with that element then we are going not load more messages using this approach these load more messages will be only executed whenever we scroll at the top and from here we're going to return a function which is going to be an unmount of this function and we have to disconnect the Observer okay whenever we um whenever we unmount uh from this whenever the react will unmount from local messages we have to just remove the Observer this is important because if we don't do this we're going to have every time whenever this code is executed we're going to have multiple observers so have to disable the Observer every time okay from here and this is great now let's scroll up and we are going to Define these two states okay we're going to Define no more messages and we're going to Define scroll from bottom so these two states are what we need to Define now I'm going to save this and let's have a look I think we have to import some things like use callback for example uh let's import this use callback that was imported okay we don't see any more um errors if we just reload the entire page no more errors now let's go in the network and we go xhr and we're going to observe if the more messages are older messages are loaded let's scroll up okay you see so it made the request pass the older message ID okay and it loaded some messages and we see that the scroll stays right here so there was another test was visible and the scroll state right here if we are loading more let's go up it loaded even more the scroll increased scroll up it loaded even more and that keeps on going until there are no more messages loaded now we hit that point when there are no more messages loaded probably right here so scrolling up now it sends the request but actually it even should not send the request now let's go in vs code and whenever we load more messages right here I'm going to do an additional if statement if no more messages then I'm going to Simply return from here okay so now if we scroll up now let's actually reload the page uh if we scroll up okay okay no more messages let's scroll up now it load messages scroll up it load scroll up up okay now it hit that point and in which case we should set that no more messages and this should not happen no more messages okay so the axio request is still sent and this is because probably the no more messages right here still has the old value so if we put the buger right here and scroll up here to reload scroll up we hit the debugger scroll up we're GNA I'm going to show this to you so at some point okay we hit this and the no more messages still shows false even though the loaded data right here is length it has length zero and we set this no more messages true but the point is that because we are using use call right here that function has the older value of no more messages okay and whenever that no more messages is changed we have to recreate these load more messages so we're going to take these no more messages and we're going to put this right here as a dependency whenever local messages or no more messages is changed then this function will be recreated load more messages and this will have proper value okay let's reload this page scroll up why it doesn't load first try okay it needs some time okay it is loading loading now it hit the edge now we have some error let's reload this page because when we change something it does the component uh rebound and maybe this is causing something so that's why I'm refreshing okay so now we hit it hit the edge and let's check the network if it is making more requests okay it is still making more requests uh however that should not happen because we set this set no more messages Let's uh write here console.log uh console.log no more m messages okay good and let's print right here console log loading more messages and I'm going to print no more messages okay good let's reload the page okay we scroll up load more messages false okay we keep scrolling okay now we see this no more messages okay but when we scroll up that loading more messages is still false but this actually should be true okay I'm going to leave this right now as it is maybe I can come back to this but the functionality doesn't H the functionality it is always making extra more requests to get messages and always it's going to return um an empty response and it is not also very common to keep scrolling at the top like this so probably when the user Scrolls at the very top they can understand that there are no more messages maybe they do it second time and if nothing is loaded then obviously they can scroll down okay so this is not very common and maybe minor it's an optimization I know but not that that big and we can come back to this later so I'm gonna um comment or maybe remove this from here and um from here as well okay okay good now let's uh go back to the case whenever we receive a new message okay um and that message was the the scroll position was changed on every new message and that message was turning out to be below the scrollable area okay also when we change the scroll position at some point and when we switch to another conversation that scroll position should go to the bottom so whenever we are navigating that scroll position should go to the bottom so for this in home jsx uh right here in this selected um conversation whenever this is changed what I'm going to do is to set this scroll from bottom to be zero okay set scroll from bottom to be X actually zero not null but zero because right here I am checking if it is not null we are going to update the scroll top and whenever I set this zero the it's it's going to update the scroll top uh position properly and the uh the scroll will go at the very bottom okay so setting this zero is an important thing so I save this now every time if I scroll up go go to another conversation the scroll is going always at the very bottom even if I load older messages which probably doesn't exist in this case uh okay let's go here so yeah the scroll position always goes down even if you scroll up go to another conversation then it goes down this is exactly what I want also uh this uh no more messages I'm going to reset this into false so if you notice one thing let me reload the entire page so I'm in this conversation I keep scrolling up and it's going to load more messages right so and yeah at some point I hit the edge no more messages are loaded and that variable no more messages actually is true now if I go to another conversation I see only small amount of messages but that no more messages variable is set to true and it doesn't load anything so what I'm going to do is for every new conversation I'm going to set no more messages equals to false so now let me reload the page I scroll up yep it's going to load messages uh one thing yeah I noticed that bug and I think I know the reason so if loading more messages actually takes one second and you hit that um you trigger that loading more messages second time then it's going to load the same messages twice and we are going to have that kind of issue so if I hit this now in this case it was pretty fast but as soon as I as we change this into like a slow um 3G for example or for some reason if the loading takes some time then it's going to uh send this request like this request for the same first message ID twice in which case um it's going to load the same messages twice so this is kind of issue and we can come back to this not not very big deal to be honest but we can come back to this and we can fix this later but what we did right now is that we are recovering the scroll position properly when we are scrolling at the top or when we change the conversation the scroll always goes at the very bottom and now I think we can test this sending a message as well because now the scroll position should be updated okay let's um the latest message is hello right here it should be hello what is here okay so hello from here let's hit the enter and now we see that the scroll position was recovered because the scroll position was set to zero uh whenever we selected that conversation okay um right here we set the scroll position to zero and whenever we received the new message on message created right here it updated local messages and whenever local messages is updated right here we are checking if the scroll from bottom is not null which was actually zero then we are updating the scroll position properly so I think we are almost done like we have a great result so far so even if John is going to send a message um hi there let's hit the enter the scroll position moves down in here as well this is great this send button I want to disable the send button whenever it is in sending so let's scroll down below we're going to find the message input we have a send button and I'm going to set this is the button right here and I'm going to set this disabled to be message sending so whenever message is in the sending process it's going to get disabled so test hit the enter it changes the color and then then it recovers um okay this is great let's try this one again uh I think the loading is kind of not necessary so I actually don't like this so I'm going to remove it test okay just changing the color is enough so you can't actually try try this second time so if you try to send an empty message you're going to get this you can't send this or andu okay so I was actually able to send this um send the same message actually twice if I double click on this okay this is pretty interesting so yeah again again this is something very minor and we can come back to this and fix this later or maybe we can do this right now because I think it's pretty straightforward If the message is sending on um on this new message input on send click uh yeah right here on send click is triggered from this new message whenever whenever we hit this um enter okay so and this on send click we can simply check if the message is sending we can return from here so so now if I let's just reload the page now if I type 22 and hit the enter twice it's not going to do and it's going to only send one message every time when we receive a new message we also need to update the latest message date right here and the latest message content as well so or whenever I send even not receive Whenever I send so if i s right here hello that message was sent I want to update hello text right right here in the date of this message okay and how we are going to do this let's first go in the conversation item and I'm going to find the place where we are displaying this last message date and I'm going to use a function from this helpers uh file which is defined format message date I want to use date short so let's use this function right here and we need to import that function if it is not imported so make sure it is imported right here so this will display messages differently like with the date and um yeah like this good okay now whenever I send a message we have to reload Whenever I send the message I want to update that part so for this I need to open um let's open chat layout and here I'm going to listen to message created okay so every new message comes with us with the message created so we actually have to um listen to that event and I'm going to do this right here so I'm going to Define this message created function we're going to accept a message and whenever the message is created I'm going to update the local conversations and whenever the local conversation is updated then the sorted conversations is going to be also updated okay so it's going to change the position if I receive a new message from this user it's going to move to the first position okay so inside this set local conversations I get the old users and I'm going to do the following so I'm going to return the map of Old users and I'm going to map each user in the following way so I'm going to first check if the message receiver ID exists and if the user so this old user basically we call this old user users but that actually old conversations so this might be a group or it might be user right everything from here so if that youu is not a group so if it is a user so if we receive message for receiver ID with receiver ID and if the conversation is not a group and if that conversation ID is either message sender ID or if the conversation ID is the message receiver ID it means that we have to update that specific conversation so in this case you last message equals message. message which is a text and your last message date equals message. create dat and we're going to return you in the same way we're going to do for group so if the message has group ID and if the conversation is for group and if the conversation ID matches the message group ID we are going to update the last message and last message date on that conversation and we're going to return user and by the end we're going to also return uh user if none of these ifs are actually satisfied okay now down below I'm going to Define now event listener using um event BS to that message created so here I'm going to use use effect listen on and I'm going to import the on at the top and that on is going to be event pass on function so I'm going to listen on message created and I'm going to execute the function asign the function message created and we have this unsubscribe function which I'm going to execute inside this function okay so once we have that ready we should be able to update this properly but we need to import on right here so const on equals event pass that event pass needs to be imported and it was imported automatically okay we are good right now so we have hello right right here but this is the uh message which we got after refresh okay now let's send a message how are you that should update the latest message as well as the date of the latest message okay question mark hit the enter and as you see how are is written right here and the date was also changed okay I think this is great uh if I reload the date right here is not actually correct right so there is a 4H hour difference between this date right here and what is the actual date so now the interesting thing is that what is return from the backend so there's I think a time zone uh Amon type of issue because we have all the messages in um UTC in the database so it's interesting what's the actual date so if we open conversation item and find that place the last message date okay this one if I just print the date right here so we get this uh 1052 okay okay this we get this without time zone 1052 in the database we probably also have 1052 if we go in messages uh here we see this 1052 so it is in actually in UTC because those dates do not have the time zone identifier they are returned to the front end in the browser browser and the browser renders them directly as they are but we know that all dates on the server side are in UTC so we need to handle them properly and let's return the time zone identifier as well let's open user PHP and right here we are returning this last message and last message date so to this last message date I'm going to append the time zone identifier so that date is in UTC Okay so let me show you something so if I create new date instance right now which is generally created in the uh helpers so this is 2024 d04 D14 and 1050 for example so if I hit the enter you're going to see that this is 1050 in my local time zone Georgia time zone okay however if I provide UTC right here and hit the enter it's going to give me a different time in my local time zone and this is exactly what I want so now by providing this UTC right here here we see 1052 if I reload the page we're going to see 2 uh 52 this is exactly how it should be and we're going to do the same thing in uh group PHP right here we are going to return this time zone identifier as well now awesome so we are sending now new message now let's try TR to send message with um Mary's account okay I am fine how are you let's hit the enter okay here we see I'm fine how are you that conversation was updated with the date as well as with the latest message and I think this is pretty cool also pay attention to this if I send a message in another conversation like hi there hit the ENT that conversation moves at the top because now it is sorted by the last message date and the last message date for this conversation is more recent than right here okay and based on this it doesn't matter if it is a group or not so even we can send that inside the group uh however yeah okay loading group is slightly slow I don't know why so let's send this high right here and for group we have this inval date because we don't actually have date what we need to do is go in conversation item and we are going to check if the conversation last message date exists actually that existed okay then what we need to do is go into group PHP and we are going to append the UTC if that date exists so if that date exists we're going to append that UT see otherwise we return um we return null okay and let's do the same thing for user PHP because otherwise we are returning only UTC and we we are getting this invalid date which is not correct if we scroll down below we're going to see empty dates which is how it should be but right now we have this group at the top so the latest conversation is with this group and that's why we see that group at the very top any conversation to which I will have the latest message that conversation will go up and this is exactly how it should be now let's test if the markdown messages are rendered properly because if we check our message item we're going to find out that we are trying to render this markdown uh messages properly we are using this react markdown component for this okay so let me try to send heading one let's send heading two let's send for example we might send least item least item two let's hit the enter okay now let's have a look so we don't see them rendered nicely however that doesn't mean that they are not rendered so we see H1 H2 we see this UL right here so they are rendered correctly but the question and the problem is that we are using tle one CSS which resets and removes all these tiles on those elements and we cannot um it's the browser simply doesn't render them as it should be what we are going to do is open up. CSS and we're going to restore those default Styles uh which are removed by Halloween CSS okay so these are the Styles which I am going to um recover okay so I'm just going to paste those Styles and we can just understand what is going on here okay so chat message content is the class which I gave right here so my Styles will be only applied to uh the elements which are inside this chat message content okay as you see for blog quote dld DD and all these elements I set the margin top zero margin bottom five pixel for uol and UL I set this list style revert and margin revert ping revert I'm reverting everything for table I'm also setting this border collapse revert for H 1 2 3 up to six we set this font size revert font weight revert we are reverting basically everything what is overridden by Talon CSS for links I'm also giving this text decoration underline and I'm giving it also color however I think we should change this color and make it more uh whitish something like this because we have the message blue and the Blue Link will not be looking great so the first message for the first message in this chat message content I'm going to set the margin top to be zero uh and for the last message I'm going to also set the margin buttom to be zero and I'm setting this styles for BL quote okay now as soon as I say this let's check in the browser we see heading one we see heading two and we see this list so what I'm interested in is a code part because I'm going to apply some special classes so this is my code and let's put my code with um betics to convert this into a code let's hit the enter so this is my code and my code actually is code variable okay and we should apply different styles and here we have multi line code in this case I'm going to use three back tis and let's actually copy and paste this CSS to send this okay good we can even apply CSS but I don't know if this is going to do anything so we sent this and we see this and now let's apply apply um proper classes okay so on code and we can put this inside also chat message content so inside chat message content on code I'm going to give it a font family to be CER um hold on Career NE uh and monospace this is how I'm going to provide uh font style font style I want to be italic okay I want to provide small pting to be like two pixel and four pixel I want to provide a small border radius border radius to be like a six pixel and let's provide also font size to be slightly smaller 90% okay for code so I'm going to save this and let's have a look okay so this is my code right here and this is my code on my code right here I want to give it also maybe a different color what do you think so background let's give it uh kind of something like this maybe or maybe we can convert this into more white and we can give it defense text color something like this also having a slightly larger font weight will be good F weight maybe 500 or 600 it's maybe too much and maybe we should also change this yeah this is this is up to you you can totally change it as you want I don't like this maybe it should be something like this we can we should probably also even reduce the phone size even more yeah again this is a sub objective topic and in my case uh I will probably leave this as it is it's not actually looking bad I reload everything okay this is my code this is my code maybe we should give um small background color like background color rgba with small opacity I'm going to provide this background color right here for this it's going to be good but not for this so this is going to be inside the preag right so if we inspect this we're going to see pre and inside we have code so in this case for pre we can provide that same background color and I'm going to also provide uh let's give it I to be one Rim let's give it uh WID space uh no wrap I'm going to give it also overflow X to be Auto because I want to have horizontal scroll if the code is very large let's provide font style to be italic uh we have the background color and let's provide border radius a little bit 0 file Ram but for code inside this pre let's set the background color to be transparent for code now this is our message which is not bad but we also have this horizontal scroll just in case the message is very large it it will have that horizontal scroll okay that's not bad and we can actually leave this as it is right now but the cool thing is that or all markdown messages are fully supported we can provide some quoted message right here and we can give it annotation hit the enter with somebody's text and our own text on this so full markdown we can provide I think even tables and so on so this is pretty cool and uh we as a developer need need to have that cap capability in our project to send markdown messages now let's try to implement sending emojis for this let's open message input scroll down below and find where we have that emoji um emoji icon this is right here face smile icon okay so we are going to use um react Emoji Pier component which we have also installed in package Json so if we just find that emoji picker right here and let's open its documentation and find out how we are going to use it let's open this so we have this live demo we can open this in a new tab and we're going to see this is pretty cool this is uh not a popover right here this is built-in but we do have ability to um select some emojis which goes in the recent recently used we have categories right here we can choose any imagy we want we can also search by smile whatever and um we can use this okay so we are going to use this as a popover so right here we might have some instructions how we're going to use it so what we are interested is this Emoji picker so this is what I'm going to use so if I get this Emoji picker and I'm going to put this uh let's put this anywhere right here okay just make sure that the Emoji picker is actually imported that should be imported right here so I save this and in our application we see Emoji Pier however we want to put this in the pop over so we're going to use now headless UI popover component for this here we have it so when we click on this it's going to open that emoji uh and we're going to have this Emoji picker right here we're going to select the emoi and popover basically gets closed and we are going to set this in the input field let's have a look in the code and we're going to understand uh what do we need so we need to import the popover and transition and we need fragment okay so this is what I'm going to do I'm not going to copy and paste everything from here instead in this case I'm going to type because that's not too much okay so let's let me actually copy only Imports part let's import fragment and I'm going to import this popover and um transition and let's put this fragment right here because we are already importing user state from react okay cool cool let's scroll down below in our place let's remove this part and I'm going to create um popover component with popover button inside there so right here let's create pop over uh we should have a class name relative uh because inside here we're going to have this popover panel to have position absolute okay so let's end this popover okay inside here we're going to have uh pop over dot uh that should be button okay it should have um okay let me just write these classes by myself so I'm going to provide the same classes what is available on the current button okay because it needs to be styled properly inside that button let's put this face smile icon let's remove this now okay so we have this button now let's create pop over. panel and we're going to provide some classes to that tunel so inside class name I'm going to provide absolute I'm going to provide Z10 let's provide right zero and I'm going to provide bottom to be 100% or we can provide bottom full as well so and right here inside we're going to have Emoji picker but I'm going to also provide right here properties like theme I want to be dark okay and I also want to listen on Emoji click event and in this case I'm going to get an event okay and I'm going to execute set new message equals whatever is right now new message plus the event. Emoji so this is how we are going to do it so I'm going to save this and let's let's have a look let's open our browser let's click on this it's going to bring up this Emoji picker which is nice it is a dark mode it is not the same dark mode color but it is a dark mode and we can work on CSS side to adjust it a little bit okay but whenever we select something you see that this smile goes right here okay we can select the emages we want and by hitting the send button it's going to actually send those em emagis and they are going to available right here as well so if we reload we see them right here so it is as simple as that and uh this filtering and everything is also supported if I zoom out slightly right now uh the browser is very small and if we search for smile and anything so we can select those emagis and send them okay cool um if it is not visible on small screens it is interesting how it's going to be visible on mobile yeah maybe on mobile we need to adjust it a little bit uh okay my Google Assistant decided that I wanted to talk to him or her for some reason um okay so this is something we need to um adjust a little bit for Mobile screen spot so far this is great uh we have ability to send emojis and let's actually uh apply different CSS color to it for this I'm going to open up. CSS let's put this at the very top so actually there's Emoji picker react so I'm going to use the following class Emoji ker react class and which also has e- dark Dash theme and I'm going to show this to you okay and for that class we are going to provide background background color to be I'm going to use theme mixing which is color uh which is a mixing of the talin CSS and I'm going to use colors gray 800 okay as soon as I save this we're going to see that its color is actually changed not every single detail is changed like here we have a different color which we can also apply and change we can use different colors like red 800 if we want we're going to see this not obviously what we want I'm just trying to demonstrate to you that you can use any talwin CSS color right here but the element on which we applied the color is I think this is the element um Emoji Pier react okay and this one also has e Dark theme additionally we can apply different colors to these sections which are the kind of heading sections Cate epr Emoji category label so if we duplicate this and inside here provide that name and maybe grein 900 is what we want to apply them so now this is this is better so this is without our CSS this is with our CSS so we choose s images we can close the popover and send that now let's also Implement sending thumbs up icon whenever hit this separate icon okay so I want uh if we search for right now thumbs up so I want this icon to be sent every time whenever I click this button so let's open message input we're going to listen on on click let's call the function on like click scroll up let's define this on like click and this is what we are going to do so yeah first of all if the message is sending this um good suggestion by co-pilot If the message is sending we're going to return okay if not what we're going to do is set new message to be this thumbs up but we if there is any other message like we have test for example what should happen um whenever we hit this button it should probably send separate not test but separate thumbs up right uh so if I set like this in this case the test will be lost and this is also something I don't want I want to preserve that test so I'm typing something and maybe a long message long message right here then I received a question Boolean question yes or no question from Jane and I simply want to respond with thumbs up so that I don't uh lose this section I think this is going to be pretty um useful so what I'm going to do is do like this um move this into a separate function okay let's put this down below const send request uh we are going to accept data this going to be function paste this right here or maybe we accept form data but in the response we set this new message to be an empty string and this is not what I want so maybe I should make a separate request uh for this so let's do like this I I kind of have small code duplication which is not a big deal okay so I'm going to do this axos post and I'm going to uh Define right here data which will have message message to be this and we also need this conversation uh if it is the if the conversation is for the user we need to open the um receiver ID or group ID so I'm going to copy this part and let's put this right here and inside data let's use multic cursor inside data for the given key we are going to attend either conversation ID inside the receiver ID or inside the group ID now we have this data and we are going to send this the data uh right here and [Music] in we're going to listen to um sorry I think I extracted that uh axio post from here and then I need to recover it so let me actually copy this entire function and I'm going to do undo main times now we have this access post right here which is how we want and now I have copied my function and I'm going to paste this right here okay so I have the data I sent that request and in then I don't want to set new message but I'm going to set this message sending or maybe I don't want even this part yeah it's going to be just like this so I send the request and that's going to be it because through socket we're going to get the response okay now let's test this I'm going to hit thumbs up and I get this right here and that was changed okay and the message stays right here as it should be so I keep clicking on this icon many times and I get um many thumbs up imag sent right here now let's start working on sending attachments uh to the server and previewing those attachments as well so and I'm going to start this with the message input and we're going to handle the chosen files so first of all let me Define two states I'm going to Define chosen files and upload progress I'm going to keep track on upload progress as well then I'm going to Define on file change we're going to get an event from the event we're going to get the uh targeting files so here inside files we're going to have all the chosen files then I'm going to convert these files into an array right here here and I'm going to map each file into a new object where we're going to have not only file but we're going to have also URL I need this because I'm going to use this URL to preview those files since we have these updated files I'm going to set them inside chosen files but I'm going to preserve the previously uh chosen files as well okay so I'm going to combine previous files with the updated files okay so once we set the files then we need to use them to send to the server right so inside on send click we are going to iterate over chosen files and we're going to put them in the form data inside attachments okay then the request will be sent and those files will be included in the request so here in this on upload progress at the moment we are only calculating progress but we also need to set this progress so I'm going to use this set upload progress right here inside that then when the response has been completed I'm going to set the upload progress to be zero and I'm going to also set the chosen files to be empty array and in the catch as well I'm going to set the chosen files to be zero and I'm going to get the message if there is some validation message from the back end I'm going to get this message and I'm going to set this input error message right here or if the message doesn't exist we are going to show some um general error okay that's great now let's scroll down below and find this input and right here I'm going to add on change uh for the regular files as well as for the images okay and after this we are going to start already rendering the preview of the attachments let's scroll down below here we're going to display above the error message we're going to display the upload progress so if the upload progress exist and here I'm using double exclamation to convert the uh progress into a Boolean variable okay it was Zero by default and zero might be outputed in in the in the js6 so I'm converting this into Boolean and if it is a true then I'm displaying the progress right here and that progress is a component from daui if we open documentation of day UI we can search for Progress right here and this is the progress and you can have a look in the js6 and copy uh with the proper style and I'm using progress info which is going to be um this one but I have a different theme so it's going to be a different style in my case Okay cool so this is the progress area let's scroll down below and here is where I'm going to render the preview for The Chosen files I'm going to have a separate div section with flex wrap to move if there are too many files chosen to move them to the next line with small Gap and margin top inside here I'm going to iterate over chosen files and I'm going to have file right here and that file is going to be an object which will contain file and URL okay so go back right here then I'm going to create new div and I'm going to provide the file do file name to be the key of the file inside class I'm going to provide relative I'm going to give it a flex justify between and cursor pointer but I'm going to also add additional class if the given file is not an image so this is image is a function which we are going to create inside helpers and which will simply return if the chosen file if that file is image or not if it's not an image we're giving it fixed width otherwise we just leave it uh we don't give it any extra class okay then we're going to do a couple of if statements here if the file is image we're going to render the image tag with the file. URL to be inside the source we're going to give it w16 h16 and object cover so if it is an audio we are going to create custom audio player component and we are going to render it through this custom audio player component okay if we scroll down below if we are uh rendering a file which is not an audio and it is not an image we are rendering we are going to use the attachment preview generic component and we're going to create that as well okay and down below we're going to have a button which is going to be uh to remove specific attachment after we have chosen this so inside here we have this x icon and on click we are going to call set chosen files to be filter um chosen files by file name so this is the file what I'm gonna remove which is called file and we get this right here in this Loop okay so I'm filtering those files and returning only those files which has a different name from the current Loop file so basically the current file will be removed and let's provide it position absolute with six H6 rounded full some basic tal and CSS classes to make this a nice close button and once we do have this um I think we are done in this message input here are a couple of things we need to work on we're going to Define these functions is audio is image and I'm going to Define other functions as well for this let's open helpers jsx and at the bottom of the file I'm going to define those helper fun functions like this image for example which accepts an attachment right here and we are going to take the Mind type of the attachment but that mine type um so in the database our attachment has mine type but the chosen file has a file uh has a type so basically right here I'm passing the chosen file uh here which has doesn't have MIM it has type so I'm going to support both because this is image file is going to be used for other purposes as well not only right here but whenever we are about to display to preview already sent files in this case the sent files will be database files then we are going to have this um as an attachment so we need attachment type as well after this line we are sure that the mime will be a mime type um in any case it's going to be either uploaded file recently uploaded file or it's going to be database record then we are going to split this get the first part so here we have this array of mimes we're going to get the first part and if it is an image then obviously the attachment is an image so in the same way we're going to do the video so we get the mime type we split it and if the first part is video we we are talking about the video attachment so in the same way we're going to do audio in the same way we're going to do PDF okay but uh for PDF it's slightly different because the PDF has a single M type this is application PDF if the m equals application PDF that's going to be it okay and uh below we are going to Define also a generic method to decide whether the attachment is previewable or not whether we can make preview of this attachment or whether we should make this as a generic attachment without preview previewable files are either image uh video audio or PDF we don't have anything else we don't preview anything else okay and at the top of this file I'm going to Define one L helper function which is going to be to format the file nicely so I'm going to use format bytes name for this we're going to accept bytes and the de decimals right here okay and I'm going to check first if the bytes equal zero we're going to immediately return zero bytes then I'm going to Define a variable for um how many bytes are there in a single kilobyte okay so then I'm going to uh just make sure that the decimal is a proper value given so if it is less than zero we're going to assume that it is zero so then we're going to Define sizes array with um all the sizes we will need in our application obviously there are other sizes such as terabyte petabyte and so on but we are not going to deal with them the maximum file size is going to be 1 Gigabyte so this is going to be fine for us okay now how to decide uh how many kilobytes or megabytes or gigabytes the file is so this is what we're going to do so we're going to Define let I variable which is going to be the index of the sizes array by default that I has a value zero which points to bytes so if I um gave right here 500 bytes files okay then the result will be 500 bytes so here we have this um size variable defined which is going to be bytes and then we start the while loop and while that size is more than K which is 1,24 then we are decreasing that size dividing this on K and assigning this back into size and we we're also increasing I okay so this is important and by the end we're going to return size to fixed with the decimals and we're going to convert this into float and we're going to upend the actual size so now let's explain this if it is not clear so if I gave a file which is 500 by then that size will become right here 500 right while 500 is more than K which is 1,24 obviously this will not be satisfied so I will be bytes I will be zero indicating bytes and size will be 500 so here we're going to return 500 space bytes if we gave for example five um 50,000 bytes which is like 50 kilobytes then when we decide this 50,000 if 50,000 is more than k then we're going to decide divide that 50,000 on K uh and we are going to save this back into size so in our case if we have these bites to be roughly 50,000 50,000 divided on K is roughly 50 or like U it's not exactly 50 but it's roughly 50 right so size is going to be 50 and I will be increased into one so after this when we check if 50 is more than 1,000 it's not going to be more than 1,000 and here we're going to return 50 kilobyte by index I right here and in the same way if we have um 5 uh 50 megabyte or whatever if it's a more than 1,000 kilobytes then this Loop will continue the IE will be increased then I will come here then I will come here and finally we're going to have some result okay so this is great now we have this helper function defined and now we need to create this custom audio player we need to create attachment preview as well okay and we also need to render these messages properly so let's go into message item as well okay and here I'm going to accept right here a call back attachment click okay in the message item I'm going to get this attachment click if I scroll down below I'm going to um create new component message attachments and I'm going to pass attachments as an array uh which I can get from message. attachments and I'm going to also pass attachment click right here scroll down below okay and we are this component will be responsible to render the attachments for each message however we are also listening to attachment click because we want to expand this on full screen and and have a full screen preview of the attachment for this let's open home jsx because right here we have this message item and we need to add this on attachment click right here okay but before I do this we're going to Define couple of steps States like I'm going to Define show attachment preview state which is going to be a Boolean State for the model whether we want to display model or not I'm going to Define second state as well which is going to be preview attachment and that's going to be object okay and it will have two values inside then let's define Down Below on attachment click function on which we accept attachments and index then I'm going to set preview attachment which is going to be an object object and providing these attachments and index okay then I'm going to also set show attachment preview to be true so this set attachment preview or set preview attachment will set the data for the modal and set attachment preview is a flag to display model or not okay and down below in this component we are going to render the model so if preview attachment. attachments exist then I'm going to create attachment preview model and I'm going to pass attachments which is the preview attachments state which just described and we're going to pass also index okay and additionally we're going to pass also show Boolean variable whether we want to show this model or not we're going to also listen when the model will be closed and we're going to set show attachment preview to be false as well okay and just like this we are going to have um that model ready right here now let's create those components we have to create couple of components we're going to create attachment preview model so let's go into Jes components up and let's create this jsx okay we are going to create Also let's go into message input and let's scroll down below we're going to create custom audio player so in in the up folder we're going to create that component we are going to also create attachment preview component okay what else do we need I think we created what we needed let's check in the message item we need message attachments as well yes we need this as well so in the app folder again we're going to create message attachments. Js X I think we don't need anything else let's check home again yeah okay now let me close everything but only open those just created for files attachment preview attachment preview model custom audio player and um message attachments I'm going to start with attachment preview okay which is going to be displayed as soon as we choose the attachments right here as soon as I choose something before I send it then it's going to be displayed right now we have a lot of errors and we're going to fix these errors right now so we're going to import a couple of things I'm going to import react I'm going to import the paper clip icon I'm going to import those helper functions which we just described from helpers js6 and let's define the component and we're going to export this component this component expects a prop called file and we are going to render the following uh jsx template so it's going to have with full Flex item center it's going to have a little bit of Gap pting y pting x and it's going to be rounded and B just slate of 800 then we are going to check so if the given file that file is an object which contains URL and file so if the file is PDF we're going to Simply show during preview an image of the the PDF so we're going to put that image in the project so if otherwise if the file is not previewable at all okay then we are doing the following so we are just creating this paper clip icon and uh that's it so we aren't going to do anything else so down below we are going to Output the file name and we are going to Output the file size as well okay and this component the attachment preview component is um as simple as that okay let's save this and maybe we can we can use that no uh we need a message attachments uh component as well okay let's go now and Implement message attachments so we're going to import a couple of icons from hero icons paperclip icon and wrot down and Place Circle icon we're going to import these helper functions and then we are going to create the component we're going to export the component the message attachments component will receive attachments array and we'll receive a call back function on attachment click so this should render the attachments but whenever we click on the attachment we're going to call this pent prop attachment click okay then we're going to check if the attachments length is more than zero we are going to start iterating over these attachments okay and for each attachment we're going to create div and we are listening on click of this div on uh through this attachment click which is as I mentioned is a prop and to this prop we are passing the attachments as an array and we're passing this index as well okay cool then uh and by the way if we check home jsx we we have this um where was that or maybe it was in the message item message item where we have this message attachments and we are listening on this attachment click we are again calling the parents attachment click and inside home js6 we have this attachment click right here and here we get these attachments and we get this index I'm going to re explain everything um and do a quick overview after we have this component ready okay cool let's go into message attachments so here we render each one div for each attachment but we are listening on its click calling the Pint we're assigning key as well and here we have some basic Talon CSS classes like we have this flex and flex column and item Center to properly style if it is an audio we're going to give it slightly larger width if it is not an audio we're giving with 32 with aspect square and background blue 100 as well in side here we're going to check if the attachment is not an audio file then i'me I'm creating separate download icon I'm doing this only for if the file is not audio because for audio file they are exist a separate download like browsers built-in download button for the audio files okay and this a link has a download attribute it has this attachment URL and on click we simply call stop propagation because whenever we click on this because of this download attribute it's going to start downloading immediately okay so we have a bunch of talin CSS classes and these are just to uh style this pattern uh with position um absolute it should have position absolute right here right zero top zero here we have that top zero on the right side which is not visible for some reason okay it is somewhere right here if I zoom out then right here we see the stop zero okay let me zoom in okay um and all those classes like uh if I think those classes are pretty basic like width height Flex opacity 100 and so on okay anyway let's continue and here we are going to have this icon then if the attachment is image we're going to display image tag with attach attachment URL we're going to have aspect Square object contain previous if the attachment is video we're going to display this video tag inside there it's not going to be actually video tag or it might be um but the main thing is that it's going to have this play icon okay and even though we're going to have this video tag we are going to create extra div uh which will have position absolute left zero top zero full width and full height so I do this because I want that div to be above this video ler so that whenever I click on this video I want it to trigger this attachment click otherwise if I remove this position Absol right here clicking on the video will start playing the video and it will not trigger this attachment click and whenever we click on the attachment I want that to be triggered so that to expand the attachment on full screen in the same way we're going to check if the attachment m is audio we're going to display the audio with its own controls if the attachment is PDF we're going to display something similar for what we did for the video we're going to have position upsold left zero top zero right zero bottom zero and we're going to have I frame inside which we provide the URL and we display this uh PDF preview but when we click on this iframe actually we are not able to click on the if frame because that is on top of the if frame we are clicking on the outer div right here and that actually triggers uh opening the PDF okay so and we're going to also check if the attachment is not previewable in any other cases if it is a zip if it is um whatever uh file exit file whatever then we're going to immediately trigger downloading that attachment we're going to give it some Talon CSS classes paperclip icon we're going to display also attachment name and um I think that's going to be it yeah we don't display attachment size right here right here even though I think we can display and we should display okay let's move on and now I'm going to create custom audio player which is actually used right here in this attachment uh not in the attachment preview but we have this in the message input when we upload a file or when we are going to record the file as well okay then that's going to be previewed with the custom audio player okay let's go into custom audio player and first we're going to import these two icons from hero icons we're going to import um user F VI state from react and let's define the component export that component and now let's define this audio ref which is going to be the reference to the audio player file we accept two props we accept the file and we accept show volume which by default is true whether we want to show show the volume control or not however for our message input we are disabling this show volume okay then we're going to define the state if the player is playing or not we're going to also Define the state for a volume we're going to Define state for duration and we're going to define the state for current time as well okay we're going to define a couple of functions toggle play P so whenever we click it's going to start playing whenever we click it's GNA Po and here we're going to get the audio which is going to be the uh HTML element through audior ref. current okay and we're going to check if it is in playing mode we're going to pause this in else case we are going to set the duration to be audio duration okay obviously and we are going to call also play so we need this set duration to be audio duration for mostly for for recorded content because the recorded content depending on how you're going to record basically um initially doesn't have the full duration and we're going to see this when we Implement recording audio uh and like setting this duration is necessary to have this proper uh player uh progress bar uh for the audio player but the whole idea in this case is that if it's playing we're going to pause otherwise we're going to play okay the set duration is not kind of mandatory in this case but it also doesn't hurt it so now we're going to also set plane to set whatever is the the reverse of not is playing okay so set is playing equals to reverse is playing then we're going to define the second function to handle volume change we're going to get the volume right here and we're going to get this from even Target value and we're going to have a range slider for this we're going to create this down below okay so we're going to get this volume and set this into audi. current inside this HTML element I'm going to also set the volume inside the state okay we're going to also create handle time update we're going to get the current audio references um and save it into an audio we're going to set the duration to be um audio. duration okay on every time update whenever it is playing I want to make sure again I did this for recorded audios okay whenever it is playing I'm trying to update the duration of the file properly and then at some point it it need several seconds but then at some point it was updating so we're going to set the current time as well to be event Target current time and it's going to be also even Target is going to be a range slider as well and the last function we're going to declare is the handle loaded meta data we're going to set again duration when the handle loaded metad data is called and that's it and the last function is handle seek change we're going to get the time we're going to set the time into audio uh ref current um current time and we're going to set also curent time in in inside State now let's focus on the template we're going to create one div inside here we're going to have this audio player we're going to um assign a couple of handlers like on time update unloading metadata and we're going to hide this audio player because we're going to have um a custom uh player functionality so we're going to have button which is going to be the play and pose button and on click we're going to toggle play and posst so we're going to check if it is in playing we're going to show pose icon right here if it is not playing we're going to show play icon then we're going to check if the show volume is given as a true we are going to render range slider input type range with minimum zero maximum one with step and with volume as well and on change we're calling this handle volume change which we have defined right here and by changing this range slider it is going to adjust the audio components volume value as well okay we're going to also have a second range slider which is going to be for duration okay we're going to have a it's going to have a class name Flex one because this is going to be stretched uh to all available space it's going to have minimum zero maximum is the duration okay and step is going to be 0.01 and the value is going to be the current time and on change it's going to call handle seek change on handle seek change we are going to update the audio references current time okay that's good and I think we are finished we are done this component and we can focus on the last component that's going to be attachment preview motor let's import a couple of uh things from uh react we need fragment use effect use memo and use State we're going to import um dialogue from headless UI as well because we're going to create that dialogue and we're going to import icons as well we will obviously need also helper functions from helper CS6 let's define this component attachment preview model and we're going to accept couple of props we're going to accept list of attachments we are going to accept the index the current attachment index which we want to preview at the moment okay so this attachment preview model is something which will be displayed when we are going to click on the message okay and we're going to pass the index as well let let me finish this and then I will again give you an overview okay so we're going to get the current index we're going to Define these states we're going to have the current index we're going to have um also attachments it's going to be a computed property in this case okay so what we're going to do through this use memo is it's it's working like a computed of uh it's a computed equivalent of view uh vuejs framework so it is watching on attachments and current index and whenever those two dependencies are Chang it's going to re-evaluate this function and it's going to return this value and save this into attachment okay if attachments and current index is not changed it's not going to change this attachment as well cool next we're going to have previewable attachments array so not all attachments can have previews like if you have zip file we cannot provide previews for this if we have exf file we cannot provide preview for this so here what I'm going to do is that I'm watching on attachments changed and I am filtering those attachments okay and only returning which is previewable and that previewable is is something we defined in the helpers and finally we're going to have this previewable attachments only video audio pdf and photo image okay I'm listening also on close whenever the close happened we're going to trigger this unclose which is a prop received right here okay and we're going to Define two functions preview and next functions when we click on the preview button we're going to activate the preview um we're going to activate the preview attachment okay so we are going to check if the current index is zero it means there is no more previous attachment available so what we are going to do is just return immediately however in other case if the current index is more than zero then we're going to decrease the current index and set this inside current index okay we're going to do something similar for next if the current index is equals to previewable attachment length whatever is this minus one if it is the last previewable attachment index then we are returning right here otherwise we're going to increase that and set back into current index okay we're going to also listen on Index right here which is a prop and we're going to put this inside current index now let's define the model and this model is actually um headless UI model and you can find the documentation of this in the Headless UI as well right here in the dialogue if you go in the code you can find that we are using this transition and dialogue and um everything we also have that model uh which comes with lateral Breeze right here in the components okay and that's a model which is used um for uh whenever we want want to delete the our own account on my profile page as well actually so let's close this so we have this transition inside here we're going to have a dialogue okay the transition is rendered as a fragment and we are listening we're giving it a show okay uh we we render this dialogue we give it ID we give give it class name we listen on close and calling this close inside here we're going to have this transition child with which will be rendered as a fragment and we're going to have the following Talon CSS animation classes okay for enter enter from enter two and so on again you can find these things in the um headless UI documentation and you can just copy them and paste into into your project so we're going to have this is going to be for backdrop and we're going to have this position fixed ins set zero and BG black with a transparent 25 now below I'm going to Define this um actual model section we're going to have position fixed overflow y Auto we're going to have full head full width inside here we're going to have this transition child okay with Talon CSS classes again for animation and inside here we're going to have this dialogue panel so this will have the following tances classes Flex call W full H full transform this going to be the full screen model okay it's going to be stretched on the entire screen it's going to have the background slate 800 um a line in the middle Shadow extra large transition all inside here we're going to have this close button which will be uh positioned absolutely okay and it's going to have this following x mark icon next to the button we're going to already uh render the attachments so we are going to do these things uh in the following way so we're going to have the previous button okay which is going to have position abs sold so if the current index is more than zero if the current index is zero uh there's no point in displaying this previous button right however if the current index is more than Z zero then we display this previous button okay with position absolute opacity 100 uh text uh gray 100 cursor pointer flex and all these classes um and I'm going I'm putting this uh vertically in the center that's why it has top 1 / two so which means top 50% and translate it's actually minus translate so translate is going to be minus 50% to go 50% up so that basically makes sure that this um uh the left icon the previous icon is in the center vertically so it's going to have Chevon left icon we're going to have in the same way the next icon and we are checking that if the current index is less than previewable attachment length minus one okay so this means that if the current index is less than the last previewable index okay then we are going to listen uh we're going to render this next icon with very similar classes what we have for the previous icon the only difference is that this one should should have somewhere here left four and this one should have right four the other things are uh very similar because this button will be on the left side in the center vertically this one is going to be on the right side in the center vertically okay and it's going to have cheron right icon now down below we're going to check if the current attachment exists and that attachment is the computed property what we defined right here okay so if the attachment exists then we are going to render this with flex item Center justify Center we're going to put this attachment in the center with W full H full and if it is an image we're going to render it as an image if it's a video we're going to render this as a video with controls and autop playay as well if it's an audio we're going to display this as an audio with controls and autoplay if it's a PDF we're going to display iframe with W full H full and if it's not previewable then we're going to display um General paperclip icon the attachment name and this is going to be it okay so let me save this and now let's open the browser and we're going to follow these errors we need to import certain things like the message attachments needs to be imported from the message item okay so let's just write uh import message attachments from message attachments that's first thing what we need to do let's reload the page actually there are no more errors which is surprisingly good so now let's choose files we can choose arbitrary files or we can choose only images okay when we choose only images it's going to filter image files right here nothing else okay so let's choose arbitrary files um anyway we're going to also choose images so I'm going to choose three images I'm going to click on open and now we have this error is image is not defined in the message input let's open the message input and probably we need to import this is audio let's move up is audio is imported His Image needs to be imported his PDF needs to be imported I think as well or we don't need this PDF okay we don't need this PDF uh let's see do we need something else let's choose one file okay e Circle icon is not defined okay we have this e Circle icon which is going to be to remove an attachment let's import this as well that was imported right here okay let's try to upload this again okay and here we see the preview so we have just chosen the file let's choose other files so now we have two files uh we can choose image or any file regular file so if I choose any file I need to go to downloads and I'm going to choose this um maybe this ZIP file and here we have this attachment preview is not defined and this is coming from the message message input I think yeah this attachment preview is displayed if it's not an audio if it's not a image then we need to import that attachment preview it should have been imported right here okay cool let's choose now this ZIP and now here we see this generic attachment and the name the size formatted nicely in kilobytes and the paperclip icon let's choose now an image okay um let's let's just reload just to make sure that the erors are gone so I'm going to choose this image and I'm going to choose this ZIP okay now we have two attachments and I'm curious if sending these attachments actually work so I'm I'm going to click on send please provide a message or upload attachments okay now we are going to modify small thing that's going to be in the message input when we are listening to this now on send click here we are checking if the new message stream equals an empty string or if chosen files. length equals zero okay only in this case we're going to show this good now if I click on send let's reload this page I'm going to choose just uh one zip file let's click on send pleas provide okay um so we set this in the chosen files don't we let's put a debugger right here because we set them in the chosen files okay once we set the debugger I'm curious what's the chosen file so let's come right here chosen file it has some length if we proceed we're going to see this message if this is oh here we need end why did I have this okay that should be good let's choose now this file let's go to next Network and click on send okay we saw the progress which immediately was filled uh that was send here we see the preview it is not uh nicely formatted but no problem uh let's reload now this okay the previous stays there uh we need to put this in the center that one um let's upload okay let's let's let's put this in the center first um this going to be in the message item in the message attachments actually so this is the small and we're going to give it class text Center we're going to save this um let's cancel this now this is in the center now let's choose several images I'm going to choose okay let's go back okay I'm going to choose this image I'm going to click on send it was sent and we don't see the preview of the image um I think because uh we haven't created the storage link that must be the reason so we're going to bring up the terminal and we're going to execute PHP Artisan storage column link it's going to create this storage link okay now let's try to upload second image now we see the preview okay and that file was saved in the public directory and whenever we click on this we should be able to expand this on a full screen click on this okay now we have this attachment click is not defined on message attachments okay let's open message attachments we have this attachment click uh and this message attach attachments is actually uh inside uh message item so we're going to find this message attachments we have this attachment click we have this attachment click received as a prop right here this message item is rendered in the home js6 and we have this on attachment click which we have not added to message item right here that's going to be um attachment click is on attachment click now if we reload the page and click on this we have another error attachment click is not a function in the message attachments so here actually I have typo instead of on attachment click the property inside message item is called attachment click so we're going to call this attachment click plus that attachment preview model is not imported in this file we see there's only one match it is not at the top so let's just delete this letter hit control and space and import this and now we have three matches we can find import right here okay once we do this let's open browser not this one let's open right here and let's reload entire page and I'm going to click on this image and we see that the image is opened on full screen we see this close button right here um and this is actually really good now let's try to upload multiple files maybe even including videos so here we have here we have the videos what I'm recording right now so um yeah we need to choose regular files let's go into videos and I'm going to choose the first one which should not be long okay this is one video which is 11 megabyte we can choose additional files let me choose couple of files and I'm going to click open we have totally five attachments I'm going to click on send we saw the progress bar it is still uploading now we see all uploaded files right there now as soon as I click on this video icon it's going to start playing let me mute it myself okay so we have this video we have this next button which displays the image next button displays another image another image and we hit the end there's no more next button it is disabled we go back and just like this this is awesome and working we can also attach um music if I have it okay we have some error Uh custom audio player is not imported okay this is coming from message input let's go in the message input let's find this custom audio player and let's import this good now let's have a look let's choose that audio file which I have no idea what it is and this is my custom audio player which we just developed okay as soon as I click on play oops so large okay let me lower the volume okay so it is plain and we see the progress bar and we can even um we don't have the volume but for preview that's totally fine okay now let's try to send this audio file it has been uploaded once it is sent then we display the default audio player obviously you can replace this but I left default because once it is there uh like um to display the duration of the audio file as well as the volume control and maybe this download and playback speed these are kind of cool and to develop all these things by myself would require much more time and I didn't want to just uh spend time on developing a fully functional audio player okay so the one what we have for recording is good enough now here we have this preview and um we don't actually even need to open this on a full screen because we can preview this however if we attach audio file uh by the way we need to reset I'm trying to attach the same uh audio file but it doesn't accept because we have to reset the uh input files once we upload it that's a good thing so if we go in the message input we have this on file change uh we get these files we put this in the updated files and then I think we need to change this right here event Target value equals null so that let's reload the page whenever I choose this file okay let's choose uh let's choose only one file and send this okay then I should be able to choose the second file the second time the same file okay previously it wasn't allowed now let's add couple of more files like we can add um images we can add PDF if I have any kind of PDF uh somewhere maybe in documents do I have I think I don't have any kind of PDF but that's um something you can test or do I have here I have some invoices okay I'm going to attach this and this is the issue this image PDF file which is something that doesn't exist here in my images section I have this PDF image I'm going to open now vs code let's open this and inside public I'm going to create new folder called image and then I'm going to put this inside Public Image okay now we have this PDF PNG inside Public Image and now if we remove this and maybe try to reupload PDF second time we have this uh PDF with its size and everything okay now as soon as we submit this all of them have been uploaded we see the preview of the PDF right here um um and we see everything so whenever I click on this PDF it's going to open on a full screen and I don't want actually to open this um on a full screen um and this is kind of cool so we have uh yeah one thing what I wanted to show to you is that whenever we open this and we can obviously navigate but even if the audio file now doesn't need to be expanded on the full screen it is still previewable that's why we can preview it but if we upload for example EXO file it is not previewable file so it's going to be completely skipped so we can choose some small EX file I'm not sure which one is the smaller one so this one maybe is small so and we can choose uh couple of images okay so here we see this exit file and images we can expand this we can navigate using arrows and um here we see this EXO file uh which actually we should not probably see because this is not previewable at all actually let me close this and I'm going to reload this and try this again so this is the image okay the exit file is um inside the preview let's check what is what is previewable attachments okay let's search for so previewable attachments are filtered uh to decide whether this is uh the particular attachment is previewable or not but when we get the attachment that attachment is grabbed from attachment so this should be grabbed from previewable right let's now try this open this okay now we have this issue can access previewable before initialization okay let's put this attachments down after pre previewable attachments initialization now let's open this we see this heart image and now we don't have this back arrow which means that we have only three previewable in files uh in this attachment and this one can simply be downloaded if we click on the download icon right here or on the image itself now I'm going to start working to record the audio and send this as an attachment let's open message input and if we scroll down below we have these buttons upload file and upload photo we're going to add extra button additional button right here but I'm going to actually create a separate component and I'm going to use this component right here let's go in app uh actually we need to go in resources JS components and inside app folder and here I'm going to create custom component audio recorder. jsx okay so let's Define constant audio recorder and we are going to obviously what's that okay let's cancel this uh we're going to return custom jsx code and we're going to export default audio recorder first I'm going to Define button right here inside button we are going to use two icons to stop icon uh when like we need one button um and it's going to be microphone button let's include this microphone icon uh let's give this H6 actually let's give it only W6 and let's give it the default color I'm going to remove this okay but I'm going to have also second icon which is going to be stop Circle icon with W6 but in this case I'm going to give it different color like text red 600 okay so we're going to Define right here State as well that state will identify if right now we are recording or not const recording and we're going to have this set recording okay good and we're going to display this stop icon if we are actually recording right now okay let's use right here end like we are generally doing however if we are not recording then we're going to show this microphone icon okay now let's go in the message input and I'm going to use this audio recorder audio recorder that needs to be imported so just make sure that it is imported okay and we can check this result in the browser we see some error let's have a look user state is not defined okay in the audio recorder we are using this user state but it is not imported so let's import this great now we see this microphone icon right here okay we obviously need to add a couple of styles to this button for example let's give it um small pting let's give it text Gray 400 and on Hover we're going to give it text Gray 200 okay and we're going to also add event listener to this button like whenever we click on this button okay we're going to call some function and let's call this function on microphone click and we're going to Define this function right here const on microphone click um and yeah just like this I'm going to also accept right here this AUD inside this audio recorder a prop called file ready this is going to be a function and whenever this file is ready we're going to notify the parent that the file is actually ready so we're going to pass this uh function right here and let's call this recorded over audio ready okay let's define this function right here const recorded audio already we are going to accept uh okay we're going to accept two things right here I'm going to accept file and the URL as well of that file okay so and then I'm going to call set chosen files um I'm going to take these preview files actually um yeah this is actually correct so we're going to take these previous files and we're going to push this file what we we receive right here but I think we can reduce this and put everything on a single line can't we I think we can so we can remove this file twice we can just write file we can remove this URL twice and we can remove this curly Braes right here we just need this square bracket actually we need one curly brace right here square bracket and parenthesis just like this um did I make any mistake this line is extra great okay now let's go in the audio recorder and we're going to implement this function let's first Define another state and this is going to be media recorder State I will need this state a little bit later okay I'm going to also make this on microphone click function to be an asynchronous function this is important because I'm going to use a weight keyword inside here okay now let's start so we're going to first check whenever we click on this button and this function is called first we're going to check if the recording is in progress we're going to set the recording to be f we're going to check if that media recorder exists which is responsible for recording okay if that exists then we're going to call stop on that media recorder okay and I'm going to also set this media recorder to become null somewhere down below we're going to listen to this media recorder stop event and we're going to do the necessary things okay so and as soon as we do this we're going to return from here because there is nothing to do down below but if we are not recording okay we're going to start recording we're going to set this recording state to be true and inside try catch we are going to try to get the users permission on the microphone and start recording okay for this I'm going to use this Navigator media devices get user media and I'm going to provide audio through true and here I'm using A8 because this returns a promise and to get this stream we need this A8 I only need audio okay so we have this stream then I'm going to create this new media recorder based on that stream okay and once I have this media recorder then I'm going to Define chunks because I'm going to get the data from this media recorder in chunks okay and I'm going to add two event listeners on this media recorder the first one is going to be data available and we're going to get this event right here and the second one is going to be stop and whenever we call right here stop that call back will be executed okay inside this data available we're going to get event. data and we're going to put this inside chunks okay then down below I'm going to get the entire list of chunks and I'm going to create new audio blob okay here I'm going to also provide the type of the audio it's going to be audio OG with specific codec down below I'm going to take this audio blob and I'm going to convert this into file because file is the type which I'm going to pass from here to the parent component and here I'm going to get this file okay so I get this audio file then I'm going to provide also the type to this audio file now I have this audio file what I'm going to do is just create URL of this audio file and then I'm going to call this file ready which is going to be a callback function given right here I'm going to execute this file ready passing this audio file and URL and whenever this function is executed this function will be executed and we will get this file in url okay then we're going to continue and we're going to start recording by executing start command on this new media recorder okay and we're going to also set this media recorder to a new media recorder and inside cat we're going to set the recording to be false and we're going to also log console log error accessing the microphone now let's open the browser let's reload to disappear all the errors and I'm going to click on this microphone and something should happen so it asks us the permission okay we see that the button was changed into U stop it asks us permission I'm going to click hello okay and it actually started recording something the button should turn into red but anyway let's click stop right here and we see audio file created right here okay let's first figure out why uh we don't yeah this stop Circle I needs to have a class called text red 600 I think we lost this anyway so we have this audio file and I'm interested if that audio file is actually playable so let's listen to it okay it is actually playable and play attention to this seek so it seemed like the duration was much larger then at some point it fixed the duration and that happens um because we are listening to that we're going to open custom custom audio player and here we are listening to a couple of things and setting the duration for example handle loaded metadata we're also updating this duration inside this handle time update and I think this fixes it because the recorded audio doesn't have the final duration until at some point okay until some point and then it gets that duration now the duration is actually correct and we can start listening again and if you hear my voice the button should turn into red but anyway let okay so this is great so now we can hit send right here we have the progress bar it was sent however however we don't have uh the duration right here okay now it was fixed again and when once you reload this um it's going to be fully fixed I think and it actually no it is not fixed but that's not a big deal I think okay great now we're going to check this audio on the second user which in our case is Jane so I'm going to log in with Jane oops the page is expired Jan at example.com okay John Doe okay this is good so we have this audio um basically when we start playing this audio the duration is not there but I think we can fix this later this is minor thing not a very big deal and just like this we have implemented recording I'm going to now record the second audio make sure that the um stop icon turns into red so this is my another test now I see blinking dot right here as well now we are using microphone we are recording something which is awesome the ideal case will be to also show the number of seconds recorded right here uh and again this is something very minor you can do this or we can do this later but right now I'm going to stop it this is good enough let's listen to it well now we are using microphone we are recording something which is awesome the ideal case would be to okay so this is great so it is it is working as it should work okay when I stop it now the duration was recovered I'm going to send this as well now I'm going to create toast component and use that toast component in authenticated layout so that on some action for example when new group is created when user is blocked whenever something happens in the system we show successful message notification okay this is going to be very important I'm going to open authenticated layout and I'm going to put my toast right here at the very very bottom somewhere right here maybe after children or I can put everything inside the fragment and and then I'm going to uh put my toast right here but before I do this we're going to create that toast component right so let's open go inside components up and inside up I'm going to create toast. jsx okay so let's create this component we can directly export export default function toast okay so the toast will accept something like we can accept a message but that's a different type of toast because our toast is in the authenticated layout it should be right here and we can already actually use it so let me change this into fragment then um I'm going to use toast component and paste this Steve right here okay great the toast component should be important Ed right here okay awesome now if we go in the toast js6 we cannot accept actually message because we cannot pass this message right here instead I'm going to have a different approach because this toast component is included in on every page uh inside authenticated layout what I'm going to do is to use event pass and listen on certain things okay so let's use const on is going to be um use event bus and that needs to be imported okay good I'm going to also Define right here state for toasts so my goal is to have more than one toast okay so that toast component will be responsible to iterate over the toast right here and we're going to call this into plural okay here we're going to have an array we're going to iterate over these toasts and I'm going to render them right here in the template okay cool and for the actual toast uh like um design I'm going to use Daisy UI let's go in the daisy UI and I'm going to search for toast click on this we can find um everything right here and we have different different positions we have the Start Center and top middle and bottom and this is actually what I'm going to use the bottom right side and we have different colors and different variations so if we go right here toast and here we have this alert and Alert info uh what I want is alert success actually so let's go into vs code and let me actually copy not this part but this part and I'm going to paste this right here okay let's save everything uh because we are using user state right here I'm going to import user State and let's check it right now in the browser if you reload we're going to see this new message arrived right here however I'm going to change this into success alert success okay now we see this new message arrived right here I'm going to also provide a couple of classes because the default style is not ideal for me what I'm going to give it is pting Y3 pting X um 4 let's give it text Gray 100 uh let's change its rounded uh size as well it is very rounded I'm going to reduce it okay if I remove this rounded LG we're going to see it's much more rounded so I'm going to reduce it into rounded LG we can even reduce it into MD as well that's going to be also fine um and um let's give it also minimum width of 240 pixel okay maybe we can even increase this uh does this work actually yeah maybe we need to provide this on toast minimum width oops that was missing okay now we see it's working okay so this is my basic layout and what I'm going to do right now is to iterate my toasts so I'm going to use toasts map toast and Index right here and I'm going to render this diff however I'm going to provide right here key as well and that key can be either index or I'm going to actually generate new uu ID for the toast because using index is not ideal and I'm going to generate the uid and I'm going to use that uid as well to hide that specific toast after certain amount of seconds but for now let's put Index right here save everything we have have some sort of error toast map okay what's the ER what is going on one extra parenthesis is missing I think good okay now we're going to start listening on a toast show for this I'm going to use use effect okay um I'm going to pass right here whenever own is received we're going to use effect and I'm going to listen on toast. show we're going to accept right here message and once I have the message I'm going to generate now a random U ID and for this I'm going to use a package uid package which we installed initially import V V4 is uid V4 from uid so I'm going to call my local function uid V4 and I'm going to create Now new uid and now I'm going to call set toasts okay we're going to get old toasts let's call this old toasts and then I'm going to Let's convert this into a function okay so I get the old toasts and I'm going to call um I'm going to actually return um apologies let's just put this in a single line I think it's going to be easier so we're going to Simply return old toasts plus message and uid okay this is what we're going to have and after certain amount of seconds I am going to completely remove or hide that toast so in this case I'm going to use set time out okay and yeah the autoc completion actually is what I want so inside set time out after three seconds we're going to call set toasts inside here we're going to get old toasts okay and then we're going to return the result of filtering of these old toasts and we're going to return the toast which ID is not date ID so simply using this line I am removing after 3 seconds I am removing the toast with this uid and I'm going to increase this into 5 seconds because this is how I want okay this is so cool um here we're going to use actual toast to render message let's use toast do message and here I'm going to use toast do uid okay this is great now if I go in the authenticated layout if I reload the page we are not going to see any message however uh right here uh let's let's do like this just to um test this functionality let's go into message input let's search for send and whenever we click on this send button even if it is empty I'm going to use emit to emit toast. show and I'm going to pass the message message sent successfully okay uh let's check if emit is imported no emit is not imported const emit use event pass and the event pass needs to be imported but I'm going to undo everything uh in a moment okay now this is great whenever I click this send button we're going to see that message sent successfully and the message should be gone after 5 Seconds great if I keep clicking on that I can't keep clicking okay oh okay now we have two messages and that's going to keep continue because we uh implemented the toast component in a way that it can support multiple toasts this is great however we don't need to um emit those toast messages from here so I'm going to undo do this and I'm going to undo this Amit show toast as well but instead I'm going to create uh a similar component to toast but that's going to be a component for notifications let's open authenticated layout I'm going to scroll down below and just like we are importing toast I'm going to import this new message notification right here the component but first we have to Define this component right so let's go in resources JS components app and in this case I'm going to copy and paste toast component and let's call this new message notification let's hit the enter let's change its name as well new message notification that should be uppercase in okay great now let's go in the authenticated layout and I am going to import this new message notification component right here and make sure that the import is written as well now in this authenticated layout we are emitting the following event new message notification and this is what we're going to listen inside our component instead of toast show and also we are passing an object right here instead of string and that object contains User Group ID and message so this is exactly what we we're going to do we're going to change this into object which will have message user and group ID okay and we're going to take that user and group ID and put this inside the message also now the biggest change comes in the template okay the deleting part is going to stay the same because we generate uid and then we we're going to removing uh we're going to remove that toast based on the uid ID so what we are going to change here is I'm going to change also the position like instead of bottom right corner I'm going to show that uh the new message notifications at the top and let's show this in the center as well toast top and toast um Center okay good now we're iterating our toasts we have this de but here inside instead of showing toast message I am going to also show the user Avatar who actually sent that message so let's use user Avatar component right here we're going to provide the user which is going to be toast. user okay and just like this we're rendering this however I'm going to also go even further and create right here a link compon component and that link is going to be inertia JS react link and I'm going to put my avatar and toast message inside there and inside the link I am going to um provide two things okay um I'm going to provide uh class names and I'm going to provide also um the hre so inside hre I'm going to provide I'm going to do a check right if the toast has group ID then right here we're going to provide root will be uh chat uh group and we are going to provide the group ID toast. group ID if the group ID is null then we are going to use chat user and the parameter will be toast. user who is also sender toast. user. okay once I receive that new message let's say John do received that message if that message is coming from another user let's say Jane then the user right here will be Jane the sender and whenever I click on that it's going to open that specific user convers ation my conversation to that specific user right okay good let's add right here class names I'm going to provide it flex and item Center and let's give it also G two okay I think this is good let's go in the browser let's reload it we don't see anything now let's open second browser and I'm going to send the message and we should see this notification in the center right new message let's hit the enter okay and here we see from J we receive that message and this is going to be um gone in 5 seconds okay so another another message hit the enter okay we we receive this another message right here another message right here and if we we have that another user activated and communicating with another user and Jane is sending a message to us we will receive that from Jay we will receive that when we click on this it's going to open conversation to Jane do and just like this we have implemented toasts as well as the new message notifications in our application by the way last half an hours about half an hours I'm recording with my new microphone this is um one of the most legendary microphone sh7b I try to upgrade my audio quality so I'm looking forward to see uh to hear your opinion about this so write a comment in in the under the video if you noticed audio quality change in last half an hour um in this project okay I'm looking forward to see your feedback on my new audio now let's add functionality to delete my own sent messages right here next to my sent message I'm going to add drop- down when which will have at the moment only one option to delete my sent message okay but in the future because this is going to be drop down we will have possibility to add more features let's open V code and I'm going to open message item component and here we are going to find the place next to chat okay the next to chat message right here okay so this is the chat bubble and next to it right here I'm going to put this new component message options dropdown but that message options drop-down component should only be added right here if the sender um if the message sender ID is currently authenticated user okay so what we are going to do right here is that if the message sender ID actually we need to write here equals current user ID okay we can use end in this case we are going to Rend under this message options drop-down component okay now let's create this message options dropdown inside app folder message options dropdown jsx so we also later need to create user options dropdown uh I think we haven't created or we do have created this user options dropdown yeah so in this case what we want to do let me just copy everything from this user options drop down and I'm going to paste this inside message options drop down but we're going to adjust few things okay so here we're going to accept message not conversation let's call this message the change roll we can delete we don't need this we have on block user we can also delete however we need other function which is going to be on message and delete okay uh let's autocomplete this okay so we're going to print this delete message that's fine uh it's going to send the request on message delete but actually the root name should be message destroy we're going to pass message ID and in the result we are going to receive this result. data but we actually don't care what is the result whenever the message is deleted it means that um like in most cases whenever something is deleted it doesn't require any kind of response so in this case reszel do data printing reszel do data is not necessary instead we might need to emit oops emit message deleted message dot delete deleted and we're going to provide the actual message object and we're going to listen to this message deleted in other places in home jsx for example and we need to remove that message from the messages list okay now let's scroll down below we have this drop down we have this ellipses vertical icon that's good however I am going to um I'm going to do something I'm going to add additional classes to this St but first let's try to render this and have a look let's go in the message item and I'm going to use this uh I think we need this proper name message message options dropdown right let's use this message options dropdown we're going to pass the message now let's have a look in the browser what happen so here we see this three dots right when I click on this okay uh on block user that's right so here in this message options drop down we still have this old functionality on block user which is not what we want I'm going to change this and I'm going to call this on message delete on message delete now we are going to provide right here Trish icon I'm going to use trash icon that icon needs to be imported make sure that the trash icon is imported somewhere this lock closed icon can be removed lock open icon can be removed let's scroll down below this is it and we're going to write also delete here we don't need second menu item in this case so I'm going to delete this now when I save this let's reload the page open conversation click on this three Dot and we see this delete right here okay however the positioning of this drop down is not ideal is not exactly what I want so that's why I am going to add a couple of CSS classes to the wrapper div of this message options drop down let's provide it class name I'm going to give it position absolute okay let's give it right Dash full so it's going to be uh it's gonna have right 100% let's give it text Gray uh 100 let's give it also top 1 over two top 50% and I'm going to also give it minus translate D1 over two so it's going to move up we need actually Min Translate Y so it's going to have top 50% and it's going to also have Translate Y minus 50% okay so this gives us the following functionality it's going to be exactly next to this div okay and it's going to also have uh the vertically it's going to be positioned ideally if I remove this minus translate one/ two we are going to have this a little bit down okay cool whenever this is opened as we see it doesn't have high Z index and the date right here is actually on top on top of the dropdown it's pretty weird okay let's find this menu items which has position absolute Z50 okay uh we can increase this Z or we cannot actually we can set this 100 but I think this is not going to work Z 100 no it is not going to work the issue is something else to make this working we are going to add Z index to the following absolute element so let's add Z10 for example and whenever the drop down is open it's going to be on top of anything else now let's go in the network and whenever we click on delete we see the request is sent and we also see this 400 and5 the post method is not supported that's correct it should be delete method let's reload the page and I'm going to try to delete this one actually we're going to also change the position of this drop down it should not be on the left it should be on the right side otherwise it's going to be um outside of the visible area so we have this menu items which is absolute uh right zero okay let's give it left zero good now I click on delete and we see 500 canot delete or update appear inol message attachments okay this is the problem so we are deleting a message which has attachment and the foreign key basically is restricting us to delete and this is the place where we need to delete the actual attachments but we also need to delete the actual attachments from the file system so we're going to open message Observer we created this earlier but we have not actually written the proper code inside message Observer here we're going to Define deleting method we're going to accept message right here and then we're going to do a lot of things actually okay first I am going to get all the attachments of the message I'm going to iterate over the message attachments and delete those attachments from the file system so here I'm iterating over attachments get the attachment and then I'm going to call I'm going to first get the directory of the attachment and then I'm going to call Storage from public disk to delete that given directory once we do this then we can delete all the attachments related to the message from the database we can do it l like this okay then we're going to update the group and conversations last message if that message we're trying to delete right now was the last message inside group or inside conversation so if the message was group message then we're going to select the group based on the message ID we're going to select the group basically with the last message ID of the message ID so using this line we are going to then check let me write an if so then here we're going to check if that group existed it means that the message was the last message in this group okay and if the group existed now I'm going to select the previous message inside that group which now should become the last message so previous message equals from messages we're going to select all the messages where the group ID is the given message group ID we are going to filter and extract the message which we are trying to delete where the ID of the message is not the message ID okay we're going to sort these messages by latest message at the top then we're going to limit to one and then we're going to get the first and that first is going to be the previous message if that message was the last message once we have the previous message we are going to check and if that exists we are going to update the group's last message ID with the previous message ID and we're going to call group save we're going to do something something similar if that message was a part of onetoone conversation so in this case I'm going to select the conversation where are the conversation last message ID was the given message ID I'm going to select the first conversation then I'm going to check if the conversation exists I'm going to write a qer to select the previous message so in this case we have a little bit more complex qer to select the previous message so we have a complex we here which accepts a call function and inside that we we're going to write the sender ID of the message is the message we're trying to delete it's sender ID and the receiver ID is the message receiver ID or the sender ID is the message receiver ID and the receiver ID is the message sender ID using this approach we're going to obviously we're going to filter out and take out the message we don't need this we're going to order this by latest at the top and we're going to limit and get the First Once the previous message exists we're going to update this in the conversation table in the database now we have this Observer ready but we need to open message. PHP and we are going to add this observed by right here for this we're going to use php's attribute so we're going to use observed by and we're going to provide the message Observer right here inside array of this observed by okay cool let's go in the message Observer also and we have to import several things like we need to import this conversation we need to import this message and I think we have to import this group as well and we need to import storage as well which is going to be facade okay we did this import now let's go in the browser and I am going to try to delete this message again okay I still see this unable to find Observer message Observer okay I see if we go in the message we have not imported that message Observer that was autocom completed but not imported and once we import this we retry to delete this message okay now we have status code 204 which means that the message was successfully deleted and if I reload the page I don't see this second audio file now I'm going to s test message hit the enter okay and now I'm going to try to delete this test message this test message right now is actually last message ID in the conversation so if we go in the conversations and we're going to find the conversation between user one and user two it is right here okay so this is the last message ID uh we see on Mouse over it has content test message once I delete this message it's going to be deleted and if I reload right here it was 10:59 previously but now the latest message inside this conversation is test okay and also if there are attachments the attachments will be deleted from the file system as well because we implemented this nicely now what we want to do is to listen to that message deleted and we're going to do this in two files I'm going to do this in home jsx and I'm going to do this in chat layout as well because whenever the message is deleted like right now test message is latest message right here but if I reload we're going to see that the last message is test okay so we need to update the last message uh right here and we need to obviously remove this from messages as well so hello we hit the enter and hello is the last message right here let's open home jsx and I'm going to add um listener on message deleted so I'm going to duplicate this on message deleted and we need the appropriate function as well message deleted what's this message deleted we need this off or delete deleted here we are calling this off created and I'm going to call of deleted as well now let's create this message deleted so I'm going to Define this right here message deleted we're going to accept a message and we are going to do this in a similar way we are doing right here but in this case we are going to remove this message so let's let me paste this right here and whenever we set the local messages instead of adding new message we are going to return the result of filtering okay we're going to return to filter previous messages by the message ID and remove the message ID from the previous messages and return the messages without that I think this is what we need so let's just copy Cy and paste this right here let's save this let's reload and I'm going to try to delete hello okay we have this drop down um it creates that horizontal scroll which is not ideal now when we click on delete this is gone but the actual message is not deleted because emit is not defined okay we are emiting this from message options dropdown I guess and we have to import this emit uh let's remove these icons which are not necessary also so let's define const emit uh equals use event passs use event pass was imported okay the message is deleted okay let's create new hello let's hit the enter cool let's hit on delete the message is gone and it is gone from the user interface as well now the second topic whenever the message is deleted is to successfully update the message in the latest conversation right here so the last message text as well as the last message text date need needs to be updated properly and this should only happen whenever we're deleting the last message from this conversation so if we are deleting any previous message obviously we don't need to touch this last message ID so this is something I want you to try to implement right now and then you can come back and see my solution so pause the video right now try to implement this on your own it yeah like you might need some time maybe several hours to to implement this functionality you first need to think how you are going to do this and then you can try to implement this and obviously then you can see my solution okay so here's my solution and as a disclaimer I'm not actually prepared for this particular section okay so this is something um I I am actually going to do this without any preparation on the fly so my idea is the following whenever the message is deleted from the message controller here from the destroy method instead of returning this empty response I'm going to return the last message content the last message object um after the message is deleted okay so this is what I'm going to do so first I'm going to check let's just write uh the comment okay so check if the message um is the group message so in this case we are going to just check if the message has group ID okay then I'm going to get this group and I'm doing this before this code basically is coming from the copile generated this code because we have this code in The Observer right so but what I want to do is to just get this previous message which we are using to update the database okay so and before I delete this I want to uh identify whether this is for group or this is for conversation and I want to select this group and select this conversation and after the message mess is deleted um according to what what we uh have written in this Observer the conversation in the group last message ID will be updated so in this case we're going to grab the last message and this is going to be what we're going to return so after this is deleted in the response maybe we can do just like this so in the message we are going to provide new message resource and we're going to provide right here uh last or we need to get this from the group and from the conversation okay let's assume that we have this last message variable which we're going to use okay awesome so now let's check if the message group ID exists we're going to select group okay if the conversation um exists then we we're going to select the conversation now let's delete this part and I'm going to actually Define these two variables in any case both of them uh will be null group is null and conversation is now okay once the message is deleted I'm going to do the following so if group exists and this means that that the message was sent into the group okay and after the message is deleted based on what we have written in the Observer the group's last message ID will be updated so here we are going to get we can get the last message variable to be group last message object however I think we might not have this related to last message to group so let's open group PHP and let's check if we have this last message I think we don't have so let's just write public function last message the group belongs to message with the last message ID this is actually correct okay now we have this last message and otherwise if the group uh if the group doesn't exist then we can check if the conversation exists or let's just write in else if else if uh conversation exists even conversation might not exist which means that the message which we are trying to delete right now was not last message nor for group neither in the conversation in which case we can simply return null from here maybe we need the following check so if the last message exists we're going to return its resource if it doesn't exist we're going to return null okay cool um if the conversation exists we're going to get this last message from the conversation last message let's open conversation PHP and we need to get last message from here as well public function last oh we have this last message right here okay we need to delete this so we get this last message we are passing this last message which which we can get inside this message options dropdown right here the res data so let's just print this res data and I'm going to put debugger here as well we're going to save this and now let's try to delete this Hello message click on delete okay debugger comes here raise data okay the message is actually null okay it should not be null because that message what we were trying to delete was the last message right so if we open message controller if it was the last message inside the conversation so the conversation should exist and after the message was deleted we're going to get the new last message okay let's check if the conversation exists maybe let's do this before delete DD conversation and maybe we can print group as well now if I reload this page we don't see this message the message is actually deleted so let's just write a new message we cut this now let's go in the network and I'm going to try to delete this okay so in the preview we see this conversation so the conversation exists and let's check what is the ID of the conversation it has ID 10 and it has last message ID to be 1065 let's check in the database the conversation which has ID 10 1065 and what is the message the message is 111 okay so at this stage the last message ID is 11 one okay I think I know the reason so whenever we delete this message then the conversation last message ID might stay the same so let's just put this right here and I'm going to try to delete again and we are going to see okay delete happened we need somehow to get uh what is is going to be the last message ID of this group or the conversation after it has been deleted so I'm trying to print this conversation in group right here which wasn't printed for some reason let's try this once again we are going to send this we get this now let's try to delete okay here we got this um okay the last message ID is exactly what I expected so 1066 and the 1066 is the new message which has been recently deleted okay so in the database we won't see this 1066 because that was deleted because the conversation was selected it had its own last message ID and that last message ID was right here deleted so what we are going to do is to get to reselect uh repopulate the conversation or group with the latest Fields okay so we need to repopulate group with a latest database um database data okay this is what we're going to do so we can uh execute this find again right here and we can do the same thing for conversation in which case we will have the that should be conversation in which case we will have the updated result inside this group and conversation variable and then we're going to select the last message and I think this should work so let's reload the page this one1 one should be gone let's create new one that's good in the database we see 1067 if I try to delete these attempt to read properties id id on null we get this in the message controller 143 right here sorry this should conversation oops okay let's try this one last time we sent that the last message ID here is 1068 now we are going to delete this rest data is the previous message 1058 which is the message test okay this is how it should be now if we go in uh message options drop down we have this res data okay we're calling message deleted um here we are passing the message which was deleted but let's change this into object so we're going to pass message and we're going to pass previous message as well and the previous message is going to be res. data cool let's remove this console login debugger now we are going to go in home js6 where we are listening this message deleted here instead of getting message as a parameter we're going to get OB object inside which we're going to have message and we are going to have also previous message but the previous message is something I think we don't need right here in this home jsx but we do need this in the chat layout so if we go in the chat layout we're going to listen to message deleted let's write this function here message deleted off deleted and we're going to execute this off deleted deleted here as well now let's create this of deleted right here we're going to get the message and we're going to also get prev message okay we actually don't need message in this case we just need the previous message the previous message might be also null which is also fine so in this case what we're going to do uh we're going to check let's check if the previous message doesn't exist we are going to Simply turn and don't do anything okay however um find the convert session by preview by pre message and update its last last message ID and date okay so this is what we're going to do cool let's Auto complete this okay so we're going to call this set local conversations we're going to do something similar what we are doing whenever the message is created okay whenever the message is created we're getting this message and we are finding based on this message we are finding the conversation right here and updating its last message and last message date Okay cool so we are going to do something similar in message deleted we're going to get the what's the previous message and then we're finding this conversation so we're iterating over these conversations right here the old users is basically conversations we're going to set local conversations and return the result of it okay so if the previous message receiver ID exists and the that conversation is not uh for group and if the conversation ID equals to the previous message sender ID or the conversation ID equals previous message receiver ID then we're going to update this last message so it looks correct and again very similar to this message created right now I'm thinking what will happen because the code basically is pretty much identical uh the code right here this code and this code is pretty much identical what will will happen if I call message created but passing previous message so it's going to take this previous message and it should update the conversation that should work in my opinion but we're going to see so here we have test let's just write 111 click on send we have 111 let's try to delete that and um if didn't get updated it did not get updated here uh we can also check if the message deleted was executed let's put debugger here now let's click on delete okay message deleted was executed we get this previous message okay the previous message is actually an object which contains message I see so we need to go in the message options dropdown and inside here we're going to pass res data. message this is going to be previous Mage because if we check message controller we're going to see that we are actually returning this under message key so this is how we're going to pass this previous message this should work let's send again 111 we get this message here it was updated right here now let's delete this let's proceed and that was um changed and this is pretty cool and now what I'm going to do is to send this message okay that should be updated right here for Jane user as well but in this case what I'm going to do actually let's send 222 from here and we get this 22 two right here okay I'm going to select another conversation let's say that John do is communicating with Eric okay but Jane decided to delete her last message with John do so that 222 still needs to be changed into 111 the debugger let's continue it was changed right here but it didn't seem to be changed right here okay that's a different story because we are actually returning response right here we are not emitting an event to all the other users let's say that there's a group and there are 10 users inside this group okay and one user inside this group deleted who whoever sent the last message deleted the last message okay all 10 users in in a like Ideal World all 10 users should receive an update that the message has been deleted through socket and they should uh see an updated last message ID right here in this conversation okay this is something which in Ideal World again should be done but right now I'm not going to do this I'm going to skip this and I'm going to move forward on more important things um such as creating groups adding more users in the application or um blocking and unblocking the users as well and finally we also have to deploy this project on production environment so I'm going to skip that part and maybe uh I can come back to this after the deployment whenever we select the group right here we see the group name and members but I also want to display the full list of users right here on the right side I want to also dis display a description of the group maybe if I am not if I am the owner of this group maybe to display the edit button of the group and delete button of the group so this component is actually C called conversation header so we're going to open this conversation header and find this place where we display the uh link We display the user Avatar if it is for user if it is for group we display group Avatar we display the name and this if this is for group we display members so this is one div and here we're going to define a second div but that second div should only be defined if the selected conversation is for group okay in this case we're going to display um div with flex and Gip three and we're going to have couple of uh icons so I'm going to create separate components for group description po power so this is going to be pop over and here we're going to provide the selected conversation which is going to be actually group it's description okay so we're going to also create a new component for group users popover we're going to also have uh edit and delete buttons so if the selected conversation owner ID is the currently authenticated users ID we are going to display a div with a tool tip and that's going to be edit group uh wrapper inside here we're going to have button and here when we click on this button it's going to emit an event group model. show and we're going to pass this selected conversation okay and we're going to provide pencil Square icon right here and down below I'm going to create another d with tool tip to delete the group and we're going to put a button right here whenever we click on this it's going to call the function on delete group and we're going to provide right here trash icon so if you scroll up in this conversation header I'm going to Define this on delete group function const on delete group uh and we are going to write first confirmation so if window do confirm are you sure I want to delete this group so if this actually returns false I am going let's put an exclamation mark here so if the if this returns false we're going to return okay if this returns true we are going to call on axio we're going to call delete okay and we're going to provide um root group. destroy and we're going to provide the selected conversation which is going to be actually group it's ID inside then we are going to accept the data and we can simply print console log result and we also need to listen to catch um now we're going to create couple of components we're going to create this group description pop over let's create this in the app folder uh let me copy the name group description popover jsx we need group users popover and we also need to create separate model so let's open chat layout and here we have this my conversation and we should see this um yeah right now we have error we can't do anything because we are using that component which are not actually defined and imported but anyway um so here in this my conversation next to the my conversation we have this uh create new group button okay basically I'm thinking that at the bottom of this component right here I'm going to include group model component I'm going to create an include group model component and whenever we click on this button I am going to uh display this model okay so first let's create this group model component as well we're going to do this inside up group model and jsx okay maybe let's focus on these three components right now group model uh group description and group users pop all let's start with the group model uh and we're going to define a form right here so let's start importing first of all couple of components and there are also few components like two components which we're going to create as well so first let's import these text area inputs from components text area input which actually is something which doesn't exist let me finalize the Imports first and we're going to create this text area input okay so we're going to also import input error and this input error exists under components input error we can open this and have a look okay so we're going to also import uh import input label we're going to import model component from components we're going to import uh text input as well secondary button primary button um and user peer is another component which doesn't exist and we're going to create this okay and we're going to also import use form and use page we're going to import use event BS and we're going to import use f and use the state as well from react now let's define the component and here we're going to um accept two props show and on close which should be a call function okay before I continue let me Define these two components text area input and uh user peer so let's go into up and create text area input and user heer jsx let's open text area input first and this is going to be very similar to normal text input if we open from the components folder if we open text input and we're going to copy everything in text area input we have to adjust this instead of input we're going to have text area right here let's add also closing tag uh let's call this text area input we don't need to provide type at all we can remove this no type is necessary we're going to have class name whether it is focused or not and everything else can stay the same okay so this is our text area input which already exists which is imported right now in the group model but I think we defined this in the app folder maybe yeah we defined this in the app folder and I'm going to move this inside components because it is imported from the components and this is more generic and it should be next to text input okay this is it good the next component we created is the user picker okay and the whole idea of the user Pier is going to be to select multiple users let's open uh headless UI and I'm going to show to you combo box example which has autoc completion feature as well so I'm going to use this combo box component right here and adjust it a little bit this one ALS Al has multiple support so we have possibility to search for specific users like let's search for Devon for example we can search it and click on this and select this okay if you go in the um examples and uh options if I zoom out slightly on the right side we have these options if you zoom in maybe those options will be at the very bottom um I'm not sure but the thing is that uh there is a multiple support to that okay uh we have different uh customization support uh right here but one of them is to actually enable um to select multiple elements this is exactly what we're going to use so right here we see checkbox um tick boox on the Devon user but actually in our application for user peer we will have that tick box on many other users as well because we're going to have the multiple Pier support so if we have a quick look how this component actually works so we here is the filtering function as well okay here is the component itself we have the combo box we have the like a Talan CSS uh classes with a plain Dives here's the combo box input which is for searching and on change we are setting the Q and we are filtering the users in the display name we see the person name which has been just selected right here okay cool um and we have the com box button actually which is the um chair up down so this is the uh button right here and we also have this transition and inside transition we have combo box options and those options are whatever we see right now okay and inside transition means that it's going to open with some animation with uh the proper uh CSS classes so inside options we have the position absolute nicely uh St filed at the bottom and uh we have the following check as well so I'm going to now create something similar in our component and I'm going to re explain things in our component as well so let's go into user peer and let's import first of all a couple of things we need fragment we need US state we need combo box component and transition uh check icon and chair up down icon now let's define this user peer and we're going to accept value options and on select function as well that on select function will be executed whenever something is selected okay now let's continue we're going to Define couple of States we're going to have selected State we are going to have a CER for filtering and we're going to have this function to um filter for filter the people actually so we get this query if the quer is empty string we're going to return options given options if the query exist list we are going to take those options and filter by person name to lowercase and we're going to return that result of that filtering and we're going to have this filter the people okay um we're going to actually uh also remove white spaces um from this person name to lowercase result okay so um and obviously we are checking that if that name remove uh white spaces convert to lowercase if that name includes the cury keyword right here which is also converted into lowercase and um wi spaces are also replaced okay then we are we are considering this person uh to be inside the filtered people okay down below we have this unselected function uh where we accept persons and we're going to set selected those persons and we're going to also call on select which is going to be a given prop function okay now let's focus on the template and we're going to use this combo box component we're going to provide the value to be selected we're going to provide on change which is going to be unselected and here we're providing this multiple so that multiple users can be selected so inside here we're going to have a proper like a plain div with relative margin top one inside here we're going to have another div with a lot of tan CSS classes okay to properly style this so I think those tan classes does not need any further explanation because they are pretty straightforward and we have done probably most of them in our project okay so let's continue and we're going to have this combo box input with proper uh classes to show this nicely okay so we're going to also provide display value what should be displayed inside this input here we are going to accept persons and I'm going to check if the person's length exists I'm going to display the person's length for whatever it is users selected otherwise I'm going to display an empty string okay if there are five users selected um five users selected will be a text okay cool so inside placeholder I'm going to um type select users on change we're going to set the cery whatever is going to be the cery of this input okay even Target value down below we're going to define the combo box button which is going to have this Chev up and down down below we're going to have this transition um and inside here we're going to have these combo box options okay we we're going to give it the same tances classes so most of these classes are actually copied oops um are copy it from The Headless UI for example for combo box options you can simply grab those classes and put in your project so you don't need to manually type them also you can do the same thing for this input combo box input right here you can get all of them the same thing for this div if you want so okay uh I mean this div you can get those classes from headless UI okay so inside options we are going to iterate we're going to check first if the filtered people length is zero and if the Cy is not an empty string we are going to uh show that nothing is found okay in El's casee we are going to iterate our filtered people and we're going to render combo box option with key person ID value is going to be person and we're going to add tal CSS classes as well and inside here uh we're going to also add additional classes based on whether that option is active or not whether it is selected or not um if it's selected it's going to have different background color if not it's going to have gray dark gray um however we're going to also uh display checkboxes so here inside that combo box option we are going to um have this selected and active function okay and we are going to um display uh the selected um checkboxes basically okay so we're going to display the person name as an option and if it is selected we are going to show this check icon as well and again this is something which you don't have to write this manually if we check this t uh this headless UI combo box uh I recommend to grab this component and customize this okay for example this is the uh selected active function and if we scroll down below we're going to see um person name if it's selected we see this check icon like uh 90% what what we have in our component is actually copied from here okay so you can copy this and adjust it make sure you understand I don't want anybody any of you to blindly uh copy and paste things or just write whatever you see on screen you should understand what you are doing okay so whenever you copy something you should understand what is doing uh what maybe not 100% but at least have a decent idea so for example what is this person name printing right here how you can customize styles of the printed person name and so on so now we have this user Pier um and I think yeah we also need to down below this comp box I'm going to display selected users okay whenever we select some users I want to display these down below not only U mark them checked with the tick box but display them down below as well so if the selected exists which by default is actually a value whatever we give so if that exists then we're going to iterate over selected we're going to get the person we're going to um create a d for each person we are going to have this BGE and BGE primary and BGE actually is a class from uh Daisy UI so if we go in the badge these These are badges okay and we're going to have this primary badge displayed page primary with gap inside here we're going to have the person name and um and that's going to be it okay we're displaying the selected users now let's go in the group model and continue so we we spend so far time to create this text area input in the user peer okay now if we continue on the group model we are going to define the page we are going to get the conversations we're going to Define these on and emit group and set group as well and that's going to be necessary whenever we try to update group so in this case we're going to have group and set group we're going to also use form of from the inertia Jes and Define the following properties we're going to have data set data processing reset postp put and errors inside data we're going to Define ID name description and user IDs and those user IDs will be the users we are going to put inside the group down below we are going to iterate over the conversations and filter them and return only those conversations which are not users so the conversations are actually all users plus groups what we see in the browser on the left side right here right now it is broken so it doesn't display so we're going to only filter and take out the users not the groups okay now let's define the function to create or update group we're going to accept an event right here and I'm going to call event prevent default we're going to check if the group ID exists it means that we're going to update the group so I'm going to call put method which is right here from this use form okay we're going to call put method we're going to provide the URL and that rote uh group update yet doesn't exist I think we haven't defined this if we go in the web PHP we don't see any group related roads right here don't we we see only uh select messages by group but we don't see an actual like group create group update or group delete let's assume that uh when we Define it we're going to give it the following name right so then we're going to provide right here data uh actually we don't need to provide any kind of data because when we create this form right here and then we destructure this when we call put method through inertia that data will be transferred okay so we just need to provide the URL on success we are going to close the model okay and we're GNA also emit show toast event toast Show event with a message Group whatever let's call this for example JavaScript um group JavaScript was updated okay we're going to also uh send and because we are returning right here we don't need to write an lse statement we can immediately send post and provide the group store URL right here so we're sending the create request if the group I doesn't exist on success we're going to again show Group whatever was created and we're going to close the model so here let's create this close modu function we're going to reset the entire form and reset is a function right here from this use form result and we're going to also call this on close which is a prop received right here okay so we're going to also use effect listen on uh on right here and we're going to listen to when the group model needs to be displayed okay whenever group model show um is triggered is emitted from somewhere else we're going to get the group okay and if that group was given as an object it means that we're updating the conversation um not conversation but the group but if the group was not given it means that we need to create it or if the group ID doesn't exist when to create it anyway so let's set this data right here and the inside the data we're going to take the group name we're going to take the group description and we're going to take the group users okay but we are going to take this users which is going to be U array of objects okay and we are going to filter those users and only return those users uh who are not the owner ID okay so whenever we have group right here it means that we are trying to update the group and the user who is trying to update the group obviously should be the owner of this group and there might be four users in this group and one of them is going to be owner okay but during update we don't need to show owner user itself in the selected users okay because obviously the owner user should not be removed from the group right so here we we are filtering out and removing owner user from this group users and returning the other users but we're going to also map this and take only ID we don't need the entire object so finally in the user IDs we're going to have the users of the group except the owner user but only user IDs okay we're going to also set the group inside the state okay now let's focus on the js6 so we have this model we're going to provide show we're going to provide on close and this model is something which is coming from components this is this comes with the U lateral Breeze uh with inertia with react so this is the model which is basically provided with the lateral brid installation okay so we're providing show we're providing on close we have here on form and we have onsubmit create or update group we have overflow y Auto some padding inside here we're going to Define uh now inputs so we're going to have this first input which is going to have margin top six flex and justify end this is going to be actually uh a footer this is not going to be group this is going to be footer inside which we're going to have the cancel button uh clicking on which we'll call this close model and primary button but and inside the primary button we are checking if the given Group which was given right here if that group ID exists then we need to show text update otherwise we're going to show create we're going to also disable this button if the form is in processing State and here we have that processing flag okay now let's continue and here we're going to Define this H2 inside here we're going to provide the title of the form so if the group ID exists we're going to show edit Group whatever otherwise we're going to show create new group down below we're going to Define already inputs so we're going to have input label which is going to be for name and value is going to be name this is going to be the group name we're going to have the text input with ID with some classes with a value with uh disabled basically during update I want to disable the group name and I don't want to allow name change this is how I decided you can change obviously this part but I don't want the group name to be changed so it's going to be disabled if the group ID exists on change we're going to get the even Target value and this happens on create whenever we are creating new group on change we get the even Target value and put this inside data now this must be required and we are going to automatically focus in the first field and we're going to also provide input error right here and the message will be errors. name now we're going to have second field which is going to be for description we're going to have text area input ID description the rows going to be three class name margin top one block W full value is going to be data description on change we are going to get even Target value and put this in the description okay um input error here we're going to uh display the errors description another field it's go which is going to be the select users and this is the place where we're going to use user peer okay so we are using the user Pier which which we created right now we accept value options and on select okay so inside value we are going to provide the um selected user so be bear with me bear with me because uh this is going to be tricky part and I'm going to try to explain this very clearly so we're going to take the users and those users are the users which are uh described right here so those are all the users okay um from the conversations we filter and we get all the users so we take those users and we filter and we remove the owner ID from these users okay then we are going to only also filter and we're going to only return those users which has ID inside data users ID data users ID is whatever we set right here as I mentioned if there are four users in the group okay and the one of them is the owner we don't display the owner in the selected users during edit during edit we're going to allow removing the users and adding new users but we don't allow removing the owner ID so we only show other three users but the thing is that right here in this user peer when we provide the value inside value we need to provide objects not IDs okay so what we are doing here filtering users which is array of objects we are filtering this and returning array of objects again removing the owner ID but only returning those users which has ID inside data users ID so if there are uh three users actually in the data users ID this is going to return three objects okay cool now we're going to continue and inside options we're going to provide all users okay on select we're going to get the users which we selected and inside set data we're going to put those users under user IDs key the same ID is what we have right here but we're going to take those users and map and get only ID we don't need object cool we're going to have also input error for errors users uh user IDs okay um that's it actually in the uh group model so we created this and now we can use that group model um uh actually before we use this group model let's finalize this group description popover as well okay so here I'm going to use popover and the popover is actually a component from this headless UI if we go in the pop over I think we have already used this popover for the Emoji picker uh it's it's um pretty easy we can just have a look right here we're going to need this popover popover button and we're going to have this popover panel okay let's create this component first let's import popover and transition we're going to import exclamation um Circle icon we're going to import fragment then we're going to Define this group description popover component and we're going to get the description then I'm going to define the pop over okay and inside this open we're going to have this popover button and here we are going to um have conditional classes if it's open we're going to have different uh color basically when it is open we're going to have uh more light color text Gray 200 when it's not open we're going to have uh text Gray um 400 on hor we're going to have text rate 200 in any case so we're going to have exclamation icon right here whenever we click this icon we're going to show panel so we're going to define transition right here and again this transition can be copied and pasted from here okay no need to type it inside transition we're going to have this popover panel we're going to provide those absolute classes right Z 10 and so on okay we're going to give it also fixed width inside here we're going to have uh another div with um like a with some transparent ring and rounded LG we we're going to have Shadow LG as well and overflow hidden so inside here I'm going to Define another deal with background gray 800 with pading um and I'm going to have H2 this is going to be the title of that panel down below H2 I'm going to show if the description exists I'm going to show the description with text excess class if the description doesn't exist I'm going to show no description is defined okay and just like this we have this group description popover component ready let's focus on group users popover as well so we are going to import popover transition users icon fragment user Avatar link and then we're going to define the component here we're going to accept users as prop we're going to create the popover in the same way so we're going to create this button the popover button very similar to what we have right here you can copy and paste from here if you want um as well we're going to have this users icon we're going to have transition inside which we're going to have a popover panel okay this one has slightly smaller fixed width then we're going to have this div inside which we're going to have another div and here we're going to iterate over users those users are whatever we're giving right here okay I'm going to iterate over users and I'm going to create link the link goes to chat user for that specific user ID so whenever we click on this user it's going to open the conversation to that specific user the key will be user ID the class name will be Flex item Center get to Ping y ping X and some hover inside here we're going to have user Avatar providing the user and we're going to have the usern name as well and I think we have finalized that group users popover as well what we need to do is to go in the conversation header and import this group description pop over we're going to import this group users popover as well and if we scroll down below we don't have any more components right here but we can go in the chat layout scroll down below and right here we can Define group model component and inside group model we are going to provide show and on close before I provide show and un close I'm going to Define new state for this show group model okay if I scroll up inside States let me Define right here another state and this is going to be uh show group model with set show group model so I'm providing this inside show we're going to provide show group model and then I'm going to provide also on close equals um set show group model U actually we're going to un close we're going to get the function executed function and provide false this means that on close we are going to hide the model however at some point whenever we click create new model button we want to show this so if I scroll up uh uh right here next to my conversations here we have this button on click of this button we're going to get the event and we're going to also call set show group model provide true so we're going to save this now let's reauthenticate I was passive for some time we have some error which is good I love errors because whenever I fix I have satisf section out user is not defined okay this is inside the conversation header let's go in the conversation header and let's do like this so const out user equals use page props do. user but we don't want curly braces right here from use page this use page needs to be imported it is imported this is good uh props out end user okay this is good let's reload the page pencil Square icon okay this needs to be imported so pencil Square icon this is it there is let's save this and reload trash icon needs to be also imported okay reload the page errors are gone and now look at this so this is the conversation header so we have this info button which is for the description these are users edit and delete and for edit and delete we also have tool tips let's click this info and it shows the description so this is actually a diamond description which was generated with this group okay that's fine if we click on the users we're going to see all the users which are part of this group uh however here I see that this three dot is above that drop down which is not what I want so let's go in the group users pop over this one has absolute with z11 here we have this pop over why is this actually behind this button it's a pretty weird C case we have Z10 we can provide um Z 100 let's say but I don't think no actually this works okay let's check what is the Z index on that it has Z10 as well okay so in this case I think providing z20 right here is going to be totally fine yeah just like this so these are these are the list of the users and I think we can increase the at least to have the users on a single line perfect we have this edit button okay whenever we click on this edit um nothing actually happens emit is not defined in the conversation header that's totally correct let's go in the conversation header and const emit equals use event pass that use event pass needs to be imported here so let's try to click edit again but nothing happens right now because we don't have this model um included actually the model is included that model is included in the chat layout okay but the point is that whenever we click on this edit button um it doesn't seem to be working group model show interesting so we are emiting this whenever we click on this pencil button right but do we listen to this show let's go in the group model search for this okay let's put debugger here and let's check yeah the debugger comes here and we have group let's continue okay the model is not displayed let's try to create new model okay then now the model is opened okay so this is create new group when I click on this it should trigger edit the group okay so whenever we click on this edit button it emits this group model show but that group model show doesn't mean that we display the model so in the chat layout we have included this model OKAY however uh this model will only display if the show group model is true so what we need to do in this chat layout I am going to maybe right here I'm going to listen to that event um of model show maybe yep something like this so this is group model show actually so we are listening to group model show we're going to get a group right here we have off model show which we need to execute here but inside I'm going to set group model show to be true okay uh I think this is good and we're listening this group model show yeah this is good so inside this group model show we are listening right now this inside group model right here and we get the group and we are listening right here in the chat layout and we are displaying the model now whenever we click on this edit icon it should display the model and populate the information so here we have five users selected a list of the users we have the description we have the name the name is not editable because this is how we decided okay now let's try to edit the users obviously the backend side is not there backend side is not working so we can remove everything from here we can just leave uh these three users perfect and when we hit the update the back inside is something what we're going to implement also when we click on this create new group the popup comes up and this is exactly how it should be let's select a couple of users that's perfect we have the selected users right here so as for the delete it shows the confirmation but we don't have the back and side ready so we have the description we have this users list and whenever we click on one of the users it's going to activate the conversation to that specific user which is down below let's go back try to click on another user now the conversation with that user has been AC activated and this is actually really good now I'm going to focus on the back hand side and create group controller and implement the operations such as creating group updating group or deleting the group let's bring up the terminal and I'm going to generate new group controller by executing PHP artisan make controller group controller I'm going to also provide couple of flakes right here I'm going to provide dash dash requests and I'm going to provide dash dash resource to generate the group controller is a resource controller now I'm going to hit on enter by the way we can also provide D D model equals to group this is also um useful so whenever we hit the enter it's going to generate three files so we have this store group request let's open this uh actually let me delete everything else so we open this store group request I'm going to open update group request and I'm going to open group controller as well okay and I'm going to also open web.php and we are going to define the group related rules right here let's go in the group controller first and I'm going to remove a couple of methods like we don't need index because we display groups with a conversation we don't want create because that create is used for rendering the create group form and we don't need this we need store we don't need to um we don't need the show function which is created to return and display a single resource single group we don't need that so we don't need also edit because this is responsible for rendering a form to update specific resource we need update and Destroy so these are three methods we need at the moment let's start our working with the store group request so here I'm gonna change this return false into return true to allow authorization on this request and inside rules I'm going to define the rules to relate it to that um group so we need name which is going to be required it must be string and the maximum length is 255 we're going to provide description which is going to be nullable and it must be string I'm going to also provide user IDs which is is going to be required actually let me think user ID is is something I think we can make it nullable okay so let's change this nullable like we are allowing to define the group but don't assign users initially okay so user ID is is nullable but it must be array and we also need to Define each element of the user IDs and each element inside these user IDs array needs to be integer and they must exist under users table under ID column okay I'm going to also Define override uh public function validated method which um accepts key with default value null and it's it's going to accept default as well which is going to have also default value null and what I'm going to do is to take the validated data validated uh in in this case I'm going to call parent validated we're going to pass this key in default it's going to return this validated data and then I'm going to inside this validated data I'm going to provide owner ID who created this group and the owner ID is going to be the currently authenticated user ID so we can use this user ID right here and finally we are going to return validated data okay now we can go in the group controller right here and we can implement this store method so we have request right here we are going to get the data data is going to be request validated then I'm going to get the user IDs user IDs is going to be from that data to get the user ID however if the user ades doesn't exist for some reason we need empty array then I'm going to create group by executing group create providing data right here which is going to return instance of the group after this on that group uh users I'm going to call attach method passing the user IDs okay however here we should also pass the currently authenticated user ID because for example I am authenticated user my name is Zur so I created the group and I assigned like Jane and John inside my group but uh zoraa also should be part of this group okay I selected Jane and John and the user ID is now contains only Jane and John and myself should be also attached inside group users so what I'm going to do is to take the currently authenticated uh users ID okay and I'm going to concatenate this with the user IDs okay just to make sure that we are fully safe I'm going to also use array unique on this array if for some reason by mistake or for some reason I don't know how but if I received myself in this user IDs list from the front end just to make sure that we don't save the same user in the same group twice I am going to use right here array unique method cool after which we can redirect user to back so I'm going to do return redirect back awesome and just like this we have implemented creating new group now let's focus on updating group so I'm going to speed up a little bit so we're going to get data which is going to be uh request validated data then we're going to take user IDs which is going to be data user IDs or empty array then on group we're going to call update passing the data after this what I'm going to do is to remove all users and attach the new on okay so in this case I'm going to call group users detach to remove all users from this group and after this I'm going to call group users attach again with the following code so we're going to take the received user IDs add the currently authenticated user ID and I'm going to call array unique something exactly the same what we are doing right here okay after this we are going to do redirect back now let's save this okay and now I'm going to implement destroy as well let's scroll down below inside destroy first I'm going to check check if the user is owner of the group okay so we're going to write if the group owner ID is not the currently authenticated user ID we can return immediately 43 St status code or we can also return with more descriptive uh response like for example return response now provide right here some text and provide this status code but I think aort 403 is actually good decision so I'm going to leave this right now okay whenever we decide to delete the group there's one important thing okay group deleting might take a lot of time so it might take one minute two minutes depending on the group size depending on number of messages in the group um it's going to take it's going to delete all the messages all attachments remove the users from there so it might take some time so this is what I'm going to do I'm going to return an immediate response from here that the message um group delete was scheduled let's just return response uh Json and here I'm going to provide message and let's provide the text group delete was scheduled and will be deleted soon okay let's put semic column so but right here what I'm going to do is to schedule deleting of the group so for this I'm going to create new job and I'm going to call this job PHP artisan make job let's call this delete group job let's hit the enter delete group job has been created okay and this is going to be responsible to actually delete group with everything from the um database from the file system but from here from the controller I'm am going to schedule that job so let's just write delete group job I'm going to call dispatch passing the group and I'm going to provide delay as well so I'm not going to immediately start at a very second I'm not going to start at the very same second deleting this group but instead I'm going to start deleting that group oops now add uh let's add couple of seconds like after 15 seconds add seconds it should be seconds in plural 15 okay so once the request is received if the owner is the same as the currently authenticated user we are dispatching the delete group job after 50 seconds but we're going to immediately see the response and that response will be delete group was scheduled and will be deleted soon okay now let's go inside delete group job and we're going to accept right here group this is going to be public group group then inside handle we're going to do the proper deletion process so what I'm going to do is to update the last message ID of this group to Beal otherwise because of this foring key we will have an issue and we will not be able to delete either messages or group okay so we want to delete all the messages but the latest message ID is saved as a key inside the group itself okay in order us to be able to delete messages we have to remove this last message ID from the group so for this I'm going to call this group last message ID equals to be Nal then I'm going to call this group save okay in this case I'm going to remove it I'm removing this last message ID after this I'm going to iterate over all messages and delete them and I'm going to do this with the following code this group messages each delete okay and on the message we have Observer we have the message Observer we can remember that part message Observer which is listening to when the message is about to be deleted and this is going through all the attachments and deleting them one after another so once we execute this all the messages and their attachments from the file system as well as attachments from the database will be completely deleted after this what I'm going to do remove all users from the group and I'm going to execute the following code this group users detach and then I'm going to delete the group Itself by executing this group delete this is going to be great so this is going to be it what we need however I'm going to also uh emit a socket event group deleted uh and we're going to notify all the users that this group has been deleted for this we need to create new event uh PHP artisan make event um I'm going to call this group delete Ed event okay let's let's open this group deleted okay and we are going to implement the interface should broadcast or should broadcast now okay now let's identify which one we need actually to implement if we go in the group controller this is the main thread okay we get the request we dispatch this in its own thread it's not actually like M multi-threading system so PHP is not multithreaded Language by its nature but what we're going to do is that put this job into q and we will be listening to the Q and whenever there is something to execute with considering with the delay as well it's going to execute that okay and the main thread so-called um threading quots basically so the main thread will return the Response Group delete was scheduled and will be deleted soon and in a separate process which is going to be handled by Q the this process will start delete group job so this handle process will start okay and from here I am going to dispatch the event um let's use this group delete event and I'm going to dispatch passing the group which should be deleted and because this handle is already executed in its own process uh I want the dispatch to happen um immediately okay so we are dispatching this right now even before the group is deleted okay but there is actually nothing unless there's database exception okay nothing can go wrong right here um regarding this um deleting so what I'm going to do is leave this group deleted first I'm going to dispatch that the group has been deleted and then all the processes will continue and inside here this group deleted instead of shoot broadcast I'm going to use shoot broadcast now the difference actually I think I explained this but I'm going to quickly recap this the difference between Sho broadcast and shoot broadcast now is that Sho broadcast will put that event in a queue and the separate process will handle this and emit this but the sh broadcast now will emit this in the same process so in this case we're going to import shoot broadcast now and we're going to H Implement that right here so here we're going to accept public uh group just like this and we are going to also override method public function uh broadcast with method and we are going to to return uh Group which is going to be a new group resource we have not created group resource this is something we're going to also create I'm going to bring up the terminal and I'm going to execute PHP Artisan uh make Resource Group resource let's hit the enter okay we're going to work on this group resource in a moment now uh group resource and we are going to provide this group let's make sure that the group resource is imported here it is uh semicolon is missing right here we need that and in the broadcast on I'm going to a it private Channel and the channel name is going to be group deleted and the actual group ID so this is going to be this group ID however I'm going to give you also the following hint following information so if we go in the delete group job as I mentioned first we are emiting the event then deleting the group completely if for some reason the one of the following processes has been failed and the group is not deleted we emitted an event to all users all uh users connected to that group that the group has been deleted but actually the group was not deleted for some reason I don't know y so in ideal case maybe we should put this below delete when everything completed successfully but in this case this group might um might not have the data anymore because we deleted that record from the database okay and let's actually leave it like this and I'm going to use uh dump and die right here group deleted and I'm going to also print this group and let's have a look uh what will happen What will be printed right here we're going to see this result in the uh Q in the terminal so let's go in the group deleted again we have right here this proper key and I think um one dot is missing right here I want group dot deleted Dot and the group ID okay so we have controller ready we have this job ready we have socket ready I think what we don't have ready is this group resource let's open this and now from here I'm going to return the following information I'm going to return ID I'm going to return name I'm going to return description I'm going to return owner information and this is going to be the object of the currently U the the user who owns that group so we're going to return new sorry here we have owner and we're going to return a new user resource passing this owner okay we're going to also return list of users and in this case I'm going to use user resource collection passing all the users of that specific group I'm going to return also last message which is going to be uh wait do we need last message so we are actually returning last message from the group model and this group inside here inside this group let me think so we're going to actually use this group resource we are using this in a single place and this is this uh socket uh event which we're going to emit okay whenever we get this group we're going to completely delete this group I think we might not need even that information maybe we don't need all that information like what we probably need is to emit is just the group ID right hey this group has been deleted maybe group ID and group name the group has been deleted we're going to show this not ification so here's what I'm going to do I'm going to change this broadcast with and I'm going to provide ID right here and let's just return okay this is what I'm going to do uh let's go in the delete group and I'm going to take out ID and name so ID is going to be this group ID and name is going to be this group name once I have ID and name Sav in its own variables then right here I'm going to pass ID and name so inside group deleted we're going to get public string ID or I I think ID should be integer public int ID and public string name then I'm going to return this ID and name so because by default if we don't remove if we don't have this broadcast with all public properties are in Ed from the event so ID and name will be emited by default so I think we are good we can just provide ID right here let's save this I'm going to save this part as well right here and in this case we might even we might not need this group resource at all so I'm going to leave this right now but if we don't need this finally I'm going to delete this group resource now I'm going to open web.php and I'm going to Define rules for group create group update and group delete so let's define first group create on slash group endpoint we are going to listen with the group controller store method and the name is going to be group. store we are actually already using that name in the front end in the react site I'm going to Define also put with the following URL group controller update providing the group update and we're going to have also delete group um/ group so we need to import that group controller make sure that this is imported right here and I think we can already test this functionality okay uh we're going to also open channels PHP and we are going to add uh the the channel right here to authenticate group delete so the channel is going to be group. deleted I think this is what we called uh group deleted group deleted Dot and the group ID so we're going to get the authenticated user we're going to get the user ID and we're going to check if the user groups contains the ID then we're going to return true otherwise we're going to return false okay let's close this we have Roots ready uh and I think now we can move on the front end side so I'm going to open authenticated layout and I'm going to find the place where we are listening all our conversations so here we start listening to new messages through socket message and what I'm going to do is listen on group deleted but I'm going to do this right here after this Echo private passing the channel here first I'm going to check if the conversation is for group then I'm going to use echo. private and I'm going to provide the group deleted EV so this is going to be group. deleted conversation ID and we are going to listen to the following event group deleted because this is how we actually called the event uh in LEL okay whenever group deleted happens let's print the entire event and I'm going to also emit the event group deleted passing this conversation ID okay uh let's add also catch uh console error E inside group deleted so inside event we are getting the ID and the name because this is what we are going to pass from here we have those two public properties this is what I'm going to do I'm going to put debugger right here whenever group is deleted it should come right here here any side group deleted I'm going to pass ID needs to be event ID okay and name is going to be event name now I'm going to listen to this group deleted inside chat layout and remove that group from the conversation so we're going to open chat layout wait before I do this so inside here inside this use effect we listen to uh group deleted but we have to leave that as well if the conversation is group so right here we are leaving this channel but also we need to leave the channel if the conversation is group we're going to leave group deleted Channel with the conversation ID as well okay this is correct now let's go in the chat layout um and this is the place we are listening all kind of events including this group show model so here I'm going to listen group deleted okay uh let's actually do this right here const um off group delete this is going to be on we're going to use group. deleted we're going to get the group but but more specifically we're going to get the ID and the name so ID and name this is what we're going to get uh okay cool we're going to also call this off group delete from here uh to unsubscribe to that specific event okay but here inside that callback we are going to do a couple of things first we are going to remove that from the conversation so we're going to actually call set local conversations here we're going to get old conversations let's call this conversations and we're going to filter old conversations and we can call this conversation we're going to filter this by ID and remove the current ID which needs to be deleted and we can put this we should put this on a single line in my opinion Okay cool so now we have these new conversations meaning that the convers has been completely removed from from the left side that group has been removed okay cool we're going to also show notification so I'm going to use emit right here and I'm going to uh use toast. Show event uh with the proper message the message is going to be group name was deleted and the third parameter doesn't exist so we can remove it please uh Group whatever was deleted and the last thing what I'm going to do is to check if the selected conversation exists and so if selected conversation is group and if the selected conversation ID is the same ID we are trying to delete right now in this case I am trying to delete the conversation which is also Group which is right now opened and active and whenever there's an active Group which we are trying to delete we have to redirect somewhere right so in this case I'm going to use rotor dot visit and I'm going to provide the root dashboard so I want to redirect user to dashboard whenever the user is trying to delete the conversation which is right now active so this rotor is something we're going to import and we're going to import this from inertia JS react so here we have this router and now I think we can test this now I'm going to open browser to test this first of all we're going to have some errors I love errors because when we fix this that's going to be great catch is not a function inside listen okay and this is inside the authenticated layout inside we have this Echo private listen catch okay what's that we don't have catch sorry we have error that's right that should be error no okay let's reload the page okay that was the only error right now and this Eric braus right now is actually a group which is right now opened and if I try to delete this right now we should be redirected to dashboard if everything works successfully but let's try this I know what's going to happen but uh I want you to also understand what's going on um step by step so whenever we click on delete click okay the request has been sent we receive this Response Group delete was scheduled and will be deleted soon and the group ID is first okay so whenever this message is returned after 15 seconds from this group controller it should start deleting this group from the database but if we check right now database we will still see this first one because this is dispatched in its own group and um in its own Q excuse me and the Q is not started so we are not listening to Q so in this case we are going to use one of the Q related U commments to listen to Q PHP Artisan uh q and we can provide D Dash help so we have two main functions U sorry two main commands to listen to Q it is Q listen which listens to given q and Q work which which starts processing Q uh on the Q as a demon I think this is pronounced as demon okay so the Q work is exactly what we're going to you in production but this Q work has the disadvantage that whenever files are changed the qor doesn't reload and it's going to still pick up old file content in our project so what we need is Q listen let's execute PHP Artisan Q call on listen hit the enter okay it is now look at this look at this what is this print it is coming from this delete group Job Line 45 this is it okay so everything happened this happened this happened everything happened and this was printed from the following line and we can check there are a lot of data we have all those messages as well um with relations and properties and I think everything is there it's a pretty large uh dump result so we can't actually see everything but the thing is that that code was executed so group should be deleted from the database okay I'm going to remove this I don't need that if I reload this right now I don't see group without ID one anymore in the database and you know why now if I reload this we're going to see not found group doesn't exist let's go on the homepage and I'm going to pick up another queue this Hera Welch maybe let's try to delete this right now but before I do this let's reload this and I also want to check that the listening to delete of the group has been actually established so here is the group with id5 so if we observe the payloads here we are listening group deleted 4 here we're listening um message group and we can check we need group deleted five this is it okay so the connection the authentication has been passed which means that whenever we emit something in this group in this private group group deleted five we should receive this in the socket right here somewhere cool and we're also listening to this queue if we don't listen to this queue uh this was by the way failed because we had this um dump cool I'm going to actually stop this and restart this okay now let's try to delete this group click delete click okay and after 15 seconds it's going to be executed right here okay it needs some time because we have this 15sec delay and we should see the result after 15 seconds right here here we go and we see the result is done so group two was deleted and look at this also we uh receive socket emit right here so inside Network we also see group deleted has been emited inside sources we have that entire event which contains ID and name now if we proceed there was one error am it is not defined in the chat layout that's actually correct if we open a chat layout we are using image right here somewhere right here to show the toast notification but that emit was not defined so let's define this oops AIT cool cool now if I reload that group is also gone because we also deleted this let's select another group let's try to delete that and we should be redirected to dashboard immediately and we should see should see success notification now let's clear up let's wait for a few more seconds let's check the back inside okay now it started processing the debugger came right here group Devon hilpert was deleted but we we are not actually redirected to the dashboard and this is something we can observe further more if we go in the authenticated layer out or we need to check this part yeah this is um I think I know the reason so sometimes the selected conversation in this chat layout is actually empty so if I print this I had this experience previously so here what we can do if the selected conversation doesn't exist or if selected conversation exists and um it is group and the ID matches then we redirect to dashboard maybe we don't need this if it doesn't exist or if it exists and whatever so let's put also group name right here in the quads and what I'm going to also do if we open um oops if we open conversation header listening to on delete group whenever we receive successful response we are going to get the message and the message is going to be res here I'm printing res which even doesn't exist so result um let's print the message console.log res. dat. message is going to be the message so I I think we can simply take this message and display it right here but let's just print console.log enti rest cool let's reload this this is also gone let's go on the hom page choose another group and let's try to delete this group and we should see this message okay here we go group delete was scheduled and will be deleted soon soon and after some time we should be redirected to homepage let's actually change this into five seconds the debugger comes right here we need to proceed and we will redirected to the homepage Group whatever was deleted and we don't see this group anymore right there okay this is cool in the chat layout now in the authenticated layout we should remove this debuger I don't want this anymore now we have this 5c delay so I'm going to select the one last group I'm going to try to delete that group was uh group delete was scheduled and will be deleted soon and we immediately see this the group was deleted after some time okay the optimum time will be probably like 10 seconds maybe and I'm going to also go in the toast jsx and very small screens this one has minimum width but on extra small screens or maybe what we're going to do is W full but extra small screens we're going to have um W Auto okay so deleting is working now we're going to test creating the group let's define now JavaScript we can just provide some lur msum doesn't matter we're going to select the users Jane this this and this four users have been selected let's go in the network uh let's go in the Fetch and click on create group was created and we see this twice okay we see JavaScript right here why do we see this twice uh let's reload this page there's only one JavaScript it wasn't created twice if I select this I see five members if I click on the description we see this lurm if I click on users we see all the users if I click on edit we have debugger somewhere we can search for debugger in the project this is it we can remove this click on edit okay okay okay on smaller screens we have to adjust this part as well and this is coming from um I think it's from group model right or I think it is user picker maybe if we scroll down below in the user Pier we have this selected we have flex and we need Flex WP okay that's much better okay this is good um let's update description and I'm going to click on update and we see the action is unauthorized and I think the reason is that we have not actually implemented update group requests so I'm going to change this into true scroll down below I'm going to return description which is going to be it's going to be nullable and string we're going to need user IDs actually let's open store group request and I'm going to part uh copy these user IDs part and put this in the um update group request so user IDs needs to be nullable and array and each of the user ID needs to be integer and it must exist in the users okay now let's test this update again I'm going to click on update and it was returned successfully W uh group JavaScript was deleted okay um let's try to edit this again this message is on full screen which is not bad we can leave this okay let's add one more user somewhere this one for example click on update and we have six members right now that's great what else do we need so we have group update we cannot change the name we can update the description which works we can remove every single user from here and whoa what's that the group with was shrinked a lot so here I think what we need is to add uh display this model either on a full width or display this in the center but I think we need to display this on full width so let me open model jsx and uh this is the dialogue which has fixed and flex and let's give it w full for example it needs to be stretched on full width uh this is okay so this one this is now stretched on full width or it is not uh it is not stretched on full width we can provide me W full as well uh I think the class is also called bases bases full okay um justify Center I really don't like spending um too much time on CSS so this is something minor in my opinion so let's put this in the center okay this is how it's going to be I'm going to give it also um this one is fixed so [Music] minimum W like 300 pixel no it is not working that should be probably panel maybe yes me minimum width full okay and above extra small we give it minimum width of 300 pixel maybe 420 pixel or above small not excess okay that's good so let's reload the page I'm going to click on edit I'm going to remove those users all of them and I'm going to click on update there is only one user and that user is me um this is good good now let's add a few more users I'm going to add Jane there as well and somebody else let's click on update and now I'm going to open guest again and I'll authenticate using Jane cool and Jane has JavaScript right here okay now whenever I'm going to reduce this whenever we delete by the way this edit and delete button is not visible for Jane because Jane is a regular user but whenever John is deleting that group Jane should receive notification as well let's wait here we see that and both John this is John and Jane as well we are redirected to the homepage and there is no JavaScript group this is exactly what we wanted I just noticed these zeros right here for Jane user and I think I know where this is coming from uh from conversation item uh it it should be coming from from here if the current user is admin and I think this is returning zero and it is outputed right here so if I remove this completely that zeros are actually gone so if the current user is admin let's use double exclamation right here to convert this into Boolean otherwise this is admin is an integer zero which is actually false but this is printed right here now the zeros are not there they are gone now I'm going to implement uploading Avatar images for the users from their profile page so if you open profile page right here I'm going to put Avatar upload input right here send the uploaded Avatar chosen Avatar and I'm going to handle this on the backend side let's start with the backend side and I'm going to open profile control controller which is coming from this up HTTP controllers and that was actually generated by laral Breeze okay so here we get the um this is the rendering of the profile form and this is the update and this is where we need to um involve so uh let's go in the profile update request first and I'm going to add right here Avatar this is going to be nullable it must be image and it must have the maximum size one megabyte um I think I don't need anything else right here so I'm going to close this now I'm going to get this Avatar right here and this is going to be request file Avatar okay then I'm going to get the currently authenticated user equals request user then I'm going to also get the validated data so data um is going to be request validated okay and right here uh actually I'm going to also call on user I'm going to call uh Feld but I'm going to pass the entire data right here and this is the place inside where I'm going to uh save the avat I'm going to check if Avatar exists then this is what I'm going to do so I'm going to save that avatar on the file system and put the Avatar path inside data so if we check right now the users table in the database there is an avatar okay I thought this was called Avatar path but this is called Avatar so what we're going to do is Avatar is going to be to save that Avatar inside um Avatar's folder okay let me think um there might be also a case that existing Avatar uh there's an existing Avatar in the database so we're going to check if user Avatar exists then we're going to delete this user avatar from the file system and let's just import this facade okay so this is one step Second Step oops second step is to generate unique name for the Avatar file so let's just generate unique file name for the Avatar and this is going to be Avatar name equals so I'm going to use unique ID Avatar let's just remove this unique ID of Avatar then Dot and concatenated this with the Avatar gate client original extension okay so we're going to have unique ID for every uploaded Avatar and this is going to be our Avatar name after this we are going to save the avatar on the file system with a custom name and this is going to be Avatar Store is I'm going to provide the path inside which we're going to save this and this is going to be avatars I'm going to also provide the file name Avatar name and I'm going to also provide the disk that need this needs to be saved inside public dis after this commment is executed it's going to return the path Avatar path we're going to take that Avatar path and save it inside data Avatar let me think now we actually need to save this inside user Avatar it can also be data Avatar to be honest but no let's just put this um in the user Avatar because data Avatar is is going to be uploaded file because we are uploading on the same path data Avatar might be might might be the file so let's put this in the user then what we're going to do is to delete using unset I'm going to delete Avatar okay that should work so we're getting the Avatar as a file we're getting the user we're getting the data if Avatar exists we are going to check if the user has existing Avatar we're going to delete this we're going to generate the uni file name for the Avatar which is going to be this we're going to save this avatar on the file system with this unique name we're going to put this under user Avatar we're going to also call user field and and finally we're going to save this user so at the end of the day we're going to have new Avatar file of the user now I'm going to open profile slash edit js6 and here we have this update profile information profile adjs 6 is this entire file entire um view right here okay uh uh by the way the scroll is not working here and we have to recover this we are using this authenticated layout um to make this scroll working we are going to add overflow y Auto to the F following div so right here overflow y Auto should display the scroll now if we open this update profile information uh right here inside this data I'm going to put Avatar okay which is going to be user. Avatar actually user. Avatar is going to be the uh path of the user and that's not going to exist so let's just provide null right here this Avatar is going to be something we are going to send to the server side so if you scroll down below uh right here in the following area above input we need to Define new input for the Avatar so I'm going to duplicate this art uh the input label is going to be for Avatar the value is going to be profile picture uh so here we're going to have a regular input okay which will have Avatar um I'm going to give it obviously type equals file um let me actually check dayi because I think we have right here file uh file input okay yeah this is I want something like this file input with border maybe we can choose obviously um different colors but I'm going to choose the default one file input with borders so this is what I need P file alt label P file alt label okay I don't need these alt labels at all so this is as simple as that uh let me grab this I'm going to paste this right here format this nicely let's give it ID to be Avatar uh we're going to provide the value um the the value actually let's delete this the value should not be given but we're going to listen to unchange and whenever unchange event is triggered we are going to get even Target files at zero and I'm going to put this inside data Avatar this is actually correct this is how I am going to do it we save it um down below of this input let me display a paragraph with some hint information like please upload square picture and we can provide example to be 5002 um pixel and let's use times um 512 pi pixel let's give this one um small margin margin top one uh text Gray 400 let's have a look in the browser okay profile picture let's choose something let's go in the pictures and I'm going to choose this one um I think I think we missed one thing so inside data uh right here we're sending patch request okay inside data we need to do the following so we need to provide underscore method to do like a method uh spoofing so we have to provide method patch we can't actually send the files using patch so that's the problem so if I remove this we're sending patch and we cannot send file through patch and the Avatar is going to be filed because we are setting this right here okay so what we're going to do is provide the method to be patched but actually we are going to send the post request so here we need post and here we need post as well but because we are sending method patch LEL will take this as a patch request okay so let's try this again uh let's click on Save and we don't know if this worked or not right uh we can see this on second user uh John still has J right there we're going to check the database as well but I'm going to also use right here let's use user Avatar providing the user let's remove everything and I'm going to provide profile equals true so I have this user profile uh user avatar for profile okay good good uh now we choose the file I want to check what is coming on the backand side so let's go in the profile controller and let's see what is the avatar and click on Save okay something is something is going on inside errors we see the Avatar field must not be greater than 1024 kilobytes um okay we don't handle this error properly um if we go in the update profile information here we need Avatar now we see this error okay good is this file larger okay uh let's choose something else which we know is is should be smaller uh let's choose my logo okay now the uploaded file was received if we go in the profile controller we can remove this and I'm going to resubmit this it was saved however I should see my avatar right here let's check now the database John has avatars Avatar something let's check now um from the profile controller when we are rendering this we are passing this request user uh no we have that user as taken from the AL user uh we're going to check um handle handle inertia JS request because the request user right here is just request user and I think that should be user resource so if request user exists okay uh we can use new user resource providing request user otherwise is providing null or instead of request user maybe use um out ID now in this case we are returning the user resource and this user resource is returning the Avatar URL properly formatted so if I check this right now I see my logo my avatar and if I check in Jan Do's browser jundo right here should have that Avatar okay why it doesn't have let's check this user.php uhhuh this is the reason so like we are returning inside user resource this Avatar URL we need the same thing right here now let's check this in the browser okay this is kind of broke no it is not broken so here we see the Avatar now I'm going to go in the Jano profile and I'm going to choose something else let's go in the pictures I'm going to pick this up uh click on Save okay this this is very large image um let's choose something else I want to CH something else maybe this is smaller no that is not smaller anyway so let's um let's go in the profile or what is the request called profile update request uh and we can increase this into 20 48 let's save this and let's try to upload this again is it even more let's choose anything okay I don't care that was saved we see this right here and John do is gonna see now the following profile picture during communication now I'm going to add functionality to add new users which is going to be only available for admin users in the header next to the profile name right here I'm going to add a button to add new user and again this is going to be only a available for admin users whenever we click this we're going to show a model with name email and a flag checkbox to mark that user as an admin user and basically just like this whenever the new user is added I'm going to send the email to the recently eded user with a random password that his or her account was just created inside the system and they can log in and obviously they can reset the password as well now I'm going to open VI code bring up the terminal and I'm going to create a couple of files first of all I'm going to create user controller for backend phpr Aron make controller user controller I'm going to also create admin user middle weer and use that middle weer so that user controller can only be accessed by admin users so let's generate middle wear PHP artisan make middle here now let's call this admin user let's hit the enter okay so on the back hand side we have controller we have middle we now let's open the controller and I'm going to Define couple of roots basically I'm going to need uh three main roots not Roots but uh actions right here okay so I'm going to Define um store action to create new user um and obviously we're going to accept right here request okay I'm going to Define also two more functions one is going to be change role and basically we're going to have regular users and admin users admin users will be able to make other users as regular users or admin users and they will be also able to block the users or add new users so we're going to provide right here we're going to accept the user for change role that particular user uh needs to change the role and we're going to get the role information in the request data let's import the import the user right here and then I'm going to also Define uh block unblock function okay we're going to implement each methods one after another but now I'm going to to open web.php and I'm going to Define these roots right here but before this I'm going to um open this middle we what we just created admin user middle we and let's scroll down below and let's check if the authenticated user is admin user we only allow request in this case otherwise we're going to Bard okay so let's um actually I'm going to write this I don't like this uh suggestion okay so if out user is admin okay or let's just write if the authenticated user is not admin then we're going to call a boort with 43 okay we can provide additional MRA here here otherwise we're going to allo so this middle wi is as simple as that whenever we use this middle weer if the user is not admin user they will not be able to perform this action now let's open web.php and I'm going to Define these roots right here first let's define uh middle Weare group we're going to use middle wear and I'm going to Define an alias admin um for the for my group then we're going to provide group in function and then we are going to define the roots okay let's now Define this admin alas middle wear for this we need to open up. PHP from the bootstrap and right here when we have this middle wear variable I'm going to provide alas okay and the alas accepts an associative array where the key is the Alas and the value is the middle so the middle we is going to be admin user alas okay so this is how we're going to have this and also make sure that the admin user is imported and here it is imported in my case semicolon is missing right here so let's provide it now if we go in the web.php we're going to Define these roots root post for user we're going to provide user controller store method and the name is going to be user. store let's import this user controller cool now let's duplicate these two more times we we're going to always have post requests because we are performing actions um and we cannot do these actions by um get we need either put or post okay and in this case I'm going to choose put uh we're going to call the second action to change DH rooll and we are going to also provide the user for which we are trying to change the role the action will be change rooll okay let's move this name down and the name is going to be user change Ro and the last one is going to be user slash block Das unblock uh we need slash right here and the action is going to be called block unblock and the action is going to be also block unblock now we have the controller created we have defined these roots we have created middle we and we are using this middle wear the only thing right now is to implement this user controller right here here now let's validate the request and get the validated data I'm going to call request validate and provide the rules I'm going to provide the name needs to be required and it must be string I'm going to provide email needs to be required it must be email and it must be also a unique email inside users email column and I'm going to provide is admin uh which is going to be Boolean after this I'm going to generate new random password and assign this to the user so using Str strr facade I'm generating this random password and make sure that the St Str facade is imported right here okay then we're going to encrypt that password using bcrypt function this rule password and assign this into Data password after this we're going to also Mark that user email is verified okay okay this is going to be now without this line email verified at will be null and user will not be able to log into the system after this we're going to already create the user and then we're going to redirect the user back and that user will already be available in the user interface now let's implement this change roll function so here we are going to get the current user for which we're going to change the rule we're going to get its is admin flag and we are going to reverse this is admin flag using this code and I'm going to assign this back into the user so this is admin flag is actually either zero or one using this code I'm converting this zero I'm converting into false and one into true and then I'm negating this reverting this so if it is true it's going to become false if is false it's going to become true after this I'm going to generate the message text user role was changed into something and that something thing is going to be if the user is admin it's going to be admin if the user is um not admin then it's going to be regular user after this I'm going to return a response with the Json whatever is going to be the following message finally let's Implement block and unblock we're going to first check if the user user is blocked I'm going to set user blocked at equals null that blocked at is a column in the database and we're going to Define if the user is blocked or not based on this column okay if it's blocked I'm removing this blocked and I'm going to also generate the message your account has been activated however if the user is not blocked I'm going to set user blocked at to be now this is the date when the user got blocked and this is the message your account has been blocked after this I'm going to call user save and I'm going to return response with the following message okay so the back end is kind of ready in the in the later lessons we are going to add email sending functionality right here okay so we're going to send email right here to that user that his or her account was just created right here we're going to send an email that the role was changed and here we're going to send an email that user has been blocked or has been unblocked in order to make sure that we test the newly created users what I'm going to do is uh comment this line duplicate and comment this line and the row password in my case is going to be something from 1 to 8 so this is just for testing purposes because we are not going to send the email uh we don't know what's going to be that random password so I'm just going to provide this row password to be from one to to8 for every new user so that we can test this before we Implement sending email so right here we are not even using this user so we can remove this now we can move on the front end side and I'm going to start with the authenticated layout so here in the header I'm going to create a button inside authenticated layout okay and I'm going to do this next to the profile section so this is the dropdown okay and next to this drop down maybe right here I'm uh this drop down I think is for the um profile right the username that's correct so I'm going to create this button next to this dropdown and let's use a primary button for this but we're going to only display the button if the currently authenticated user is admin if the user is not admin we don't display this button okay so if the user is admin this is what we're going to do so let's use this primary button component and I'm going to provide uh let's close the tag and inside here we're going to have user plus icon to a new user um and I'm going to provide um hate five with five uh let's remove this and I'm going to provide margin right to and the text is going to be add new user user then I'm going to listen to click of this button and we're going to get an event and I'm going to uh call a function new function set show new user model and this is going to set the state let's scroll up and right here I'm going to Define this state this is going to be const um let me call this show new user model and the uh Setter is going to be set show new user model so whenever this is going to be true the model will be displayed let's scroll down below find the button and I'm going to execute that function uh with value with a value true to display that okay so the button is there the function is there let's check in the user interface okay we see the button I think we need proper CSS classes to display this let's provide this Flex okay this is how we want it now whenever we set this we need to display display this pattern right and I'm going to create Now new user model and then I'm going to use that right here here in this layout so in JS components app I'm going to create this new user model. jsx let's collapse the left side this component is going to be very similar to group model so I'm going to open group model and I'm going to copy everything from here and let's put this in the new user model and let's start changing this so instead of group model this is going to be called new user model we're going to accept show right here and we're going to also accept onclose function okay so in the data let's focus on the data first in the data we need uh we need name we need email and we need is admin which is going to be false by default we don't need anything else okay so we don't need this users um I'm going to actually remove this part and I'm going to call this function submit okay here we're going to call this prevent default then we're going to send the post request okay we don't need put so we can remove this we are going to send post request to user store whenever the new user is created we can show message right here user data name was created and we're going to call close model okay this close model is going to reset the form and it's going to call on close Okay we don't need conversations so we can remove this we don't need group we can remove this we don't need page we can remove this and we don't also need on and I'm going to remove from here and I'm going to remove from below here we have this use effect which is listening to group model show we don't need this part at all okay on submit I'm going to call function called submit oops okay the heading is going to be create new user so we're going to provide the following Fields we're going to have name uh it should not be disabled so I'm going to remove this part we are setting this name it is required and is focused by default let's scroll down below here we're going to have email and for email we're going to need also text input not text area so I'm going to copy and paste this text input and I'm going to change name into email and here we have this HTML for description I'm going to change this into email and the value is going to be email as well okay inside errors we are using errors description and I'm going to set this email to be as well however right here I'm going to remove this is focus because we can only have one is focused U inside this form okay we have those two inputs and the next one is going to be checkbox so I'm going to remove this user Pier completely okay and I'm going to use use checkbox component which is used inside login form as well okay I'm going to provide it name to be is admin let's provide it whether this is going to be checked or not it's going to be checked if data is admin okay and I'm going to listen on change and I'm going going to set his admin as well so I'm going to um get the event and I'm going to call set set data providing is admin and event Target checked let's save this and I'm going to have a look at this so This input label is going to be uh wait the label actually is going to be rendered differently let's search for this checkbox in our project we have this in login jsx let's have a look so we have this label um I think we we just need to copy this so I'm going to copy this and I'm going to paste this uh right here but I'm going to leave this input error for errors do e admin cool the name is going to be is admin as we already did uh here we need is admin as well and here we need is admin as well the text is going to be uh admin user okay cool we have this close model on cancel and the inside the primary button we're going to have create the disabled will be if it is in processing good now let's go in this authenticated layout I'm going to scroll down below at the very bottom and right here I'm going to create this um new user model so new let's use new uh user model we're going to provide show to be show new user model and on close I'm going to get an event and I'm going to call set show new user model providing false right here to hide that okay good now let's open the browser let's reload and check if we have any errors there are no errors and I'm going to click this add new user which shows this error show new user model is not a function where are we using it as a function where to have it as a function yep here we have it we need set show new user model let's close this click on the button and now the model is opened okay and we are going to provide the information regarding the user and hit on uh create just to test this functionality okay let's call this test the email is going to be test at example.com and let's say that this is admin user I'm going to click on create user test was created which is awesome we don't see also any err in the console if we check in the user section we see test right here so that was definitely created now admin user has just created new user test and let's send email um not email but the message to test hi test user the email was sent now let's log out and I'm going to try to log in with the test user test at example.com the password is from 1 to 8 let's hit login and the test user was logged in and here we see the message from join do high test user this is awesome so we have just implemented the functionality to create new users and also that user is admin user because that user sees this add new user right here now let's try to add one more user but in this case we are not going to mark this as admin user let's give it name test two test to at example.com this not admin user I'm going to click on create user test two was created now I'm going to log out I'm going to use test to email click on login I was able to authenticate and I don't see this button right here I also don't see three dots next to each user which is for managing each user so everything works as expected now I'm going to log out and I'm going to log in back with my join at example.com user now let's Implement blocking and unblocking of of the users the following functionality so whenever the currently authenticated user is the admin user they see the following options for each user so these are block user and make regular user this test user actually is admin user and because this is admin user we see this make regular user so Jano however is a regular user and we see make admin option right here okay let's first Implement block user and we're going to do this inside uh I'm going to close everything and I'm going to open user uh options dropdown next let's scroll down below and we have actually already these functions ready change user role and on block user okay and the roots are even used properly we did this earlier the only thing what I'm going to do right here is that in success whenever we get the response I'm going to emit the me message toast. show and here I'm going to provide user rooll was changed into something so whenever we click on this change user role uh from the back end let's open user controller we are returning a message this message so that message is going to be inside res data message I'm going to actually take this res data message and I'm going to show this toast notification the toast uh the emit is something we need to import so const emit um equals use event bus just like this and let's do something similar on block user right here I'm going to call P it toast show res data message now let's save this open the browser this is actually admin user okay and let's try to block this user okay I'm going to click on block user I see your account has been blocked actually this should not be your account has been blocked let's go right here this is your account has been blocked okay the message should be user whatever this is going to be let's use the actual username user name has been activated and down below we need [Music] user usern name has been blocked now let's save this this user is actually uh it should be blocked if I if I reload the page um no it is not actually blocked so let's click on block user we see user Jane do has been blocked okay now it was blocked and the blocked users are displayed at the very bottom and they are gray out slightly now if I click on this button which displays these options we see unblock user and we should see unblock user here as well locked users are not able to log into the system they should not be able to log into the system we are going to test if Jane do is going to be able to log into the system in a moment but before I test this let's test unblocking functionality as well so I'm going to click on the test user I'm going to click on unblock user and we see this message user test has been activated this is actually pretty cool only thing minor thing what I'm going to add is double quotes right here around the name here and we can use this here as well now let's test the functionality to Mark the user as admin or unmark it as admin or make it as a regular user test user is actually admin user so when I click on this drop down we see right here make regular user as soon as I click on this I see this message user role was changed changed into regular user let's reload this it the reload event is not necessary but just in case that there's nothing cached I'm going to click on this drop down and we see this make admin I'm going to click on make admin and we see that the user role was changed into admin okay this is pretty cool so we have right now functionality to block the user unblock the user uh make the user admin or make it regular user now I'm going to open authenticated session controller and I'm going to find the place where the user is actually authenticated this is the place the store and here login request is used inside login request there is a method called authenticate okay and this is the place where the authentication uh is attempted okay and before this happens what I'm going to do is to select the user by email and I'm going to check if that user is blocked or not so let's just write a comment uh select user by email uh okay uh for this I'm going to write the following code um user where email is going to be this input email first so I got the user let's import this user model model sorry user model and now we have this user and then I'm going to check if user exists and if the user is blocked at then I'm going to return with validation exception with messages on email your account has been blocked now if I save this Jane user is actually blocked now if I open my second browser where guest user is uh this is actually Jane do and basically on reload even the user needs to be logged out so this is another functionality which we need to do but if I click uh on logout actually we probably need to create Med we just to make sure that the user uh the blocked user is not able to perform any actions right so we need middle we for this but now let's try to log out and try to log in Jane at example.com we provide the password hit the enter and we see this email your account has been blocked okay this is great now let's come right here and activate make actually unblock the user Jane do user Jane do has been activated now let's click on login and the user was able to log in again now let's block this Jane user again and create the middle year whenever the user is blocked even if they are already authenticated and they try to reload the page or send a message right now even probably sending a message is going to work we are going to create this middle wear to block that functionality let's bring up the terminal and I'm going to execute PHP artisan make middle wear and I'm going to call this active user middle Weare okay now let's open active user middle Weare and here I'm going to check if authenticated users okay blocked at exists I'm going to call out log out and I'm going to redirect user to the login rout with error message your account has been blocked okay so I'm going to save this let's move this down like this and uh we are going to register this active user middle wear so we can directly open web.php and use this uh we should use this right here okay out and verified uh I'm going to add right here um excuse me I'm going to add right here that middle year or what I'm going to do is to create uh alas register this active user as an alas [Music] okay active user class make sure that the active user middle we is imported now we have this alas active let's go in the web PHP and I'm going to add this third middle we right here so the user must be authenticated must be verified and must be active as well okay so since we did that now the Jane is actually logged out okay uh I'm going to test this functionality once again so I'm going to activate unblock Jane do I'm going to try to to login now I'm going to block jane. gain and whenever that user is going to try to select messages to perform something uh they are redirected to the login page okay and they are redirected to the login page with message with error your account has been blocked but maybe that message is not actually handled so it's not that important so the main thing is that they are redirected to the login page and that's I think enough however if we open loging js6 scroll up and we're going to find this status place so if the status exists so then something is displayed so that status is a prop of the component and this this is actually coming from the controller so if we open Al PHP rotes find this login open this authenticated session controller and find the create right here which actually renders the login for here we see that session status is taken and given to the component to this out login Inside Out login we take that status and we display this however that status is displayed with text green 600 so this is kind of the success message so in the same way we can create we can duplicate this and we create error for example like this error here error there we can provide uh margin buttom font medium text extra small and text red 600 maybe and we um I'm going to call this error message here and here I'm going to accept this as a prop and I'm going to pass this as a prop from here error message okay and if we go in the active user uh when we redirect to login we can provide with error message to your account has been blocked once this redirect happens the code comes right here because we provided this with error message it's going to be inserted inside the session then we take this error message from session and give it to the component then from component we accept right here we take this and display this properly and nicely okay let's do this test one last time I'm going to unblock that user and I'm going to try to log in again with Jan example.com let's hit the enter Jane was uh logged in now let's block the user and Jane is going to try to open the conversation and we see this message your account has been blocked now I'm going to implement sending emails when the new user is created user is blocked or users role has been changed and we're going to do this from these three methods but to do this we're going to create new mailable classes let's bring up the terminal and I'm going to execute PHP artisan make mail and let's create user created mailable class let's hit the enter it was created under up mail user created now let's change this name into user blocked unblocked let's hit the enter and the last one is going to be user roll changed okay so we created these three mailable classes I'm going to open user blocked uh let's open user created and user R changed as well and let's go into user created and understand what is happening right here so if we scroll down below we're going to find this envelope okay so we create this envelope and we provide this subject whatever it's going to be user created okay um and in this case we're going to send an email to the user itself like if admin user created let's say John Smith John Smith will receive an email and this is going to be subject we can change the subject and call this your account has been created let's put this in the lowercase okay your account has been created or we should put everything in uppercase um because this is kind of a subject okay now if you scroll down below inside content this is uh the view and the view basically doesn't exist uh in this case the view is dami view it's a view. name but I'm going to provide this to be mail user created mail user created so inside mail folder under resources I'm going to create this however I'm going to use markdown approach so in this case I'm going to change this view into markdown okay LL has support for uh standard blade views as well as the markdown views so in our case I'm going to create markdown view file markdown um file actually blade file okay awesome so we have prepared the content we have prepared the envelope the only thing what we are going to do is to create the following resource file but let's focus on other um other mailable classes as well and then we can create those blade files for all of them inside user blocked unblocked main label class we can change the subject and basically in the following class we are going to accept the user uh to which we are sending the um email and we're going to also provide that user will have the role itself so let's accept this public user right here user and we need to import use up models user semicolon okay so now since we have this user we can already use right here the proper subject and that subject can be very similar what we generate uh in the uh message right here user Ro has was changed into whatever so let me take this message put this right here we can call this subject and your role was changed into this user is admin whatever okay so in this case the user for which the role has been changed will receive an email your role was changed into admin or your role was changed into regular user and we can use this subject right here okay awesome um in the view right here in this content we're going to change this into markdown and I'm going to provide right here mail user uh blocked Dash unblocked and this is the this is the name actually which um we are inside block and sorry so the message is not correct uh that should be this one has been activ activated or has been blocked um so this should be for something else let's put this right here user uh in this case it's going to be your we're going to comment this out we're going to move this into another file your um your account your account we can remove this has been either activated or blocked based on what is the flag of this user blocked at okay so your account has been blocked or activated let's put this uppercase a and this is going to be subject and we are going to use this subject right here however that subject is going to move into user Ro changed file let's review this so we accept the user here your account has been blocked if user blocked it is something uh otherwise it's going to be activated okay and the inside the content we are going to have um mail user blocked Das unblocked okay that's cool let's let's go into user R changed let's scroll down below and this is going to be our subject let's uncomment this save this and here we're going to accept public user user and we need to use app modules user and this is going to be good and correct so we provide subject right here inside content I'm going to change this into markdown and the file is going going to be mail user roll Das changed we don't provide attachments so we prepar the mailable classes and we now need to create those blade files let's go inside resources views and here I'm going to create mail folder inside I'm going to create user folder and inside there I'm going to create created do blade. PHP this is one and this is the pl blade file we are using um inside user created mail inside mail folder user folder created blade okay good let's now create second blade file this is going to be blocked Dash unblocked do blade PHP and the last one is going to be roll Dash chain ched blade PHP oops blade PHP okay now I think we can close these mailable classes because we have them prepared so I'm going to close this uh I'm going to close this user R changed and user blocked and unblocked okay let's also go into user controller and let let's try to implement sending emails so from here I am going to send an email using mail facet so we're using mail facade I'm going to provide to in here I'm going to provide the email to which or email or user to which we're going to send um an email and the user uh is something uh which is created so let's get this user okay and we're going to send the email to that user so mail to then we're going to provide send and inside send we're going to pass mailable class okay so inside send we're going to pass new user created Created and we're going to pass which user has been created okay user and we're going to also pass the password which was assigned to that user user we're going to send this password by email to that user so that the user can log into the system and change the password so in this case we're going to send row password as well now let's open user created and that user created needs to be also imported up mail user created that's correct now let's go inside user created and inside Constructor we're going to accept two arguments public user Us in public string password okay so let's import this model use app model user now we have this user we have this password and by default all public properties which are defined in this class will become available inside the file inside the markdown so inside inside markdown file we will be able to use the user as well as the password okay and we can use it and we can print that information now if we go in the uh user controller so this is how the mail is going to be sent let's scroll down below and let's send the second email right here let's use mail to we are going to send this to the user um sorry for this and in this case I'm going to provide um I think we need to only provide user role changed so we just provide the user for which the role has been changed this new uh user Ro changed the the mailable class needs to be imported so once this is imported we are good to go so if we go in this user R changed we see that we are already accepting a user right here we are using this user to generate the proper subject and because this is the public property it's going to be also accessible inside this Mar markdown file user roll roll changed okay let's go back into controller scroll down below and we're going to implement the third mail sending email to user um send to new user blocked and unblocked this is going to be the mailable let's import this and also inside user blocked and unblocked we have this public property defined we are properly generating subject and because this is public it's going to be accessible in the markdown own file now let's uh we can close these mables they are kind of ready uh we can even close this controller and I'm going to first create this blade created blade PHP then inside the blocked unblocked and then inside roll changed blade file in these blade files we can use the proper markdown syntax to style the messages as we want we can use the public variables that are described inside mailable classes but there's also possibility to use other components right here okay if I start writing anything um at the moment uh that's going to be the email that will be sent to the user but I want to define a generic layout for every email okay nice looking I want nice looking emails and and that's why I'm going to have this generic layout for every email actually in lell whenever in with leral Breeze for example whenever we register we create an account or we try to reset the password uh we receive an email which has nice looking layout with nice pattern inside there okay I'm going to try to reuse that same uh layout template and for this we have to uh Power publish the mail files let's bring up the Lal documentation and I'm going to search for mail right here click on this um and what I'm looking for right now is to publish okay customizing the components you may export all the markdown mail components to your own application for customization to export the components use the vendor publish Artisan command to publish the Lal mail asset tag so this is exactly what we're going to do so I'm going to copy this command bring up the terminal and I'm going to paste this hit the enter and this is going to create this is going to copy from this vendor LEL framework Source illuminate mail resource views into our project resources views vendor mail and this has been done let's go under resources views vendor and here we see this mail and inside here we have this HTML components and we have text components and we can observe this we have also Right Here theme and styles we have the default CSS and the CSS is basically for styling these mail labels and we can scroll down below this is pretty large CSS and uh this is done very nicely okay for example I want to find a button somewhere here we see this button so we have button red also button error we have button green or button success and we have button blue or button primary okay so we can change the colors if we want to customize this in our in different colors we can add more classes right here we can customize these as we want now let's have a look in the button blade PHP this is the button everything inside Lal Mals are built with tables because tables are the HTML Elements which is rendered by every male client without issues but if we use Dives or other HTML elements in in our mail labels some of the male clients maybe uh Office 365 Microsoft some of them might not uh take them properly and might not render them nicely okay so here we have this button blade and this is regular component regular lateral component we have defined props with URL uh which should be required we have color which default is primary we have align which by default is Center and we are using these variables inside here inside this HTML um table trtd we have a nested table as you see and so we don't need to understand everything what is going going on right here but this is a button created by table okay and again as I explained uh why this is created this because to be uh rendered nicely in every client so if we have a look in the footer we're going to see something else uh the slot is rendered and pared uh nicely with uh the proper styling we have this header as well we have the general layout which includes all the boilerplate code dock type um we have this HTML tag and everything with its own media CSS uh Styles as well and uh we have this message which extends this mail layout it is using this um header uh slot and inside the header we have this xmail header with its own URL and config name we have this main body we have this sub copy we have this footer and that's it okay we also have this panel and we have this sub copy and basic table so this is these are all the components that is available and this is inside the HTML inside text obviously we have everything just uh for the email clients which don't render HTML and only render text so now let's close these files this is the sub copy panel message uh layout header footer button and maybe this one as well so we have this created blade and now I'm going to use the following components and render my own custom email with the same layout okay and again you have all the possibility to customize that so here I'm going to have xmail message okay inside here we're going to write hello user whatever is hello John Smith uh your account has been created successfully here is your login information and we are putting this uh using according to markdown syntax we are making this um uh bold okay so here's uh your login information we're providing email and providing the password that password is the public property inside user created mailable class that's why we can directly access this so here we have additional text please log into the system and change your password and here we're going to have this login button okay we're going to provide the URL to the login and we're going to provide the text click here to log down below we're going to have thank you with BR and the config application name the application name uh is going to be whatever we Define in the file okay so this but as we saw is a component which accepts other props like it accepts a color and also a line and we can provide right here different color like color equals green and we're going to have this button in green Styles okay cool now let's go in the blocked unblocked plate file and implement this so again we need this message inside here hello user whatever is John Smith for example then we're going to check if the user is blocked then we're going to write the message your account has been suspended you are no longer able to login else we're we're going to write your account has been activated you can now normally use the system okay and right here when the account has been activated I'm going to also put the button with URL to login click here to login and we're going to have also thank you text with BR and the application name and the last thing is to implement this rule changed again we need this message hello user we're going to check if the user is admin we're going to write the text you are now admin in the system you can add and block users if it's a regular user then your role was changed into regular user you are no longer able to add or block users and we're going to have BR right here thank you message with BR and the config appli application name so please keep in mind that if you have uh in your vs code or in your other editor doesn't matter setting um activated format on autosave okay format on uh format on Save search for this so if you have the following setting activated whenever you save the file it's going to reformat your blade file into like this okay depending on uh also what uh formatting plugins you're using but like it might change the file so if it is changing the files adding these wi spaces or tabs in front of it you don't need this because that is a markdown spacing here actually matters and the actually rendered message will be different with this and it's going to be different with this so you're going to leave it like this everything on a at the beginning of the line so you don't need white spaces at the beginning of the line right even right here generally if I'm writing a normal HTML components markdown then I have it like this and generally I format my code like this as well but you should not do this in your markdown files okay so in this case what I'm going to do is to deactivate this format on Save generally I had this activated for uh like so far so far in this tutorial I have this activated but now I'm going to deactivate this because I don't want it to format reformat my markdown message so I'm going to save this and yeah we're going to have the same format it we don't want this indentation here we don't want it like this like this like this no you don't need to do this even though this is not much less readable than what I had right now or we should leave it because this is marked down and this is how it should be okay so we have files ready we have mail lables ready we have the controller ready and I think right now we can test sending email let's go in our application and I'm going to uh just block one user and then reactivate that user and we're going to see the result inside storage logs Lal log file I'm going to delete everything from here okay and why are we going to see this inside L log file because our uh mail driver is set to log if we search for mail something we see mail mailer is actually set to log okay now uh I'm going to also change the application name so this is the application name right here and uh I actually really like the name Lara Chet okay so I'm going to call my application like this um maybe the domain is not available but uh let's leave this name larad so I'm going to save this um okay let's authenticate okay and now I'm going to try to block this user test user and I'm going to open L log file to see the actual message so let's click this block user let's wait and we see user test has been blocked and here we see the mail so here we see it is sent to um test example.com the subject is your account has been blocked we see this is sending from lat hello example.com um and this is a text message okay and if we scroll down below we're going to see this text HTML message and what I'm going to do is just uh grab everything right here and we can go into any uh online based HTML reviewer we can open code pen for example let's click on this and we can start coding and I'm going to paste this HTML right here let's change this layout like this okay and here it is so we see hello test which is the username your account has has been suspended you are no longer able to log in thank you Lara chat okay uh if we go right now inside uh browser and if I activate this user P test user uh let's unblock this we are going to receive second email this is it so that was the previous email this is the second email so let's copy that second email HTML content let's put this right here and the message is going to be hello test your account has been activated you can now normally use the system and here we see this click here to login which has link to a login page of our our application at the moment we are already authenticated so it didn't do anything uh special it didn't show the login form but this is is actually uh this is actually good uh let's test uh I'm going to uh unblock that user as well so here we should see third message this is going to be it and this is going to be to Jane doll so we should see the proper name here hello janeo your account has been activated okay cool now let's change the user role so I'm going to change this into admin but first let me clear up this lateral log because I have a lot of emails there um where is this so make admin okay your all was changed into admin now here is the whole HTML I'm going to copy this paste and let's have a look hello J do you now admin in the system you can add and block users okay and let's try to add new user as well let me remove everything from Lal log add new user let's call this John John Smith uh um Smith example.com because John at example.com is me and I'm going to mark this as admin user I'm going to click on create here we see this message and let's grab this HTML put this right here and our message looks like this hello John Smith your account has been created successfully here's your login information email Smith example.com password is 1 2 3 4 5 6 7 8 and please log into the system and change your password so we should be able to log in right now with Smith example.com from 1 to8 however I want to test this once more I'm going to delete everything from L log I'm going to go into user controller and I'm going to change this hardcoded new password into a random password so I'm going to leave it like this let's come right here a new user I'm going to call this bread traversy uh bread at example.com now we're going to make bread admin user and let's click on create so inside leral log we're going to see this email let's get this and paste this right here now we should see Bread's uh credentials okay bread at example.com and this is the random password so I'm going to copy the password uh and I'm going to try to log in with user bread so let's open the second browser and let's use bread at example.com with the random password uh click on login and bread was authenticated we can go into Brad travers's profile page and Brad should change his own password from here and that's going to be it okay so just like this we have now implemented sending emails with nice layout in our application so I'm going to also mention that right now every email because uh we have configured it like this every email comes inside Lal log file whenever you provide the proper email credentials right here you proper provide the proper host Port username password encryption your sender and uh the sender name as well it's going to actually start sending the email and you're going to also change this into proper mailer like there are several mailers in um LEL documentation you can have a look there is Amazon sees you can Pro provide SMTP with SMTP credentials and you can find this in the lal's official documentation so okay at the moment whenever the message is deleted which is not the last message we have the following error so this returns with 500 and the error is basically the following let's open vs code I'm going to close everything the error is following so inside message controller right here uh we are using this last message variable but that last message variable is not defined because nor this if neither this if has been satisfied because that was not last message so what we are going to do right here is to Define this last message to to be null by default and once we do this okay even though this message has been deleted um it wasn't gone from the user interface because of this error now if I reload this we're going to see that this message has been gone and if I try to delete a message which is not the latest message right now it will be removed and it um and it is not going to return an error was it removed now here we have three messages I'm going to delete this one by the way when the messag is deleted the scroll goes at the very bottom this is something which which I think is not ideal which should not happen so at the moment I know that this should not happen but at the moment I'm going to leave this as it is because I have to focus on deployment this project on production now I'm going to start working on the deployment part I'm going to use hostinger as a choice of the deployment we're going to grab BPS hosting and we're going to set up the entire project with Lal Reverb as well and assign custom domain to it okay it's going to be exciting it's going to be very interesting so be attentive and sit down and just follow with me okay so let's go to the hostinger age panel once you register on hostinger and grab your VPS hosting then you will be redirected to the hostinger age panel this is the age panel from which you can manage everything regarding hosting your so on the homepage you're going to see just overview of your services you're going to see your hosting your domains your emails VPS and so on so you have obviously dedicated menu items for each of them for websites hosting emails domains and here we have this VPS menu item once I click on this VPS menu item you're going to see your VPS server right here and it's going to about it's going to be probably um unset up so we're going to have the status pending data entry right here okay so I already set this up for testing purposes this is it it is running but I also have unset up VP S servers as well which I am going to set up right now okay so let's choose this one which is created earlier I'm going to click on setup and we just bear with me and we're going to follow certain steps it's going to take also some time because it's going to install uh all the operating system it's going to set up the Ubuntu latest version of Ubuntu we're going to go with the LEL version and we're going to also have a cloud panel installed inside the uh server which will help us to manage our LEL application and also gives us possibility to add more websites on the same VPS server it's going to be pretty easy setup we're going to just uh follow from wizard from browser and it's going to install uh the web server which is going to beineix it's going to set up um couple of versions of PHP it's going to have PHP fpm running MySQL will be there redies will be there and we're going to have bunch of additional features so again bear with me it's going to be really interesting okay so you're going to choose the location which is probably close to the audience of your website there are a couple of locations you can choose whichever you want North America South America Asia India and so on so I'm going to stick with France which is close to my location because I live in Georgia but again you can choose whichever you want okay I'm going to click on continue right here and then we have a couple of options so we have plain OS option and we can choose the OS well whichever OS we want you can choose sent Os or you can choose K Linux you can choose dban and so on and the latest versions are there at the moment latest version is to 2204 you can choose with this and install this as a plain OS but what I'm going to uh install is uh OS with panel okay and we have a couple of options we have the C panel option PL virtual mean and so on and uh we can choose uh whichever we want from here but we also have a separate tab OS with application okay and this is what I need if you scroll down below we're going to find Ubunto with different applications okay Ubunto with Docker for example we see Ubunto with gitlab and so on and here we see Ubuntu 22 version which is the latest version at the moment with L okay and this is what I'm going to choose so I'm going to click on select and again whenever I select this it's going to set up the latest version of Ubunto and it's going to install a lateral application as well running on certain it's going to be test Tomy by the way before you select anything you can have a look and you can find out that there are tons of options right here okay you can have magenta you can have mean St M St with react and node.js you can have M St Maven St however you want to pronounce it with vuejs and just scroll down and have a look okay we have bunch of bunch of options right here and it's going to be great so let's choose right now buun 22 with Lal I'm going to click select right here then we can install additional features with our uh VPS which um maybe we don't want so I'm going to untick this so it's a malware scanner I don't want this so I'm going to skip this then click on continue so and we have to set up the P the password for our panel and this is going to be the user interface in the browser uh Cloud panel user interface uh we we have to set the password and whenever we set the password basically we will have access to everything in inside our server we can manage the PHP version create MySQL servers uh we can uh configure PHP settings we can even configure engine configuration a virtual host configuration file and there will be everything what we want to manage so let me set the password I'm going to set something which is going to be actually secure password hold on okay so I choose the password I'm going to click on continue okay and this is our VPS host name which is going to be reserved name created for us okay and we can um we cannot modify this part uh if we want to access this using SSH we can add right here SSH key which actually um I want to do okay so I'm going to click on SSH key if you don't have SSH key it's going to be fine you don't need uh it's not like kind of mandatory but it's going to be good to set the SSH key because that's um a little bit more secure way of authentication through SSH um but you can also indate with the password and as you see right here it is optional but I'm going to set the SSH key and for this I am going to open get bash which is my preferred choice of terminal on uh windows and let's expand this on full screen I'm going to zoom in and I'm going to grab public key from my SSH ID RSA um HB file this is the public key so I'm going to grab this I'm going to copy this and if you don't have that public key in your SSH folder you can always generate this with a certain command okay let me show you which command you can use to generate this okay so here's the command it's going to be SSH d e gen- t RSA DB then we're going to provide right here the length of the key it's a 496 then we're going to provide right here- C which is going to be the comment and general practice is to provide your email to easily identify this but you can obviously provide other name because this is going to be actually comment okay so once you hit the enter right here it's going to generate public private keys if you don't have that and it's going to put this under SSH folder in your home directory but because I already have them generated I'm going to execute this um and by the way if you don't have git bash you have to use maybe a p gen to generate these public private keys and again if it's too complicated for you you can skip that public private Keys you can uh completely omit that part and we can authenticate using uh password so now I'm just going to put this right here the name is going to be my um home PC this is just my name um okay and here we have also the command how to generate it and this algorithm is actually a different algorithm to generate public private Keys okay so in our case we had the algorithm called RSA but this is a different algorithm okay I'm going to click on Save right here and that was added now we're going to also set the root root password and if you don't want to generate public private Keys you can always set the root password and you can authenticate with that so let me set the root password okay then I'm going to click on Save and continue and here here we are doing an overview the location is friends in my case the operating system is Ubuntu with LEL this is my host name which I cannot edit and I can edit the SSH key if I want and now I'm going to click on finish setup and this is going to take several minutes it might take um 10 minutes as it is written right here so I'm going to pause the recording uh and I'm going to just simply unpause it when this is done while this is being being installed at some point the percentage progress right here stopped and it might stop in your case as well so in this case I just reloaded the page and the percentage was set into zero and you just need to ignore that part okay so in the background the process is running just it might stuck and when you reload the page it might set to zero and that does not mean that it stopped the process internally now the process is there and you will receive an email once it is completed okay and I just received this email and this indicates the that uh the setup has been completed successfully here I have this login Button as well and the VPS host name is this I have the IP and I have the panel access URL as well with the username and the password is something only I know so I'm going to grab this the which is the VPS host name I'm going to copy this I'm going to open the browser and by the way if I reload this uh what's going to be the state it is still setting up I don't know I'm not going to wait this I'm going to bring up new tab yeah that was uh that redirected me back into home and we can always go to VPS and we can see that this is this is running okay this is the second one which is running right now and let's now open new tab and I'm going to paste that domain hit the enter and we're going to see Lal 11 up and running on our custom domain on VPS we see Lal the it's going to grab always the latest version of Lal PHP is 8.2 because Lal 11 actually works on 8.2 and above but the PHP version is something we can easily change okay I'm going to again open you can always open email or maybe we can just close this email and I'm going to open this VPS so we're going to click this uh manage button right here and we have a couple of information we have the VPS overall information right here the location and everything we have the SSH access information how we can access through SSH we have the panel access information this is what I'm going to access right now and this is the application access and we have the plane details which we can always upgrade if we decide that the memory bandwidth or whatever is not enough for us so now let's access panel so I'm going to open this in a new tab uh this because we are accessing this through IP address we just need to proceed with the advance right here proceed to IP address which is unsafe but we have to do this to access the panel so I'm going to grab the username and I'm going to provide the password which I assigned during installation let's hit the enter and I was able to access the system let me switch to dark mode and let's have a look quick overview okay in the dashboard we're going to see the overall information of our operating system host name IP and the size of the server and couple of uh charts right here okay so you can always monitor this from the dashboard however when we go in the site section we're going to see only one site at the moment but you can always add more sites right here I'm not going to cover in details how you can add multiple sites on the same VPS server but that's totally possible and when you click this add site button you can choose which type of website you want whether it's going to be WordPress PHP node.js static h HTML or even python site and when you choose whatever you want for example create PHP site then you're going to provide the domain name you're going to choose the PHP version and you have bunch of options right here you have older versions as well uh and you can choose the site user password and site user access and then you can create this and then that new PHP site can be managed in the same way as existing PHP site so I'm going to go back and I'm going to go sites and I'm going to click on this domain to see what possibilities we have inside there so let's click on this and when we go inside there we have the following options so in the domain settings we have the domain name and we see the root directory of this application on the server and this is going to be the full directory it's going to be under user called user okay inside HT docs and this is going to be the actual application folder name and obviously public is the folder which is going to be served because index PHP is inside the public okay so we don't have that much information right here we have the site user SSH keys and password down below we have PHP settings which we can always change okay so now let's scroll down below we have some page speed related um options which is actually for um like optimization actually what what whatever it says it's for page speed and we have obious to delete the side the following section let's scroll up and we can click uh actually what I'm going to do is to change the PHP version to the latest version I'm going to stick with PHP 8.3 I'm going to set the memory limit as it is Max execution time I'm going to set this into 1 minute Max input time I'm going to leave everything only I'm going to increase this post Max size to 1 gab and I'm going to also change upload Max file size to 1 gab I'm going to ELO in my application to upload files which is going to be up to 1 Gigabyte maybe we want to share some video those to our colleagues or inside group that's why I'm going to allo uploading 1 GB files right here okay so PHP version postma size and upload Max file size is the only thing what I am going to change right here and then I'm going to click on Save good now let's go in the vhost section here is the entire virtual host Eng genix virtual host file which we will need to modify a little bit later um to actually allow the lateral Reverb pass through right here we're going to do this a little bit later okay in the database sections we need to create the database but before I create the uh database let's go through all the tabs and quickly cover them and then we can create the database and finalize the project setup so we have this varnish cache possibility right here as well if you want to activate this we have SSL in TLS separate section and we have self signed and let encrypt certificates activated right here we can always add new let en Crypt certificate for our domain if we want in the security tab we have IP blocking um if we want to block access to our application from certain IPS that's going to be also allowed we can also activate basic authentication which going to be a second layer of authentication on top of our project and we can also wi list the IP address which can tax us through these we can also allow traffic uh from cloud flare only and we can pass through through uh through Cloud flare which is going to be an additional safety because Cloud flare uh can prevent us from dos attack and from um like accessing from untrusted sources and it's going to block um untrusted requests as well that's going to be pretty useful in certain cases the cloud player part uh I'm going to deactivate this basic authentication I don't need this I'm not going to also block any IPS right here so from SSH and FTP section we can add more users to access through SSH or through FTP if we want so but I'm going to leave this part uh in the file manager you're going to see entire di entire home folder of the currently authenticated um of the website user basically so if we go in the settings we're going to see that the uh this is the application uh application path where the application is installed but the actual user folder is The Following part so if we go in the file manager we are going to see every thing under home user we're going to see HT docks right here as well if we go inside we're going to see this our Lal project and this is typical Lal project so we see all Lal uh folders like app bootstrap config scroll down we can see I wender and everything what basically um inside LEL project we can see also Crown jobs and we can add new Cron job if we want so and that's going to be also through the interface pretty um neat and we can see always logs section as well we have also this admin area from which we can see all the events all the uh like um instance related things what what services are they running like the MySQL engine we see PHP fpm for different versions we see reddish and varnish as well you can manage backups we can um like consider more Security in terms of what rules do we allow in the firewall and we can also activate basic authentication from here and we have the settings tab as well we can assign custom domain to the following C panel uh because right now we are only able to access this through IP address if we decide we can assign custom domain to this as well and we have the support section which redirects us to a different tab we need to go back to sites and uh we need to create database let's click on the database and let's click on add new database and we have to provide the database name and I'm going to call this Lara chat and I'm going to choose the same name for the database username and this is the random password we can always generate new one but I'm going to grab this this is good password um it's I mean it's a unique uh password pretty unique one and I'm going to grab this and I'm going to click on a database make sure you copy this password and save it somewhere so I'm going to click on database add database which has been added okay so we have possibility to uh access PHP my admin as well of the following database PHP my admin is also installed and it's also there let's click on this it's going to open PHP my admin and we can observe this to the data when we execute mygrations right now the database is empty there are no tables okay I want to save my password somewhere so I'm going to uh open uh open new file and I'm going to put the password this is my data database password right here okay so we have the database now we want to clone the project and we want to set it up properly for this first we need to access this server using SSH let's go into H panel and we can go into SSH access and we need to uh copy the following command to access through SSH so I'm going to copy this and we're going to open uh gitbash or any terminal which supports SSH and I'm going to paste this and I'm going to hit the enter so before I hit the ENT enter if you don't have uh provided your SSH public key to H panel it will ask you SSH password and you have to provide that SSH password however if you provided your public key you will be authenticated and logged in immediately inside the system but on the first trial it's going to ask us are we sure that we want to accept this fingerprint and continue and I'm going to typee es yes right here now because I added public key to the server I was immediately able to login but again if you have not add your public Q it might ask you to provide your password now we are accessing this website as a root user but we don't want to perform any actions using root user at the moment we want to generate public private keys for our user and our user is actually called user so we can find this again in the settings so under home user we have the following project installed so if I want to access um the user I'm going to use Sue command then Dash and then the username in our case the username is user I'm going to hit the enter and now this part changes and I'm accessing the website as um user if I type ls- La I'm going to see all the folders in my and files in my user and here I see this HT docs okay and inside HT docs we have the project installed but before I go into the project folder I'm going to generate public private keys so here I'm going to execute the following comment SSH D Key gen- T and I'm going to provide the algorithm RSA we're going to provide the length right here 4,096 and we're going to provide the comment which is going to be the following email so I'm going to hit the end enter so it's going to ask us uh which in which file we want to save our uh key and I'm going to leave this as a default and it's going to generate uh the private key right here I'm going to hit the enter then it's going to ask us if we want to add additional passphrase I'm going to leave this empty uh just leave this empty you don't need to provide this uh because that's additional security and we don't want that additional security because uh during automatic deployment it might might just uh it might not work basically so let's hit the enter now the key has been generated and we are going to grab this key using cut command okay and SSH slid RSA dbpb so we want to grab the following file and this is now public key this is not secure um this is not actually sensitive information this is public so we can grab this and now I'm going to open GitHub where my project is installed so because my project on GitHub is a private project it is not public project I need to allow this particular server to access to my GitHub and clone this project if your project is public project then you can clone this without generating these public private Keys okay so just make sure uh before when you click CL you use https version of the URL not SSH version of the URL if your repository is public and if you don't want to generate public private keys right from here and to clone this project like this use htps version of the URL now because I generated uh public private keys right here and I want to clone this uh as a private repository I'm going to go into settings then I'm going to scroll down below find this deploy keys and I'm going to add new deploy key I'm going to paste my key right here and the title is going to be Lara chit let's give it like this lar at VPS maybe okay scroll down below and I'm going to add this key okay so here um the GitHub basically asks me this two factoral authentication so I have to approve this it's going to take two minutes I'm going to enter the number in my mobile application and yeah the key has been added now let's go into the repository and I'm going to click on this code button and I'm going to go to the SSH Tab and I'm going to copy this SSH URL now let's open Terminal again and I am authenticated as a user so I'm going to go inside HT docs folder and here is the folder inside which the application is installed this is the application okay and this is Lal application but what I want is to create new LEL application but I'm going to put this under the same name because that Lal application is the basic fresh new Lal application but what I want is my specific application which is a messenger type of application so what I want to do is just to move that project into a different directory so let's just move this under the same folder but up P.B as a backup type of thing so I'm going to move this and now we don't have that name we have back name right here and I'm going to execute git clone paste oops I want to grab that URL right here from the GitHub paste this and then I'm going to hit the space and I'm going to provide the original domain name inside which the folder needs to be created inside the which the project needs to be created so git clone then the repository name and then we have this uh our host name VPS host name that's going to be uh you can find this under application access right here so the actual domain without https um and SL slash this is the actual login name okay so I'm going to hit the enter and now uh we have to accept this fingerprint so type yes and now as you see it successfully cloned the application in the folder and let's clear this up and have a look now we're going to have two folders this is the old one which is a brand new we can even delete this um completely I think I don't need this so maybe I can just remove this rm-rf SRV doback so I'm going to delete this right now when I try to access the application which was previously L 11 application on the following folder running now it's going to crash because we have just clowned new project and we have not set this up so we're going to go inside the folder okay and we're going to set this uh entire project so first I am going to copy example inside okay cool now let's open. and we're going to adjust couple of things okay so I'm opening this using Vim so vm. once you open this you're going to go into insert mode and type I on your keyboard then down below you're going to see insert okay then you can using arrows you can navigate right here so the application name is going to be Lara L CH this is how I call this the en is going to be actually production and we are going to uh come back to this and we're going to change this but until we are setting this up and we might see some errors and we want to see those errors so I'm going to leave this local in the same way I'm going to leave up debug true so we're going to change the application URL and I'm going to change this into https and then I'm going to provide that uh is going to the application name at the moment unless we provide the domain so this is going to be it well we have this twice so we're going to delete this yeah we can leave everything else default in the DB connection we're going to change into MySQL then we're going to uncomment the DB host DB Port database username and password and we're going to provide the password for the MySQL user which we created here I have the password I'm going to copy this and I'm going to paste this right here the username what we created was Lara inside my SQL and the database was Lara chat as well so everything else I think we can leave but one thing what I'm going to change is to a broadcast connection okay so for bro broadcasting we're going to use Reverb so I'm going to change this into Reverb let's scroll down below uh we're going to leave now everything including this mailer I'm not going to set up actual production um sending email um so I'm going to leave this part we can leave it everything and yeah we are almost done down below we're going to add Reverb related en keys for this let's open our project we can close this because we have already um pasted our username so um actually pasted our password so we are going to open file and at the bottom of this en file we're going to find these Reverb related Keys okay we're going to copy all of them and we're going to paste this right here and this is going to be something what we are going to change a little bit okay so this is the application IDE of RB which we can change maybe we don't want it to be the same as our local environment we can change this we can also change this application key and application secret as well so I'm going to change this host into the application host so I'm going to get this and scroll down below and inside Reverb host I'm going to paste this without this slash I'm going to also change Reverb port and that Port is going to be where the um where where from the browser we are going to connect to our Reverb actually Port 8 is already occupied on on This Server because one of the services are is occupying this so we're going to try to connect to on Port 8081 in here I'm going to add also additional key which is going to be Reverb uncore server uncore port and this is going to be 8082 and we're going to change the schema into htps as well and this is going to be it but before I close this let me try to re explain things as well so from browser we're going to connect to the following host on the following Port okay but internally on the server Reverb will run on the following port on Port 882 but in the engine configuration we're going to have this path pass through uh to to basically uh rot all the traffic which is coming on Port 8821 to Port 882 and just like this we're going to have um the Reverb server active on Port 882 but we will be able to connect on Port 881 okay we're going to save this using escape on your keyboard then colon W and Q to save and quit and we have provided file properly now let's clear this up and I'm going to execute composer install let's hit the enter it's going to start downloading all the packages and it's going to quickly finalize this part okay that was already done then we're going to uh we can already execute migrations so we're going to execute uh actually we need to generate um also um sorry key um PHP Artisan uh key column generate d-i hit the enter application key set successfully that needs to be set in the en file let's execute now PHP Artisan migrate D- seed to create all the tables with the seed data I have extra s right here which basically appears this whenever whenever I um unpa the recording so I'm going to hit the enter and the tables have been created and it was seed as well now if we open the browser and if we check PHP my admin we're going to see tables right there and we're going to see also users and we're going to see also messages now let's open Terminal again and we're going to execute mpm install in this case but mpm is not actually installed and nodejs is not installed on the server so we're going to install node.js to install node.js the best way I found was from the following repository you can go to node Source SL distributions and you're going to find installable Commons for different operating systems where I'm interested is the ubun version verions okay we can scroll down below and we can find a node.js version 20 for Ubuntu and this is the command we're going to execute so I'm going to copy the following command and I'm going to open my terminal and I'm going to paste this right here and hit the enter um okay so because right now we are authenticated as user regular user we have to exit this and I'm going to type exit right here now we are authenticated is root user as you see right here and then I'm going to paste this command sorry we're going to copy this command and I'm going to paste this and hit the enter now it's going to start installing no JS with root user and this is exactly what we want here it's going to ask us which Services we should be restarted and we can use up and down arrows right here to navigate between Services we don't want any services to be restarted to be honest so we can use also space to just deactivate Services which should be restarted so okay I'm going to deactivate everything then I'm going to hit on Tab it's going to go on okay and we're going to hit on enter and when we do this uh the node.js command actually was installed and we can type node DV we can type npm D npm DV as well now we need to again authenticate using user so sue Dash user user we are now authenticated using user we're going to navigate into HG dos SRV and our domain name and from here we're going to execute mpm install and then we're going to execute mpm run Dev mpm run Dev uh excuse me not mpm run Dave but we're going to execute mpm run build to build our asset files JavaScript and CSS files for production okay that was also done and now I'm going to generate storage link by executing PHP Artisan storage column link let's hit the enter okay the storage link has been created as well now let's open the browser and I'm going to reload this page and we are see our application up and running and we have those two users John at example.com and let's try to log in with that and we are able to uh log in successfully let's save this and we can open second browser as well and we can authenticate using second user Jane example.com with password Okay cool so both users have been connected but pay attention that we don't have Lal Reverb server up and running and we don't see right here that Jane is online uh or we don't see right here that join is online this is something we're going to take here right now and we're going to also start the LEL Reverb server on the server side so let's again open the terminal and we are going to start the Lal Reverb server by executing PHP Artisan Reverb start let's hit the enter if this has been started but now let's test from the browser how are we connecting to LEL Reverb let's reload the page let's check the network right now here let's go into all and right here we see the request is pending and we are trying to connect to our domain name on Port 8081 and this is something that is not opened on our server and we are going to make this open to determine which ports are opened on the server or not we are going to execute some command using PSE sudo so I'm going to close this Reverb I'm going to type exit so that I'm root user and then I'm going to execute ufw status verbos so this will give us all the list and ports that is opened uh from outside to be accessible we see that 22 Port which is used for SSH is um allowed 80 Port is obviously for engine X allowed 443 is also engine allowed then we see this is for cloud panel which is is also allowed and that's basically all so these are all ports four ports basically which is allowed and now we're going to Alo Port 8081 we're going to execute Pudo ufw L 8081 this Sudo is something we can even remove because we are a root user I'm going to hit enter rule has been added and now if we execute uh ufw verbo status we're going to see Port 881 is actually inside a load list now what we're going to do is to update Enix virtual host configuration file so that we're going to pass through from Port Port 8081 into 8082 let's open the browser go into Cloud panel we're going to go into virtual host and we're going to make some changes right here let's scroll down below and we're going to add um something additional location and server right here here okay so we're going to add server something similar let me copy this part I'm going to copy this part scroll down below and uh we are going to add this inside uh that server basically we're going to do this right at the very bottom right here okay so we're going to have server we're going to listen to Port 8081 but we're going to listen on SSL because we are we want to connect through SSL we are connecting using uh WSS protocol not WS we want SSL as well so this is important where we are we are here so we are going to also connect at uh at1 right here through SSL so we're going to remove these two lines we need SSL certificate key and SSL certificate and we need this server name right here as well and I'm going to remove this route obviously we need server closing tag as well so we are listening to these two ports under SSL uh that should be SSL okay and we're going to add a location right here and that location can be found in the lateral Reverb documentation if we go to the web server side you can find this and we're going to copy this location go to our virtual host and I'm going to paste this right here however what I'm going to change is this proxy pass and we are accepting requests on Port 881 but we have leral Reverb server running on Port 882 so I'm going to change this into Port 8082 I'm going to click on Save and the vhost has been saved and now let's go into our terminal and I'm going to again authenticate using um user let's navigate into HD docs uh into our project and we're going to execute PHP Artisan Reverb oops Reverb start let's provide DH Das debug as well let's in hit the enter now we see that this is running on Port 8082 and let's check in the browser if we are successfully able to connect to our application okay we are able to successfully connect to our application if we go into WS we're going to see the connection has been made and in the messages we're going to also see receiving some data right so we can open now second tab reload this tab and we're going to see now joho is actually online so let's collapse this side select jono and send message high and we receive that message high right here which proves that now we have Reverb server up and running and we are able to successfully communicate to each other hi there as well let's hit the enter and we receive this hi there however this is not enough now we have manually started this Reverb server but we want this to be a background process always running always there additionally we need also to start q and that Q needs to listen uh to certain things and in our case whenever we delete the group we are dispatching a job to this q and that Q in the background should take this job and delete the group in the background and it's going to then send this event to a it to other users that the group has been deleted and it should remove as well if we go in the cues section in Lal documentation we're going to find the dedicated section right here which is called supervisor configuration supervisor is a program that supervises certain commands running on our application and make sure that if the command was exit for some reason for error it's going to restart the command automatically and this is exactly what we need in our case so we're going to first install the supervisor by executing the following command so let's open our terminal I'm going to stop this lateral Reverb because we don't need this on production started like this I'm going to exit from here because I need to be root user and I'm going to execute sud sudo upate install supervisor and that's going to be installed once this is installed we're going to create a new file under Etc supervisor conf D so I'm going to navigate this asks me again whether which Services I want to be restarted so actually I don't want any services to be restarted so I'm going to deactivate all of them hit on okay okay that's good now let's navigate into the following folder okay and I'm going to create a two configuration files for supervisor one is going to be for lateral Reverb so I'm going to call this um LEL Dash Reverb DH uh sorry doton file let's hit the enter and I'm going to hit on I to go to the insert mode and then I'm going to copy the following command and I'm going to try to explain this okay so we're going to provide the program name and I'm going to call this LEL Reverb actually this is uh for Lal Q's this particular one and we're going to also do something for Q's but let me first do for Lal Reverb so we're going to provide the actual program name then we are going to provide the command which should be executed so the command is going to be PHP Artisan in our case it's going to be Reverb start but let's provide the correct path of our application let's go into Cloud panel let's go into settings and this is where our application is installed excluding public so I'm going to copy this and I'm going to paste this right here and I'm going to delete the right side and only leave Artisan so Artisan is located under home user HD docs our domain name artisan and then we have to provide right here the command which in our case is going to be Lal um sorry Reverb colum start then we're going to provide auto start which means that this should be started as soon as supervisor starts AO restart means that whenever this uh command is going to be failed it's going to restart automatically and these options like stop as group and kill as group should be also provided true we're going to provide user to be user because the user is the name for our application run processes so this indicates how many simultaneous process we want to be run and I'm going to set this into one okay and uh we want to also change this STD out log file and I'm going to provide this to be the project name uh this is going to be the project name home user HG dogs then this is our um this is our project name and inside there we're going to have reverb. Log 5 okay and we're going to set this stop wait seconds as well so now I'm going to hit escape on my keyboard then colum right and Q and then according to the documentation right here we are going to do the following so we're going to execute pseudo supervisor CTL reread to read the file and Lal Reverb is now available then we're going to provide update okay that's great edit process group and then then we're going to start the process by executing the following command however our name is called LEL Reverb because this is what we called here we see that right so we're going to execute the following commment pseudo system CTL start Lal Reverb hit the enter and uh that should be started and we don't have this PHP Artisan Reverb start manually executed and if we did everything correctly we should be able the connection to Reverb to be made successfully and we see that this has been made successfully and we can again open both tabs and we can send message from one and because that was received right here supervisor right now is supervising this Reverb start command and make sure that it is always started and working now we're going to do some something similar for cues so first let's create Now new file Lal let's call this Q let's hit the enter let's hit I to go to the insert mode Let's Open the documentation we are going to grab this command and I'm going to paste this right here let's change now LEL worker into laravel Q let's provide the proper location for our project which is going to be this I'm going to paste it right here home user HG dos the domain name Artisan Q work and we are going to provide right here I'm going to remove the sqs because I'm going to listen to the default queue uh but I'm going to also adjust these options so let me delete these options right here and I'm going to quickly copy and paste these options in um from my so basically here I want to provide um dash dash tries to be three uh and the second um comment the second parameter is going to be dash dash timeout d d timeout to be 120 seconds okay so there are a couple of commments available on the uh Q work you can find all of them uh if you open your vs code for example uh in your project and if you execute PHP PHP Artisan Q um work and then D- help it's going to provide all the comments like tries timeout rest and everything which is available and in this case I'm just going to provide those two options tce equals three and time out is 120 seconds let's copy this uh the uh older location name and here I am going to provide that as well and uh let's just call this q log now I hit escape on the keyboard call column write and quit let's hit the enter that was created and we're going to execute first um uh supervisor CTL reread now okay and we see this error invalid username that's right we have to adjust the username which will left for Forge but the forge user doesn't exist so we're going to change this into user and let's change this number of processes from 18 to one Pi so Escape colum right Q then let's execute reread then we're going to execute uh update and then we are going to start this using the following commment uh supervisor CTL start Lal Q now if we hit the enter okay that's going to start it and just to test this if this is working correctly we are going to open the browser uh this is the uh let's open the second browser I have a lot of tabs open right now okay here it is and I'm going to try to delete the group and I know for sure that the group delete dispatches a job which should be executed in the background okay and if that works successfully I should be able to see the socket message received regarding the group is deleted um okay this is this is going to be the decision so let's let's click on one of the groups and I'm going to click click on um delete that I received an immediate message but in the messages right here I should also receive something and I should success should see success notification that group was deleted and here I see that and I received that message as well event group deleted so this proves that Lal Reverb and Q work comments those are running uh in managed by supervisor and this is how it should be on production and just like this we have the entire application hosted on VPS and it is up and running in under one hour probably with Lal Reverb Lal um Q running in the background everything is production ready and we just need to assign proper domain to this to assign custom domain to our project let's first grab the domain I'm going to go into domain section I'm going to click on this get new domain if you have existing domain and you want to assign an existing domain to your VPS server that's going to be also possible and totally fine I'm going to get a new domain and I'm going to call this lat. chat let's search for this and this domain is actually available lat. Chet so I'm going to buy this domain actually right now so I'm going to buy this for just one year we have also this free domain privacy protection included so I'm going to scroll down below and I'm going to complete this payment if you are purchasing the domain very first time time on hostinger you might need to provide some contact details like your email uh phone number first name address information I'm doing this I have done this multiple times so here I have the contact information which is going to be blurred and I'm going to finish this domain registration so my domain will be registered and available to redirect to our VPS okay once you see the following message you can click on continue and this is the domain overview domain is already owned by us okay and now I need to point this domain to our VPS let's go into VPS and let's open our let's manage click on manage to our VPS server right here and then I'm going to get the IP address okay and I'm going to add this domain right here but I need IP address as well so let me provide right here Lara chat. chat I'm going to click on ADD right here okay so this is detected and this has been um owned by us this is owned by us so we need to go to D domains dashboard and change its DNS addresses as well so here we have this lat. chat I'm going to click on manage right here then we need to click on these DNS and name servers let's scroll down below and there's nothing no DNS records which is fine we are going to add one a record and the name is going to be at which means the root domain and the this points to the IP address of our VPS and I'm going to paste the IP address of our VPS right here I'm going to click add record okay I think uh right now it needs couple of time because the domain has been recently set up it might need some time to be properly um adjusted soal it so let's try this once again I'm going to click on it record okay I'm going to wait for a few minutes and retry this adding a record right here was not even working after 5 minutes so I tried to reset all the DNS records with this button reset DNS records right here and previously there were no DNS records as soon as I click on reset DNS records it appeared the default DNS records which comes with the U hostinger new domain so now I'm going to try to add this a type of DS record right here the name is add this points to the following VPS and I'm going to click this add uh record and now this pops up uh the following screen existing a record is something what I want to change there's actually existing a record which points to the following IP address that's interesting and now if we try to open the following domain uh in the browser we should see something that is the default hostinger page okay this this is something what we're going to change I'm going to scroll down below and I can leave this C name with www which points to this or maybe I should delete this as well so basically delete all the cname records and delete all a records from here then I'm going to scroll up and I'm going to add this new a record let's reload the page the button got disabled so name is ADD points to the following IP click a record DNS record created successfully and now I'm going to create second record which is going to be C name the name is going to be www and the target is going to be Lara chat. chat once we do this adding this record right here then we need to uh update the virtual host configuration file so I'm going to open um This Cloud panel basically and I'm going to go into vhost and we're going to find where we have this server name mentioned so let's search for the server name on the page we have this right here and I'm going to put a space lat. Chet is going to be our second domain and let's search other server name mentions I'm going to paste these L chat. chat right here let's scroll down below this is one this is two this is three so we have have these three times now let's scroll down below and I'm going to click on Save now we have this domain main uh also updated in the virtual host now if I open incognito browser and type lat. chat now we still have this DNS probe finished um an issue because this is still in setting up process after 2 three minutes we see the following screen and that is great so the DNS completed successfully it took the a record it took the C name record but right now we have this SSL Error because we don't have the SSL installed on the domain if we click on Advanced and proceed with lat. chat we're going to see the welcome page this login page of our application which is actually great I'm going to close this then we're going to open this Cloud panel again go into SSL and TLS Tab and we are going to add new let's encrypt certificate we're going to add a domain right here provide uh Lara chat. chat uh and we are going to click on Create and install just make sure the domain is correct lat. chat let's click on Create and install it's going to take two three minutes also once you see the following message certificate has been installed you can open incognito tab and you can type lat. chat and you're going to see that the connection is secure we have SSL certificate installed successfully the last thing what we need to do is actually to change parameters in our application you need to be authenticated as user you you're going to execute su- user and then let's navigate into HG docs we're going to navigate into the project and then we're going to open and I'm going to hit I on my keyboard and I'm going to change the host right here and this is going to be lat. chat and we're going to go up and we're going to change this right here as well inside the app URL I'm going to set this into the same lat. Chet now Escape colum WQ and we're going to execute mpm run build to build the Javascript file so that it can take the latest values from the file once we do this we can open this application uh let's open now in not in Incognito in a regular browser right here let's open in another browser lat. chat this is from we Jane do is going to be installed so in this browser it still has this cached and we see that DNS prob finished and ex domain but that's not a big deal let's open this right here as well L chat. chat we see right here let's log in using John example.com John has been authenticated successfully we can save this now let's reload this or I'm going to close this and reopen this lat chat. chat and now Jane is going to authenticate right here let's hit the enter and now we see online right here and if we open now the developer tools and go in network and reload this page and go into WS we're going to see that the connection has been made with this domain lat. chat on the following port and let's try sending messages hi hit the enter and we see this High message right here receiv it from there hit the enter and we see so now we have also assigned custom domain to our application which is exactly what we wanted the last thing what I'm going to do here is to open file and I'm going to go up and I'm going to change this environment let's hit I I'm going to change this environment into production and I'm going to also disable deug I'm going to say up debug to be false make sure you do this whenever you uh run your application on production otherwise if there is any error all the sensitive information might be exposed during this error so I'm going to click on Escape W and Q to save that now let's start working on continuous integration code deployment and we're going to do this using GitHub actions whenever new release is created GitHub actions is going to grab that release code and it's going to execute all the necessary steps we're going to also put inside the workflow to execute tests so if you have tests in your Lal application which you want to deploy on VPS it's going to execute tests as well and if everything is completed successfully in this case it's going to deploy this project on the hosting server okay so we're going to start this with uh by creating a new file under dot GitHub folder inside the we're going to create folder called workflows and inside there we are going to call we're going to create yml file and we're going to call this whatever we want I'm going to call this hostinger Das deploy dot deploy do yml file let's hit the enter and it created the following file under GitHub workflows now we're going to focus on this yml file first let's define the name of our uh workflow then we're going to listen to when new release is created if you want to also listen when something is pushed on Main Branch or a branch which starts with releases slash then you can add the following code inside own then we're going to Define jobs we're going to have the main job which is going to be called build this is going to run on the latest buun and then we're going to Define steps so the first step is going to be to check out um check out the code on the corresponding version okay then we're going to set up the PHP environment with the PHP 8.3 version then we're going to set up the nodejs environment with the node version 20 after this we're going to copy en example into en in our runner to set up the entire Lal application um for uh for the whole process basically so then we're going to install composer dependencies then then we're going to set the application encryption key then we're going to create the storage link as well and we need this if for example some tests might be using this storage upload functionality okay in our case it doesn't use but to make our deployment process more unique more Universal and more um like appropriate for other types of projects I'm going to put this so then we're going to run the migrations and then we're going to install the nodejs dependencies in our project and we're going to also build assets after this we're going to run tests and if everything has been completed successfully then we're going to deploy the uh project to server and here we are checking if everything has been successfully we're going to use the following package SSH action to connect to the server in our case it's going to be hosting or server for deployment to connect to hosting or server we need SSH credentials like we need host we need username we need port and we need key we're going to provide those secrets in the project so let's open GitHub um I'm going to go into our project and let's go into settings then scroll down below and find the secrets and variables and then we can click on actions so right here I have already created these four variables okay and we're going to uh use those variables right here in the deployment script let me tell you what are these variables the host is the actual host to which we're going to connect so this host might be an IP address of our server it might be this or because we have already assigned a domain to it it's going to be lat. chat we can also specify right here inside here uh the domain lat. chat or the IP so inside key uh okay let me explain the key as the last thing so inside the port we're going to have SSH port and this is going to be uh 22 by default we can grab this port right here then we're going to provide uh we are going to provide the username and the username is going to be in our case user so we want to connect to the server using not using root user but using a regular user and in this case the username is going to be user so basically right now if I open git bash I am actually able to connect let's bring this up I'm actually able to connect to the server using user not root I'm also able to uh log in using root but I want to demonstrate SSH user at Lara chat. chat so this is the host this is the user the port is default 22 so I'm going to hit the enter I'm going to type yes right here and I was able to connect and how this happens why I was able to connect because inside this user right here we have added to we have added public key of my computer okay so I'm going to open from the home directory I'm going to open SSH authorized keys inside authorized Keys we have two public keys so this is my computer's public key and this is the public key which is going to be connected to the private key of the GitHub Runner okay so this is the public key using which Runner will be able to authenticate as a regular user not as a root user and will perform some actions on the server so we are is this public key generated this is the same public key actually what we have already generated so if we navigate into SSH folder here we have public private keys this is the private key this is the public public key okay I took this public key and put inside authorized keys and I took this private key and put inside SSH key okay and just like this the runner knows what is the private key and its corresponding public key is already added inside users authorized keys and the runner will be able to connect to our server with the following um variables okay and the key is going to be the private key in this case okay I hope this makes sense if you have any questions please let me know in the comment section down below now let's continue so we're going to provide right here scripts what scripts needs to be executed on the server so we are going to navigate to the following folder where the project is installed by the way we can also change the following path you can easily do this if we open this Cloud panel we have right here domain settings so we have this root directory we can just change this into for example lat. chat or whatever you want you can call this but make sure in your project if you navigate into HT docs make sure you also rename the following folder into whatever you type right here okay but that's not going to be fully enough we have to also update supervisor configuration files we created previously because right there inside their config we are using the following name we might be also using this name in couple of other places so I I am not going to change this name actually I'm going to leave this so it's a that's a internal path where the project is installed that doesn't hurt anything so I'm going to leave this actually now to go back to vs code we're going to continue and once we navigate into the project folder then we're going to check out to the master branch and here's what I'm going to do so I'm going to check out to actually the final result is going going to be I'm going to check out to a new release so the the whole Job the full job is going to be only executed when the new release is created and let's say we created a new release version 1 0.0 so here I'm doing the following I'm going to check out a master branch and I'm going to check if there already exists a version whatever we just released let's say 1.0.0 for example if you release 1.0.0 uh version and the following workflow is going to be executed and inside G repository inside on the server the code will already already be checked out to version 1.0.0 but if you decided that that version was released mistakenly you want to delete this and then re rease the same version 1.0.0 Pro I want that still to be triggered properly so here's what I'm doing I'm checking if there already exists a version called 1.0.0 or whatever then I'm deleting this if that already exists okay after this I'm calling Fetch and then I'm checking out to new 1.0.0 version okay if there is no such tag already we check out to master then we fetch all then Weck out to new release let's say the current release is 1.0.0 I created 1.0.1 then it's going to check out to master this will not be satisfied then it's going to fetch all and then it's going to check out to the version okay cool then we're going to execute composer install then we're going to execute mpm install and mpm run build then we're going to execute migrations then I'm going to clear the configuration rules and Views all the cach cach content then I'm going to restart q and I'm going to restart Reverb basically supervisor will be uh will take care of these these restart commments will stop them and the supervisor will handle them properly now we have this workflow and we inside this workflow we are also executing tests and I'm going to check right now in my computer how tests are performing but whenever I execute PHP Artisan test in my project with the following code it's it's going to use the same database I'm using and it's going to clear the entire database for me so what I'm going to do is just go into project root directory and open PHP unit XML and here we have these two files commented two lines commented DB connection and DB database I'm going to uncomment both of them this and this and then I'm going to exe execute PHP Artisan test so this going to go through all the tests and execute them one after another but one of them is actually failing and this is example test PHP let's open this and as you see it expects the status code to be 200 uh whenever we are accessing the slash which is the home of our application but we have changed this and whenever we try to access slash we are redirected to 302 basically sorry we are redirected to login page with this state code 302 and this is exactly what is happening we're going to change this test and we expect that when we try to access uh the homepage we will be redirected to somewhere else with the status code 302 now I'm going to save this and when we are uh when we will execute this PHP Artisan test it's going to actually pass all the tests and this is great now tests are passing we have this um workflow created and we need to push everything on GitHub and create the our first release and see how the workflow will be triggered now let's go into the project and then right here I'm going to click create new release and let's call this 1.0.0 create new tag and let's call this initial release now as soon as I click this publish release button the workflow should be triggered let's go into actions and we see this initial release let's click on this and let's have a look we're going to see the entire output inside build how this is performing I'm going to pause right now so the job has been completed we see green checkbox right here every step has been completed successfully if there are any errors you can obviously see them our application is up and running now let's check the code and I'm going to type uh let's navigate into the project directory and I'm going to type get status once I do this we can see that we are on the tag version 1.0.0 okay and just like this uh everything has been deployed successfully and just to make sure that um the deployment works perfectly you can make some changes in your code like for example you can go and open tnd config JS you can change this theme into for example Forest you can comment push this and create new release right here version um 1.0.1 for example and it's going to trigger this and right here you will see that the theme is going to be forest and this is how Forest theme looks like with green messages from other users all right my friends if you're here and still listening to me you are lucky because you have just completed one of the most exciting projects for me personally and if you finish this and you can put this in your portfolio you should be very proud of it if you have not yet liked the video please hit the like button and subscribe to the channel you can also provide a positive comment because that also helped the channel to grow thanks a lot and I will see you in my next video