NestJS Angular WebSocket Private Concurrent Chat Conversations SocketIO - LinkedIn Clone [28]

Video Statistics and Information

Video
Captions Word Cloud
Reddit Comments
Captions
welcome to the highlight of the linkedin clone in this video we'll be implementing all of the logic for our messaging system so as you can see on the left hand side we're logged in as user one john one and on the right hand side we're logged in as user two and if i go ahead and i just send a message from user one to user two we can see that it appears and you get the correct image and the correct date and message of course and if i were to write back you'll see that john 2 has written back to john 1 and also there's no weird behavior such as if you're in the conversation with john 3 and then john 2 sends a message to john 3 we can see that the room is self-contained and of course if i leave this area and i come back to the messaging it retrieves all the messaging so it's important that you've watched some of the previous videos this can be a little bit tricky there's a lot of logic to implement here and you really need to be comfortable with rxjs and also the previous videos where we've set up the web sockets so you haven't seen that already go back and watch those and let's just go ahead and get started so the first thing we'll see is that the ui doesn't look too bad but we're unable to click on different rooms and right now our messaging's just set up so that there's no difference between the rooms and also the data isn't persisted so we're going to go ahead and add those in but before i do that i want to be able to select a room and we're going to have this little green rectangle here so what i'll do is i'll just open up the vs code and i've done a npm run start dev for the nest server and also ionic serve but let's just start in the linkedin in the front end project and let's navigate to the chat component so let's just open up the html the scss and also the type script so around the when we're rendering each of the friends we want to add a click event here and we're saying let friend of friends and let's also add a let i equals the index so we can get the index and what that will allow us to do is we can say open conversation and we can pass in the friend that's being rendered and also the index here so let's just go ahead and create this open conversation function and we'll just open that up here so we have this open conversation here and we need to just declare some variables here so we've got friends let's also have a friend which would just be the singular and we'll have that unassigned and let's also have the observable version of the friend um we'll make this a behavior subject of the type user and we can say new behavior subject of the type user and initially they'll just be an empty object but we also want to have and we'll come back to that we'll need to we'll need to set that up later on um well let's just go ahead and just add all of these here so then we can do that and then also what we'll have is we'll have the selected conversation index which is just a number and we'll set that equal to zero so with that we can go return to our function that we just created the open conversation function and we know from the template that's going to take the friend and also the index so let's just go ahead and specify those parameters we'll have a friend of the type user and an index of the type number now this won't be returning anything so we'll just say returns void but what we'll have is we'll have this selected conversation index which is initially zero will be set to the index that we're passing in and we can also say for later on the particular friend that we've selected is equal to friend and also the observable version of that and of course you could probably refactor this but as you'll see later on this version comes in handy as well so since we've got a behavior subject we can just actively call the next uh friend based on what we're passing into it and then go ahead and save that but what we're more interested in just for now is selecting the conversation index such that we can have some sort of ng class on our element here so let's just see how we can do that we can think that okay so we've got this class friend which we've defined before and i actually might put the ng4 and the click event first so we can have the class here and then we can also have an ng class and we can set this equal to all right so we can create a class and we'll call it selected conversation and we'll need to put single quotes around this and what we'll do is based on the selected conversation index that we've set if this is equal to the particular index then we'll apply that class so let's just go to our scss component here and if we go to the select connection which is this main left section here nested within there what we can do just at the end here but still contained within the um friend section we can have that selected connection actually what was it selected conversation and in that case what we can do is we can just have a border left of four pixels solid green so let's just go ahead and save that see where we're at so right now nothing's being applied here so let's just see why not and i believe this should just be tabbed down one so let's just uh do an alt down and let's see if this works so right now you can see the green arrow's been applied of the green rectangle if we click on the next one we can see that's moving but we are seeing a bit of a jerk the rest of the image is moving so we'll just need to offset that and how we can do that it's quite simple actually where we've got the friend here we can have a border left of 4 pixels solid transparent so we'll save that and now when we click and change the user we can see that there's no jerking there okay so that's the initial styling we can close the scss file we won't need that again now the next thing we want to do is we just want to control c and we want to use the next cli to generate a service so let's just go ahead and do that and in the chat folder and actually we do want to do that but what we firstly want to do is we want to use the ionic cli so ionic g to generate a service in the front end now right now we've got this core and which has error handling we can also go ahead and put inside here our new socket service because in its current state if we look at the app module the api are the front end version right now we're configuring the socket here for root config but what would be better and what we'll need for our um application is we will actually want to set this up in a service so that means when we enter the chat module which is nested within the home module when that service is instantiated then we'll be able to uh get our socket behavior and you'll see later on why that's needed so for that rather than doing it this standard way because we have a little bit more complexity we will need to do it in a it's not a unique way it's still a standard way as well it's just doing it when it's not configured at them at module level so we'll just go ahead and get a service here and we'll call it the chat socket service so inside core here we can just go ahead and call it chat socket let's get that now we don't need the spec file so let's just go ahead and delete that and let's open up our service and we can already start to copy a few things here we can get this config this won't be needed here we'll just put it up here for now and we can go ahead and we can remove the socket i o for root here we no longer need that and that means we can go ahead and get rid of these imports but i'm just going to go ahead and cut them because we can just use them here the socket i o config in the socket i o module although i don't think we'll need the module because we're doing it in a slightly different approach but what we will need instead of that is the socket so we got this service here and it's the chat socket service and we need to go ahead and we need to extend the socket class here and this is um configured like this is already set up through ngx this is just looking through the documentation and this is just an alternative approach to the module configuration it's the service approach without the modularity so we can go ahead and get that now we will see that this is airing here and that's because we're extending socket and that means we just need to have a call to the base class so or the subclass so we can go ahead and just call super based on our configuration here so we can actually just close the app module at this stage because it's not defined there anymore but we can actually what we'll need to do is report in our chat gateway and i'll open that up so far what we've been working with is very primitive we do have this cores here with origin localhost 8100 for our ionic application we need to do this in addition to our regular http rest api so right now what's happening is we're communicating from this client and we're trying to make a websocket connection to the server such that we can have that software communication between the server and client i.e when you send a message you want to push from the server to the connected client through a subscription a subscription uh using rxjs on the front end so [Music] you do need to specify the back end so right now we're pointing towards the url low close 3000 so that's okay but we've got a little bit more complexity in our application where we actually want to um have some jwt guarding on the uh these socket end points so we need to set that up properly so within these options here we can actually have another object called transport options and there's something in there called polling and what polling allows us to do is allows us to apply some extra headers so extra headers and what we want here is the authorization header and how we're going to do this is basically we need the json web token and we know that's in local storage so we can just go ahead and just call local storage get item and it's called capacitor storage that's the prefix because we uh set and get it with the uh the capacitor method um but i'm doing it with the you know the more general way the local storage um just because of the extra complexity in getting that in this nested object in a service that's been extended of socket so i'm just going to go ahead and get that and that's all we really need so we just need to verify that the json web token that we have in local storage is present and also so when we make the request to the server and make the websocket connection it can verify that and it can just uh make the request from there it's very hey the web socket communication is just building up on http so everything that we've done previously with http and json web token uh it's just a constant communication stream using that you know fundamental protocol so that's all we really need here in the chat socket service so what we can do since we've removed it from the chat gateway oh sorry we've removed it from the app module we're going to need to reference it in our chat component here so let's just go ahead and get that in there so one thing to note is right now we've been using ng on init and ng on destroy we actually want to use the ionic life cycle methods because these will only occur once basically when applications created and you navigate there and when it's destroyed but we could be navigating back and forth between the chat page and other pages or we could log off into a different user and all sorts of things so what i actually want to do is i just want to get rid of this influence on init and on destroy and i can just go ahead and use the ionic approach so line view did enter and iron you did leave and we can also since they're now being called we can now um use some we can use this approach here where we have this user image path subscription which is of the type subscription and i might move the subscriptions down a little bit uh in their own section here so we'll have a few of them and that means when we set our subscription here we can go ahead and we can unsubscribe to that so i'll just have a look if there's any others um so i can get rid of this refactor unsubscribe message here so yeah right here we can say this dot and we'll call it this dot conversation subscription and when we set it equal to this this will still run it's just in this case we're able to unsubscribe from it with our ionic life cycle methods let's just go ahead and copy this down so we can see we have this conversation subscription it gets the new messages actually let's name it this message subscription but we'll also have one for the conversation subscription as well so this message subscription and we'll set this one equal to this dot uh conversation subscription actually we don't have the conversation just yet i'm going to call this the friend subscription sorry just getting ahead of myself here this is actually the friend subscription friends subscription and we'll add some more things in soon so let's just get the this message subscription and also the friend subscription and we're going to change all this anyway so let's just come here and do an unsubscribe shift alt down it so then we can just change this to the friend subscription and now we shouldn't be getting any memory leaks and of course you can refactor the application in other areas to have this style if you wish um but we will be focusing on the core features of the websocket socket io communication to the server so let's just go ahead and run the ionic serve just to make sure that the new web socket is working and also that these things are working here so we could just have a console log here we'll get rid of this but just to um [Music] see in the console is working here so i have a console did leave did enter and i may as well have one in the chat socket service as well just to make sure that it's been called here um chat socket all right so we've got this here i can close this one here and press f12 we can see that we don't have any of those messages appearing um let's see we're getting some sort of error here uncaught no error no provider okay so let's fix this so i must have forgot to put this in here when i was refactoring other stuffs but the chat socket service we can go ahead and we can just grab that and we'll import this here and we'll save that actually we don't want it here sorry we want it in the chat service rather than having it here so i'm just going to get rid of these and also these life cycle methods here and then i'm just going to go to the services here we see we've got this chat service here and we're actually already injecting it here so we've got the private socket but rather than coming from socket now now we're going to be using the chat socket service so i'm just going to continue to call this socket but so we need to import this here so then these things should still work here so let's see if that now works so we'll refresh our ionic application here just taking a moment to load here if i navigate to the messaging tab we see we got socket did enter and did leave so that's how we're going to you know handle our subscriptions so that was just to demonstrate now i can just go ahead and remove the logs get rid of that and close it up and here in the chat component we can get rid of the console logs okay so what we've done so far is we've you know we started by working on the front end to select the particular conversation and we've sort of refactored things in preparation for our json web token secured websocket routes that we're going to be calling so now we actually need to implement all of the um tables and set those up so they're all sort of integrated together so we're going to have a conversation table in postgres so you can imagine that each conversation contains two people so there's actually we're going to need a many-to-many sort of relation for this sort of thing um because there's also going to be many messages in each of these conversations and there's also going to be active users so the way we want to do this is well when we first come here it should load the first person and that means we should load all the existing messages if there are any and we need to you know display them you know with the date and with the correct picture and the message and all that and then we also need to make it so that because right now we're able to send that but it's in both the room so we need to isolate the rooms and there's a bit of logic that needs to to be implemented for that to be achieved [Music] so not only do we have the conversation but we also have the active conversation so all these conversations will be created up based on the friends list here um but the active conversations they'll only be created when you clicked on one so that means we'll have to be able to leave a conversation and get the you know the id of the conversation and then also having the right users in the conversation and active conversation and making the messages uh relate to those so you can see that it's quite um integrated there's quite there's going to be lots of relations here and we need to build those tables up so we're able to store data and retrieve data from those different entities so let's go ahead and start to do that so we'll go into our api and i'm just going to kill this because we're going to generate a few things here so let's just open our api folder up let's just collapse everything and then open our source folder up now we're going to be working within the chat section here so we're going to have a folder a new folder for our models and we're also going to have a [Music] um another folder for our servers so let's just create that while we're here and this is where we use the nes g to generate the service in the chat folder now we want to put it in a new folder called services and we'll just call this the conversation for the conversation service and we don't want the spec file so we'll just say no spec we don't want any further nesting so we'll just say flat so let's just get that we'll just check that it generates correctly um so it's updated the chat module and we can see in our services we've got this server so we will keep this open [Music] because we're going to come back to this but what we want is we want to start to create our entities to map our tables that we're going to create and then also have the corresponding interfaces such that we can work with [Music] the we can use the repository methods using typeorm so let's just start with a now we're going to need to work on a few of these simultaneously because because of that integrated approach like um i think we'll just start with the conversation model so let's just have a conversation entity and then we'll just see how we get on so basically what i want to do here is we're going to have a new entity so and we're going to call this conversation and then we're going to export the class conversation entity and then we can start to use some of the type or annotations to sort of lay out how our um table will look so on all of our tables we do have a primary generated column that's got a unique id that gets created so we're going to have that now there's going to be a many many relation here between a conversation and a user so a conversation it can have many users so it's only going to have two but we need to use the many to many uh to signify that you know there's more than one sort of thing and so every conversation's gonna have two users and every user can have many conversations so we can just say many to many we can import that in from typeform and this is going to relate to the user entity so we're going to need to open up the user entity as well so let's open that up come back to that in just a moment i'll just finish off this what we'll do is for many too many relations you need to specify way you're sort of joining the table so [Music] you know we've got this relation to a user entity do we want to get the user in this conversation table or would we prefer to get the [Music] conversation in the user table so what we can do is we can just have what we're going to do is we're going to get the conversations so this is a conversation for example and then this is another conversation when we get the conversation we want to get the users associated with that conversation so we want the join to be on the conversation table so what we can do is we can come here and on our conversation entity that we're working on we can just go ahead and just join the table and because we're going to have multiple users we can just specify we're going to have the user entity array here so that's the first thing now with these um you know nested or integrated tables that have foreign keys to one another we need to go to the corresponding user entity here so just down here down the bottom i'm going to have a many to many now this is going to be based on our newly created conversation entity so we'll just need to import that and then we take the conversation and we take the conversation entity i like to suffix it with entity just to distinguish if we use an entity or interface we can say for that conversation entity we can get the users so the users here were defined so that's a property on this conversation entity so we can just access it using object notation with the conversation entity dot users and this is going to relate back um to the conversations which is the conversation entity and there's going to be an array of them so we've seen one to many relationships and many to one uh this is the many-to-many so on both sides you're going to need to have many to many and you'll need to specify on one of the sides which one you want to join the table on and then basically this user's property needs to be referenced here and then we can call this conversation entity um and then there's the join on this table here so that's the first thing so let's continue to work on our conversation entity so we'll get to an example of a one-to-many now so basically one conversation can have many messages so we're going to need to create a message entity now just before i do that i'm just going to do the last property first because it's simpler the conversation can have an update date column and we can have last updated and the reason we have when the conversation is last updated is because the last updated conversation we want to bring to the top of the list so we can start to work on this one here but as i said we'll need to start to create the message entity so let's just go ahead and copy this shape here and we can get the message entity dot collapse group copy this it's pretty much all gonna you know we'll just get rid of all that stuff here get rid of that although we'll probably need that as well um i'm just going to get rid of these for now but it's going to re-import them anyway so i'll just do it like this now this message entity needs the name message because that's the name of the table we're going to create so when the mapping's done between the you know the type on and this entity and the it generates a table it's going to be called this and then we can just rename the class to message entity now we're going to have the primary generated column here but what we're working on is um let's see the conversation entity so every conversation can have many messages and every message can have one conversation so let me just get this simple column here first and i'll get the simple properties out of the way it's obviously going to have the message so the text you write and then we're going to have this many to one which is the converse of this one to many we're about to create so let's start here let's start in the conversation we'll work on the conversation um but i'll just add this one last property here which is the simple property which is the create date column now we're going to want the create date column because we want to get the date a message was created at so we can display that to the user on the front end so if we return back to our conversation entity every conversation can have many messages so now that we have our message entity we can go ahead and import that and for the message entity what we can do is on that and i'll do a lowercase m here on that message entity there's going to be a property called conversation and then we can have our messages here which is just the message entity so if i go to the message entity now so i can save that and now it's just complaining about this conversation but we'll just we'll get that go on here so we'll have this many to one here and it's going to reference the conversation entity and for the conversation entity we can take the what we just named it so um we've got messages here so we can get that dot messages and then we can just go ahead and we can just call this the conversation as we call it in the conversation entity and this is nothing more but the type of the conversation entity here so we've got that and now these errors have gone away so i think that's what we need on the conversation so every conversation you know it's a unique id for the conversation there's a meaning to many relationship because a conversation can have more than one user it has two users and the users can have multiple conversations one-to-many one conversation has many messages and then the update date column which is last updated so i think that's the conversation entity so i may as well do the conversation interface conversation dot interface dot ts so we can just export an interface here and we just call this conversation and we can have an id which is a number you can have the users which is an array of the user type and it can have last updated which is a date and then we'll just need to import that in we can save that so we don't um we don't need to specify the one to many on the interface so we'll get that later on with some other joins so we can just go ahead and close the conversation entity close the conversation interface and let's continue working on our message so a message it has a unique id it has the message itself it can belong to a conversation with there's many messages belong to a conversation so the create date column and it's also going to belong to a user of course so we'll have a many to one here where we reference the user entity and for that user entity lowercase u here we say user entity now we're going to create this again but basically use a lowercase user entity um messages and then in this case there's just going to be one user so we can just let this equal like that now this needs to be a comma and it's saying that messages doesn't exist yet which is correct let's just save that so we can open the user entity backup and we can just have a one-to-many here so i'm just going to copy this one-to-many here just get the general shape now we want a message entity and it's going to be the type message entity we're going to get our messages here well okay this is message entity so for our message entity actually we want the user here so it's got that sort of inverse relation um and then of course we just need to have our messages here and we just need to import those in okay let's just check that so a message id message user conversation creator and for the [Music] many messages that belong to one user references the user entity references the messages and then that's referenced here in the user entity so i don't think we need the user entity again so let's just go ahead and close the user entity but what we will need is the message interface so let's get that and let's just go ahead and copy this interface here message now it's going to have a couple more properties just going to have a single user it's going to have the message which is nothing more than a string it's going to have a created app which is also a date and then it's going to have a conversation here so the conversation is a conversation well i accidentally got rid of the user here so i'll just go ahead and put the conversation here which is the type conversation okay so that's the message so we can close up the [Music] message interface um let's just open up our conversation entity so okay so we don't need that but um what we want to do is in addition to all the conversations that are created here between two users and their messages we also want the active message so when you send a message let's say the second person in the room is also there since they're active we will want to uh send them a message to the websocket communication so when you just click on one of these rooms it's going to essentially just get the messages that already exist but we want to also have that websocket communication where we're able to push you know client server client so we're going to need a another entity here and we're going to call this the active conversation entity now you could do some relations here like we've just done and demonstrated in the other files um but these uh we're gonna basically add remove add remove and remove these constantly and uh it's just a little easier if you don't have those um foreign keys and stuff like that but feel free to do it however you want i'm going to copy this in here so i'm just going to get rid of a couple things here um basically we want this to be called active conversation and i'm just going to prepend the word active here active conversation entity it's going to have an id primary key now rather than do these relations i'm just going to use regular the regular column i'm going to get rid of these here and this is just going to be a single user so any one person that creates a websocket so let's say here i'm you know where i'm logged in as uh well the server's not running but i think i'm logged in as john 3 here um it would get the user id of john 3 and then we'll have a whole list of active users and then we'll be able to send them a message through the websocket communication so we're going to need their socket id now unlike other ids um this this one's generated for us using socket io uh similar to a json web token i think it's like a hexadecimal although it's a little less characters in this one um so there's that so we able to you know leave a room or leave a conversation based on the socket id that you've made the connection with you're able to get the user id and we're also going to need here the um conversation id of course because user can have many um they'll have the one socket um id for their active session um but and i shouldn't overload the word session there for their uh websocket connection um but we need to distinguish which room they're in based on the conversation id so they'll prevent it from sending it to other rooms and i think that's all we need there for that so we can just go ahead and get the close that we can just go ahead and get the active conversation interface typescript file so let's just copy the shape of the conversation interface close that and close that now we know the names of the properties essentially we won't even need that because we're using the id so let's just prepend the conversation with the word active conversation we're going to have an id we're going to have a socket id which is a string to have a user id which is a number and we're going to have a conversation id so what we can do now is we can just go ahead and do an npm run start dev and just catch any errors if any errors exist and also just check the tables to make sure things are formatted the way we want them to be formatted okay so we've got a unable to connect to the database here now we're getting this type warm issue and one thing i should probably do i don't know if it's related saying user entity conversation is not found i haven't actually hooked up the entities in the module so i'm going to go ahead and do that so if i open this chat module here we've got our imports now i'm just going to well i need to write the imports i'm also just going to get the auth module because i'm using json web token uh the guards and stuff like that so i'm just going to import that in and then we've got this type on module and this is where we can hook up our entities that we've just created so for the feature [Music] here we can have an array of the entities we just created so i believe it's the conversation entity and we can see that we've got this conversation service here we generated before that's cool sometimes it doesn't put in there sometimes it does so we've got the active conversation entity and then we've got the message entity so we're just going to need to put a comma here for the chat gateway and conversation service so let's go ahead and save that and let's see if that helps us out a little bit so here's our few tables we've got we're expecting a couple more to be generated we're getting this error again so let's look into it okay so for whatever reason i didn't have this here like that so let's go ahead and save that we'll give it another go okay so let's see here user entity in active conversation entity user id is not supported by the postgres database so what i'm going to do is i think because i've created these new relations in the user table i'm going to have to drop the existing tables so that will mean i will need to create a couple of users again so if i just go ahead and drop these here if i try and run this again we might not get an error this time so let's just wait this load up okay so it's still airing for us so we must have done something wrong in our code so we'll investigate this so you probably spotted this um i didn't realize i typed in the user entity here so this of course should be a number here and then you can get rid of that save that give it another go and this is why it's good to check incrementally when you make changes um it's a little harder to do that here because you know we have this nesting in three or four different tables here so we sort of have to do it in one bulk go but we can see that we've now made our connection here so let's go ahead and refresh that refresh the tables open it up and we can see all of our tables here so let's look at the four new ones here so the first one is going to be the conversations and it's just going to have an id and a last updated on and that's because there's a relation and then that's in the conversation users user so the relation with the two users um it well it has the conversation id to reference it back to this id here but then it has this user id which relates to the user table that we've seen um then of course there's the active conversation table here so it's gonna have the socket id of the you know the id of the connection between the user and the client uh to signal you know that they are that person and what conversations they're active in um which you know references the user id and conversation id again i didn't do the primary key approach with the also the foreign key approach here you could it just makes it [Music] it just takes a little bit extra to set up when you're using the um you know doing all the ads and deletes and stuff like that so you know that's up to you and then and of course you just set up in the same way that we set up the other tables here um but then in the message table you get the message who it's by which conversation belongs to and when it was created at so we've now created all of the tables for us to you know make our websocket connection and be able to send messages from users to users and we can begin to work on that functionality we'll start on the back end and then we'll switch between the back end and front end and then we'll integrate it all nicely together so let's build up the logic let's start with the back end and we can close these entities we can close this chat module we can close this interface and the back end files is this chat gat way gateway and this conversation server so we'll mainly be working with these two and then on the front end we'll mainly be working with the chat service and the component consuming that service so of course we're going to change a few things here so let's actually just test something let's just test um that our guard is working so we'll just run this ionic serve here and just above this handle connection so we've got this on gateway connection which cause handle connection on gateway disconnect which handles the disconnect so what we'll do is we'll just say we add these nests js annotations here and we'll just pass in the jwt guard here so let's just go ahead and save that that should restart the server here and actually we'll end up wanting this on um you know lots of things we probably don't i mean to handle the connection um to disconnect we need the ability to connect so we just need the guard on the connection there oh what's going on here i must have topped in oh i need to join again that's right all right so let's just do this quickly i'll just join john hotmail.com password i'm just gonna do one for now because i may need to redo this again so get the error message um so basically when we go to here we'll get our friends which we don't have any and we'll have this message here but the fact that it was able to connect means that it got through the jwt guard that we've set up and that's all well and good so it needs to pass that guard to be able to do things um but if it is able to handle connection means you know everything's alright so we don't need to have that uh on other things like handle disconnects and such like that um so let's let's begin by working on our conversation service so the very first thing we want to do here is we want to well we checked that was in the module it auto loaded that for us so we want to have a constructor and in the constructor we want to inject our conversation entity active conversation entity and message entity so we can use the repository method so we can go inject repository you can get the conversation entity import that in now this is going to be a private read-only conversation entity and it's going to be of the type repository for that conversation entity so that's the pattern there that we use now we can just go ahead and bring this down twice we can get the active conversation entity and we're going to need to bring that in and then we're also going to need the message entity and then we'll bring that in of course we'll fix this up so [Music] we'll get that there and that there like that and then i'll just make this a lowercase here so we have these we've injected the repository so we're able to use our um typeform we can use our functions on those um so for example if we wanted to save something to the message table we can use the message entity and so on we've seen this in previous videos um so i was just getting that so we have access there so i'll save this as some formatting here so let's begin to work on some of the functions and let's think about some things we'll need here um so the first thing we probably want to be able to do is creating a conversation so let's say we had our friends list here basically what we'd want to do is we'd want to [Music] when we click on it we'd want to create a conversation now obviously we don't want to create a conversation every time we click on it let's say i'm logging into user one and then i'm creating conversation with user two that should also create that same conversation for user two so we need to check first by seeing if a conversation exists so we can get a conversation see if it exists if it doesn't exist then we can create one so probably start off by having a method called get conversation so let's get that get conversation and we're going to do a bunch of um you know we're going to define the logic that it's got it's basically typeform with the sql and then it'll just be easy because we've created the tables and we've creating what we're doing now is we're creating the methods that runs the sql to interact with the tables and then it'll be quite straightforward from there we can just sort of like from the back end perspective after that's done we can just implement those methods um and then we've seen how we can use web sockets and call that from the front end and then we'll just need to consume that from the front end so i mean this probably is the trickiest video on the playlist but it's also you know we've been building it up and if you've you know been following along all the way to now you know you've done a great job and yeah let's just create it so we've got the get conversation and we're going to get the conversation based on two things it's going to be based on the creator id because let's look at the conversation here um we're just looking at the shape of things this conversation it has an id listen uh and a last updated here so it's got an id conversation id and user id um so what's going to happen is it's actually going to create two rows so we we can see that um like it will create one row here and then two rows here because for every one conversation in this conversation table we're going to have two users so it's going to let's say there's user one and two in conversation 1 id 1 here and then we'll create two rows here so basically we're going to need to join this user id with the user table so let's just open that up so we can get a look an idea of the shape there it's going to have this id here and then behind the scenes it's going to have a couple of joins it um because the way we've set it up on our entities with our relations so we can go ahead and just work on that so like from the front-end perspective we're going to have you know the id of a friend and also of a of ourselves so we can get the creator id quite easily and also the friend id now what this is going to do is going to return an observable and it's going to return a conversation or undefined because it might not exist and it's okay if it doesn't exist because we'll just create one so we can access the conversation repository and i just realized i called these entities here these should probably be called repositories oops repository so let me just check that message entity message repository based on the entity yeah so i just use the word entity a little bit too much there you probably caught that so if you get the conversation repository what we can do is we're going to have access to the repository methods so we're just going to use this create query builder and you know for the conversation we'll just call it conversation that's what we're calling it um it doesn't really matter what you call it i'll just call it the same as the table name you can perform a left join between the conversation so that's that's just what called a conversation just makes it easier to rationalize the joins so based on that it's going to have a users now it doesn't have it directly in the table itself but because we've set the relations in using typeform in the entity and it has this uh you know this relation it can determine the nesting for that so there's going to be multiple users and they're going to refer to the two rows so for those users here and we want to and this is this table needs to be called user because we're combining it uh with this user table here doing a left join so we're combining everything with conversation with on the user's uh key so we can just say where the user id this is going to be equal to the creator id and we just need to escape any um sql injection so we can just do it like this here creator id so it could be the case um you know the conversation it could be that the user id is the creator id so when you make the join between the conversation table and the user table and you're looking at the user id you know a conversation has a creator and you know a recipient so [Music] we can just say create id and the friend id basically if if it's the case that that um basically if the creator id is that or and you can have an all where um the user id is equal to the friend and i might just put a space here um friend id so right now this might look a little weird but basically if you imagine you have a conversation there's one conversation between user one and two so it has a conversation id of one then there's gonna be two rows here for conversation id one user one and conversation id one user two you join that with the user table based on the id they'll just give you access to the properties that we can display on the front end like the first name and whatnot but imagine imagine you know a user has more than one conversation so user one has a conversation with user two in uh conversation one but conversation two is a conversation between user one and user three now obviously if you're looking through this table based on the friend id you could get um more than one result right so what we need to do is we actually need to group by the conversation id because if we group by the conversation id we can check if the count is more than one and then if it's more than one it means the conversation has the creator and the friend both in that one conversation hence that is the correct conversation to get so we can just go ahead and implement that logic that we just discussed get the conversation id grouped by the conversation dot id and having and this is where we can do a little bit of sql having account count star greater than one and then there should only be one match for that so we can just say here get one we can go ahead and we can save that so just to go through that one more time a conversation has two users in it we're joining the conversation to the user table and if the creator id and the friend id are both um they're both present then there's more than one person for that conversation id because we put them in and since it's more than one i.e it's two it's going to return that conversation for us so that's what we want here now we just want to pipe into here and then we can just use the map operator here so let's go ahead and get that and for the conversation we can of the type conversation we even want to return back the conversation all undefined now strictly you don't have to do this because it will just be undefined but this is just sort of helps when you look back at code just to be like oh yeah okay it's not always there so with that we can start to work on the creating of the conversation so if we have a function here we call it create conversation it's going to have a creator which is a user and a friend which is also a user and it's going to return an observable and we're going to get back the conversation that we're creating so we can return and firstly we can just get the conversation and we can since we're passing in so when you when you create a conversation click on someone you have that user and then you also have yourself so whoever's creating the conversation is the creator um so we can just pass in the creator id and the friend id we can pipe and we know that this is going to return a conversation here uh we want to return a conversation but we want to return a different conversation um or we might want to return that conversation but we could want to create a new conversation so we will just go ahead and make use of the switch map [Music] pipeable operator so you know that's just going to take the conversation of the type conversation and then we can just define a variable here does conversation exist and this is either going to return a conversation or undefined so we can say exclamation mark exclamation mark conversation so the first one just says if it's true or um you know if it's truthy or falsey um so if it's undefined um this will say that it's not undefined and then not not undefined means it's undefined or it's falsy so then it will be does conversation exist equal to false but if this exists and then we say not that that will be false but the second exclamation mark will negate the falsies statement and it will you know execute to being true or equate um to being true so basically we can just do this simple if check here so if does conversation exist if it if it doesn't exist we'll go ahead and we'll handle that so we can have a variable here new conversation of the type conversation and this is going to be equal to [Music] now all we really need here are the users so we've got the creator and the friend the conversation has both those in there so what we can do here is we can just return from this conversation repository we can save the new conversation and if we just look at the type here you know we've got the two users here that we're saving in there the id and last updated they'll get automatically put in there for us sort of thing so this will return here but if it makes it past this if statement it means the conversation does exist so what we can do is we can just return the observable of the conversation so that should be enough to create a conversation so the next thing we want to do and we're just going to as i said we're going to go through this back end um the logic the service then implemented in the gateway and then in the front end so basically what we want to have a look at so we're able to create a conversation and we're able to get a conversation we might want to get all of the conversations for a particular user so [Music] if i'm a user and i got a bunch of friends here i want to be able to get all those conversations so i can have this method here get conversations for user passing the user id which is a number returns back an observable of the conversation array now i can return from this conversation repository create query builder for the conversation i'm just going to move this onto another line here i can do a left join and i'm actually just going to copy a little bit of this here i can do a left join the conversation uses with the user where the user id is going to be equal to the user id here now i just want to have this ordering on it because when you get your users you want to have them in the order in which you've last talked to them first so you can just get this conversation dot last updated and we can do the descending order of that and then there's going to be multiple uh here so we can just go ahead and do that now right now we might want to get the users in the conversation so let's say we have a function here called get users get users in conversation so based on a conversation id you could imagine a scenario where you want to get the users based on that so we can just say we have an observable here of the conversation type an array so let me just copy this here it's basically it's going to be quite similar it's just another helper method that we can use create query builder conversation conversation users user conversation id so even though we're we're doing a join here um we might want to do an inner join and select because here we joined it but we didn't actually get the users whereas here we're actually getting the user based on the conversation id so they're based on different things id or conversation id um so we create this query builder conversation so i'm just checking things are okay i believe they're okay um do an inner join between the conversation table and the user table where the conversation id is equal to the conversation id and we don't need this order by although you could but it wouldn't make a difference for us um because we're just passing it by the conversation id so we're only expecting two users to come back you've created a conversation for that conversation you pass it in you pass the id of it in and then you get the users back in that conversation so now we have that we can have get conversations with users and there's very there's a large number of ways one could do this you could very much simplify the um the code a little bit um you know using type orm if you're better with typeform and you know all the methods and stuff like that you know you could obviously make a nice query i just find that when you're doing this query builder and you're using multiple of them um things get a little bit complex so just sort of separating it out into their own so [Music] and of course you could even set up the entities better when with their own joins and stuff like that but we'll just do it based like this so we're going to get all the conversations with the users so we can just go ahead and return so we can get the conversations for the user based on the user id so you're you're a user you're logged in you've got access to your user id you want to get the conversations and you want to get the users so obviously you're going to be a user but you want to get the other person in the conversation you pass in id uh and then you can just go ahead here and you can just pipe and take one and then we can just go ahead and we can just do a switch map so we're getting the conversations for a user we only need to subscribe well before we subscribe we only need to get that once because otherwise we'll just call it again um so take one switch map conversation it's going to be a conversation array now i'm doing this switch map mainly just to map it to the conversations here and there's probably better ways to do this than this approach here but i just wanted to highlight the uh another important um rxjs operation and that is the merge map i don't think we've used that much so basically what this will do is well firstly this return getting the conversations gets multiple conversations and then for each of those conversations we want to get the users in the conversation so then we can based on the conversation id that we're getting back from the conversation or conversations um you know we can get the [Music] um get the user or the users in that conversation so [Music] getting conversation for users it gives you back a bunch of conversations and then the merge map can be used to put each one of those conversations we can just go ahead and we can just return this dot get users in conversation based off that conversation id now what this will do [Music] is rather than giving back an array of conversations it's going to give back one conversation each conversation at a time um with the users so it has this join here so that's going to come in handy and we'll see that implemented on the front end shortly so what we would want next is the ability to join a conversation so if you want to join a conversation we're going to need a few things we're going to need the friend id we're going to need the user id and we're also going to need the socket id and that's going to be a number number string respectively for those three parameters so what it's going to return back is an active conversation so we've created that and just to reiterate an active conversation is going to occur when you click on a conversation you click on a user you want to message and you're going to get when you're connected you're going to have your software id so that's going to be part of the active conversation you're going to have you know your user id [Music] and you're also going to have the conversation that you're in so the conversation id will be needed there as well so going to need those three properties now first things first we need to get the conversation so we can return this dot get conversation as we did it before and we can just go ahead and just pass in the user id and the friend id and then we'll just pipe into that and then we'll just use the switch map operator here and then we'll have the conversation and basically by this stage you should have a conversation um if we set things up um but sometimes you know with subscriptions and stuff they might fight twice or you know those sorts of things can happen easily so it's good just to have a couple of checks and we'll just check basically if the conversation doesn't exist um i'm just going to have this console log here or console.1 but maybe and i'm just going to say no [Applause] convo conversation exists for user id and then we can just get the user id and friend id and of course you could handle this you know you could do error handling or whatever i don't think it's necessary because this is something we're not really expecting to happen um but if it does happen uh for whatever reason basically what we would want to do is we'll just want to return an empty observable so it won't error but it also just won't join the conversation so we're not really expecting that to happen anyway but that's just so it you know fails gracefully or whatever so we can have a constant variable here called conversation id which is since we've made it past here we're obviously going to have the conversation and every conversation has an id so [Music] i can just go ahead and return from so i'm just going to get the auth service in here and we can see that the auth service we may need to create something here okay so below the login method here we're going to have get jwt user you pass in the jwt and you get back either user or null so we can return and this has the jwt service in it which you know it's a library and then we can use this verify a sync and pass in the json web token here and then we can pipe and then we can map and then we can destructure the user that we get so if you're not sure what the type is here what we get back after we verify our json web token is a object of the user type here and then we can just simply return that user there um let's see here is there an extra arrow here and that's here okay now if there if this you know if this doesn't work we can just go ahead and do a catch area you could do a try catch as well but you know already in this rxjs sort of style so we'll just return null here so that's why we need the all servers because we need to you know get the user based on the json web token and remember the json web token that's coming from the requests so they've signed in their json web tokens in local storage it's attached to each subsequent request when they make the request you can extract the json web token and then we're able to get the user from the json web token so we can return back to our gateway so that's why we've got auth service there now what we want next is we want the conversation service that we're just working on so we got that so basically on module init we can access this conversation service we can say remove active conversations sorry this is only happening once and i'm going to put a note here i know i'll put a note over there as well but just to make things super clear runs when server starts so this hasn't got to do with people connecting through a web software connection it's just when the application runs or eventually find this class here this chat gateway and i'll instantiate it and then i'm just removing you know any lingering active conversations or anything like that that might occur so i can just pipe into this and just so i don't get any memory leaks i can just take one here and subscribe to that so i'm just going to go ahead and control period get that in now i'm going to duplicate this down twice and i'm just going to remove the messages here and i'm going to remove the conversations in an analogous way now the web server websocket server is just the server we got this json web token guard on handle connection so this is where we can start to uh i might uppercase these characters um so i'm just going to when you make the connection we want to get the json web token and we have our socket so we can say for the socket there's this method called um oh and obviously we need a passing socket so socket um where is socket socket that's just coming from socket io so [Music] and we're able to pass that in where we can pass that into any of our methods we want um so there's actually this method on it called handshake and we're able to extract the headers um from there and in particular we can get the authorization header now if that isn't true for whatever reason um we'll just say null here just for type safety although you know it's got the json web tokens pass the gut it's going to have it sort of thing um so this dot auth service dot get jwt user we just created that we can pass in the jwt we can subscribe we'll get back our user and we can say if the user doesn't exist this dot handle disconnect and we'll just pass in the socket here so we can just copy this signature here here like i said you can put it wherever you want we'll import user here i'll just check that importer from the right spot yeah looks like it so basically if there's no user um we'll handle disconnect now this should really be a user by this stage we've got like all these checks going on um but you know just you know when you're developing sometimes not everything's set up properly in here you know these extra checks just save you so otherwise we want to do everything else so that means you know the user exists so on the socket on the data for the user well we can put a user on that data object based on our user here so what i might do here i might just delete this one i'm going to have a function here called get conversations and that's going to be based on the socket so remember the socket is going to have user data on it now um and i'm also going to pass in the user id in particular so i'm going to need to create this get conversation function here so i'm just going to do this here get conversations be a private method down the bottom or something all refactored i'm just going to chuck it here i mean obviously if you made it to this video you know quite a lot about coding and you know refactoring and stuff like that so i just want to focus on the more interesting stuff so this is actually going to be a subscription and for some reason i'm not getting um i'm just going to import that i think it's from oh it is coming in it's just i probably need to return the subscription so what i'm going to do i'm going to return this dot conversation service dot get conversations with users based on the user id now we don't want to pipe and take one here because what we're doing recall that we're actually doing this merge map thing so it's going to me you know rather than an array of conversations it's going to make this extra um you know query to the database and then get the users associated with the conversation um and it's going to omit those one at a time sort of thing so we don't want to actually uh make the mistake of using the pipe take one or anything like that it's actually deliberate here um and then we can just go ahead and we can just subscribe and we can get the conversations so we know they're going to be an array of conversations actually we don't know that we're going to get okay so actually that's kind of true kind of wrong um basically what's going to happen is because we're getting one conversation at a time we will be getting all of our conversations but we're getting it in a stream of single conversations being emitted so we will that is true but um basically what we want to do is we want to say the socket id so if i'm connected it's going to have my socket id to that socket id i want to emit an event called conversations and then i'm going to pass in the conversations here um yeah and it turns out um i think this is gonna pass something like this so it's gonna have a conversation an array of one conversation so on the front end that means so like let's let's go back a step let's say i'm logged in and i have three friends i want to get those three conversations and the way i've got it set up with the merge map it's going to subscribe to this method here and then you know this might be id1 it's going to emit this but the subscription stays open and then it's going to emit another thing like this and then another thing with id3 it's not going to be the um you know like this sort of format here it's going to be one at a time so we basically on the front end we can just take the zeroth element because one's getting emitted each time sort of thing so we'll see that we'll see that on the front end um so yeah that's getting the conversations for us um so if we return back when we connect you know based on our json web token we can get our user and then for that user with our socket id connection we can get the conversations and those conversations are going to come with users because we need to we need the both our and the friends data to display things like images and stuff like that dates and who created which posts and stuff like that so that's get conversations handle disconnect it's going to take the socket i'm just going to make this like that so this is going to take the conversation service and then we can just leave the conversation and based on our socket id this is going to fire each time we handle the disconnect so we can just pull up into that and just take one here and then we can go ahead and we can subscribe here ah what's going on leave conversation if i just take a look at leave conversation it's the socket id is a string and all it's doing is it's deleting based on the socket id that you're passed in so if i handle the disconnecting i pass in the socket id one oh it's just a bracket okay okay so now we before we able to send a message we should be able to [Music] uh let's just copy this get the shape of it we want to have an event called create conversation and we'll give the method the same name it takes the socket and it also takes the friend which is a user and then this dot conversation service dot create conversation socket dot data dot user now remember we handled the connection and if it didn't disconnect we've attached this the user object based on json web token so the current user that's logged in to that um socket data user there so that means we can create a conversation with that attached object and we can also just pass in the friend so it's going to be the logged in user and the other person in the conversations um you know you need two people to have a conversation so you're passing both those people we've already handled the logic in the service we can just go ahead and we can just take one and then we can go ahead and subscribe and we don't really need to you know get the return of the correct conversation because what we can do is we can just go ahead and we can just get the conversations again so we have that method there already set for us so this dot get conversations we can get the socket get the socket.data.user.id so recall get conversations here it gets it based on your socket and your user id and then you know we'll meet the conversations so socket message this server oh sorry wrong message uh socket socket data user id and just save that so that handles the creation creating a conversation now the reason we're also admitting conversations again is because when you first load it up it doesn't necessarily mean all your conversations have been created recall that we create a conversation not when we add a friend as or click accepted but when we actually click on the friend and we open up the conversation so that's when a conversation is created now if it is created then we've seen how it handles that just returns the conversation that already exists but if you you know you create a new conversation you're going to need to re-get all the conversations so when we're subscribed to that on the front end you know we're getting the latest data so now we're able to work on uh joining a conversation and leaving a conversation and then we'll come back to sending a message to complete off the chat gateway so i know it's been a long video but there's a lot to cover and it's quite integrated this there's lots of things that you need to do in a bulk um sort of like all those entities are related and all of those service methods you need all the logic written out and then we can handle the gateway and it's it's sort of uh websockets are quite interconnected between the front end and back end so this is probably by far the hardest video but it's also probably the last video unless there's any you know major bug changes or any uh major fixes that need to be done um so yeah we're almost there probably only need another you know 30 45 minutes or something like that um but yeah let's just continue off we'll um let's just copy this shape here so we've got the create conversation we've got the send message we're going to have a join conversation and join conversation is going to be here and rather than message here we're just going to get the friend id the number now we're going to access this stock conversation service join conversation now we've set up the logic so we just need to pass in our arguments here for friend id socket data user id and socket dot id let's just have a look here friend id user id software id for an id socket id oh sorry user id socket id because we attached the user to the socket so we know this is going to return an active conversation so let's just go ahead and just pipe into that response and we'll tap into that actually so we can get the active conversation of the type active conversation on the station i'm just going to import this tap from rxjs so when you join a room you get the active conversation back so this dot um basically what we want to do is get the messages so we can just say this dot conversation service when we're in active room we can get the messages and we're getting the messages based on the active conversation conversation id we can go ahead and we can pipe into that and we can just go ahead and just take one here now i think i will just need to import active conversation the model there the interface so unable to subscribe here and i can get the messages which is an array of messages and for those messages i'm able to say okay this dot server got to do the socket id uh emit an event called messages which is just going to be the messages and messages will need to be imported here um and all of this um we're typing let's see here so we'll need to plug to the outer observable and we can just say okay we'll just take one for that and we can just go ahead and subscribe so just to recap that method when we join a conversation um you know we've got our socket and we have our user id and then we also need to get the friend id so we're going to get that from the front end so based on those you can get the active conversation so when you click on a conversation that'll be the active conversation and then for that active conversation you want to get the messages so based on the conversation id you can get all the messages there and at this stage we're considering like based on whoever the login user socket is they'll get those messages so if someone else is logged in they'll have their own socket id and then they'll get all those messages emitted to them now you could do this with like an api approach but since we're doing everything the web socket approach we may as well have it in a cohesive mana such as this so that's the join conversation now i'm just going to have a leave conversation and then we can return to our messages and then that will be it for the back end basically so leave conversation so we don't need that so we've already got our socket by this stage it's well established that we've got our socket so we can just say the conversation service pass in the socket id oh sorry we're gonna have to leave as well leave conversation pass in the socket id and then we can just come in here we can pipe take one subscribe and that's all we really need to leave the conversation so we have the ability to join a conversation have the ability to leave a conversation we have ability to create a conversation and we're able to get our conversations and handle that connection and handle the disconnect and we're also clearing up all of our conversations on the module init here so all that's left is the send message this is the most interesting part because this is what so everything we've set up has been basically the infrastructure to be able to send the message so it's be nice when it all comes together and we're able to send the message um so basically to send a message we're going to need the socket and i might just call this new message just to distinguish between the getting the existing messages and the new messages i'm just going to go and get rid of this but the socket they'll just come because we've got the socket new message now this is actually going to be a message so recall message has an id mess like the text message user conversation created that so basically now this probably won't happen because this will be handled on the front end i suppose but it's probably good just to have this if there is no new message conversation uh just return an observable null i'll just make it so things handle gracefully but that shouldn't actually even happen um so we can actually just do some object destructuring um so basically recall that on the socket data we put a user object so we can just go ahead and destructure that and we can say for the new message that passed in so when we when we get it i think it's just going to be the string and the conversation but there's also a user and the user is just going to come from the user from the socket so we can just go ahead and use object notation and attach the user here and now we can have our if statement here so if the new message has a conversation has an id and this should just happen because we've got this check here along with the front end check but just you know just to be extra safe here if the conversation service we create a message we have a new message here we can just go ahead and we can just pop into that new message that we're trying to create and we can just take one here we can subscribe and for that message that we're getting back so this is going to have some of the good data for us with id and stuff like that we can say new message dot id is equal to message id because when you pass it in from the front end all you know is you know who sent it and have the text um by that stage it hasn't actually created uh the id but when you save it using the you know the sql query or the typeform performs sql query then we can get that message id back so then we can attach that to the new message that we're going to you know omit so here we can say for the conversation service we can get the active users and this is going to be for the new message dot conversation so we've got the conversation we've got the id of the conversation based on that and then we can say we can pipe into here take one you can subscribe you can take the active conversations which is an array of active conversations and then for those active conversations what we can do is we can say okay active conversations here so for each of those active conversations so at most it's only going to be two um basically so what we're doing here we're sending a message and we're getting all the active users that are in that particular conversation and if they're not active when they go on it they'll just you know they'll get the messages when they join the conversation by meeting all the messages but if they are in the room and there's multiple people in the room we need to go through each of those and for each of those active conversation so each active conversation um basically what we want to do is this is where we want to omit to the from the server to the activeconversation.socket id and here we just want to omit new message so a new message here okay so that's cool so just to recap when you send the message first you check you know the message has the conversation the id of the conversation and based on the conversation id in which the message is coming from we're able to create a new message and we're able to get the id of that message and after we have the id of the message we're able to get all the active users in that particular room in that conversation and for all of those active congress people so up to two um obviously there's going to be one because but there's going to be one person sending a message but someone else could be in that room they're able to uh retrieve that information from the back end and we'll subscribe to it on the front end and then we can get that new message there so that's pretty much all there is in terms of the logic in the back end now we just need to subscribe to it in a similar way how we have in other videos and use it all now there is a little bit of setting up to do there that's relatively straightforward from this point on so if you've been following along for this long video you're doing really really well and now we can just reap the rewards so i'm going to need to just add two more users and also um just add some images and stuff so i'm just going to let's see here i got an unauthorized it means my tokens expired so while i'm signed out i'm going to have a um and i set the token to like 30 minutes for development purposes so that's something you would change in production um but you know how to do that so let's say i have here john two p john two at hotmail.com password accept and join come back here john three um john three p accept and join i'm gonna sign in as john three here i'm gonna write a post john three ah let's see here this is john 3. it doesn't really matter i just want to upload an image here let's get this ionic logo here um let's just sign out jon2 hotmail.com password all right i'm going to have this symbol here i [Music] um so i might just add john 3 here um i can also go to john 1 and add him here sign out now sign in as john one now i'm not going to have an image here because i need to handle the case that i don't have an image so i'll just use this blank image here um as you can see i've got a message from john two i can go ahead and accept that and i'll connect with john three here and i need to log back in as john three probably could have done this more elegantly but um let's just log in as john three john three he has two requests from john two john one so i accept both of those um sorry if i sent a request to john 1. okay i think i did what about john three okay so i'll just sign out here and then i'm going to log in as john one here close that close that and navigate to my messaging tab so now you can see that i got these two friends here and i can click between them now right now we've got name last name here now this rock roll this uh we didn't include this in the data model so this would just be hard coded but you easily could um and we can see that we don't have a picture here so let's go ahead and start to work on the front end side of things so a good place to start when doing full stack apps is um starting with the database tables then doing the service and then the gateway or controller and then going to the front end and working on the service to be able to you know into get interact with the back end and then make the visual changes so we've got this function here get friends that's an api request and we've already set that up so that's getting these friends here now we want to um this should stay the same get new message it's going to get the message although this is going to be a message um this is going to be a message so we're going to need to define a couple of types here so let's go ahead and define these types now in these models here we've got friend request and post let's create a file let's call it message now you might want the dot interface.ts but it doesn't really matter um so what we can do here is we can just export an interface and we'll call it message and you might want to name space things and stuff i'm not going to do that here so we'll have an id we'll have the message the text which is just a string we'll have the user which is a type user we're going to have the conversation which is of the type well we need to create this and created at this is going to be a date so let's create another model here conversation.ts so here we're going to export the interface conversation it's going to have an id of number it's going to have users so the two users and it's also going to have a last updated which is a date so we'll just go ahead and we'll get this user model in here and now we should be able to get this conversation here okay so we can save that and then we can start to look back at our chat service here so now we have this type get new message we can get that in here so we've got get friends get new message send message um let's just work on send message so it's got the message string [Music] but now we want to get the conversation as well because we need to send the message to a particular conversation and what we can do here is we can say new message is going to be of the message type so we'll construct this and then we'll pass in the message and the conversation the user gets attached as we've seen just before and then we can just go ahead and send this new message here so that's getting the friends getting and sending messages what we might want to do is have the ability to create a conversation so for this we are getting the recall we're getting i'm going to close these down ah let's see here where is it create conversation is well the socket's already there so we just need to pass in the friend so because we're the user we just need the friend to create the conversation with so here we can just pass in a friend this is nothing more than the user so [Music] just create conversation get the friend and we can just say this dot socket dot admit create conversation same as it is here because that's what we're subscribing to omitting the event and then we're getting the result of that um we're not getting the result but that results being processed for us um so when we emit things it's admitting to the back end when we're getting something from event um you know it's going to be um this is emitting to the back end an event to the back end um this is coming from the back end so create conversation you just go ahead and just pass the friend straight in like that it's nice and simple so let's go ahead and just copy this down so we might have one called join conversation and here we just need the friend id this is just a number and rather than create conversation we'll have join conversation and rather than friend we'll have friend id now analogously we'll have leave conversation now actually we don't even need anything for that because the sockets already existing on the back end so we just need to leave the conversation and that's all we need to do now all we really need to do we have the ability to join leave create conversations and get and send messages so the missing link for us is just um i might put this get friends because it's an api call i might just put that one at the top and then sort of leave conversation and then the last one last thing we need here is get the conversation messages so this is going to be an observable and we're going to get an array of messages you can just return this dot socket from event and you know the type's going to be the same again you only need it once um probably here and then type script will have type inference for this but i i sort of just like this personal preference that's up to you and these is the messages so that's the chat service and we've worked on now we just need to work on our component a little bit so we should have everything that we need so let's return to our component here and looks like we got an error on submit so first things first we're getting the message from the form then we're sending the message but this now takes a second argument which is going to be [Music] this dot conversation now this doesn't exist yet so we're going to need to come back up to the top here and just define a couple of properties so we've got the user full image path which is a string we're going to need the user id and we have that on the auth server so we'll be able to get that no worries we then probably want to have conversations which will be an array of conversations and we'll initialize that to an empty array and i'm just going to disconnect because i need a little bit of code before we can check a few things but yeah the conversations it's just going to be an array of conversations we'll bring that in now we just need a singular conversation which can just be blank and then i might have one more of these so the conversations the observable version which is the observable version of that and i won't initialize this so we've got new messages the observable uh we've got messages which now is a array of the message type so we've got that now we've got friends which is an array of users we've got a friend which is user we've got a friend a behavior subject version we got a selected conversation index number initialized to zero we've got a user image past subscription i'm going to have a user id subscription we've got the messages subscription i'm going to have a conversation subscription and i'm also going to have a new messages subscription you've got the friend subscription and i'm also going to have a friend singular subscription so i'm just going to copy these down to our iron view did leave now i think we got four more of these so i'm just going to unsubscribe to that unsubscribe to that unsubscribe to that unsubscribe to that let me just check things here [Music] this user image path messages subscription user id subscription uh friends subscription [Music] let me move that one down to friends so it's grouped nicely um conversation new message here friends friends okay so one two three four five six seven okay so that's the variable set and we will need to come back to the template and uh you know display things correctly but let's just work on the logic a little bit on the typescript side of things we've got the chat servers we've got the auth service iron view did enter [Music] we say this user image path subscription we get the subscription and then we unsubscribe to it this message subscription well let's get the user id subscription that one's going to be easier to do so we can say this dot user id subscription is equal to this auth service dot user id dot subscribe we can get the user id which is just a number and then we can say this dot user id is equal to id or user id so we set that user id here i set it to user id and we'll use that to pass into some of the methods we've created so let's work on the this dot conversation subscription now this is going to be on the chat service we can get the conversations um so maybe i missed that one on the chat service here um get conversation so let's just get that we can just um copy this down and we can say get conversations it's going to return an array of conversations from the event conversations i'm just hoping i've spelled everything right here because i've done a bit of a bolt go here but if i return back to the chat component there should be the get conversations here so i can go ahead and i can subscribe to those and for the conversations that i get back of the type conversation array for those i can just say this dot conversation this conversations dot push now this is where i have the conversation zero um now recall i'm going to put a note here from map stream so recall in our chat gateway um if we look at our um getting the conversation with the users here uh we're getting all the conversations first but they don't have the user so for each conversation we're going to [Music] get another conversation um in a stream and so as i mentioned before it's going to be in this sort of format so we need to get the zeroth element of the array and if that doesn't make sense uh go back earlier in this video um sort of explain that um but again the whole approach like the whole approach of this could be simplified just by having some joins uh the entity level and simplifying the query but it's just another tool new tool another approach so basically we're subscribing to these and each new conversation that comes in we're going to push that in so we'll push that into the conversations then we have the messages subscription so [Music] i will actually uh want to do this differently so this dot chat service dot get conversation messages so you want to get all the existing messages if they exist you can subscribe here and for those messages of the type message array what we can do is we can say all message ids because right now we have a message array but we only need the ids of the messages to retrieve them um so what we can say oh we're retrieving the messages but um we only need the ids to be able to display them so what we can say is all message ids this is equal to this dot messages actually let me just back up a sec there wait we got all the messages for the conversation so what we can do is we can say for each of those messages for each message then we can say const or message ids this is going to be existing exist to sorry let me start again initially there's going to be no messages but as you add messages we're going to append to these um so and we'll come back to exactly how those messages get populated um so obviously in the first iteration uh that won't be the case they won't exist but you're subscribing to this so later on there will be messages and when you receive a new message um we'll be able to push uh to that so i think this will make a little bit more sense with a little bit of code so if i just go ahead and map something here and i map a message to the message id so i'm taking an array of objects an array of messages and i'm returning an array of the message ids and essentially what i want to say here is if well firstly if all of the message ids or third array of message ids um is contained by the message id or another way to say that is if the particular message that we're looping through from the messages we got back so we get a list of messages back that already exist and for each one of those messages that does exist it has an id and if that id if that is already in the basically if it's not in our current array because we need a way to display this on the front end so we're storing in this variable this messages and if the messages on our front end contain that or doesn't contain that basically what we can say is we can say this dot messages dot push dot message you might think that's a little bit of overkill you only really need this messages push message but um what's going on here let me just look into that and then finish that point off so get conversation messages there's an array of messages okay that looks okay so this chat service dot get conversation messages subscribe am i missing some brackets or something probably oh if statement probably needs some brackets around it ah let's see here i'm firstly just going to call this this messages subscription so that's not exactly the issue but just found that because it's not one message it's messages i can save that i'm just gonna click on this message here the message uh what's going on oh i've got capslick on oh it must have imported the wrong message somehow ah well that's interesting i was wondering what was going on okay so i got the wrong message somehow this is coming from here now that should be okay so yeah um the reason i got these checks here rather than just this is because sometimes with these subscriptions sometimes they fire twice and stuff like that so i'm just double checking that it's not going to add it to the right twice and get like two messages or something like that in the ui um so here we got this dot new messages subscription and this is going to be equal to the chat service get new message and i can subscribe here and take the message that we get back from the subscription now recall that a message at this stage um when we're getting the new messages coming from the front end so we're just going to have the message and the conversation we want to display the created at time stamp and we can just set that to a new date uh it does need to save on the server and it is possible if it's a bit of latency to be a couple of seconds out but it's uh good enough sort of thing it probably is probably fine um now i want to you could copy this into a function but i'm just going to duplicate this code here so basically um again when when you retrieve the messages and have new messages you can go through all the existing messages so this is where this comes in handy basically is by this stage you've pushed all of them um you push the messages here um but when you get to this case you've got a bunch of messages that already exist and this is just an extra safety check so basically um you know you're not getting duplicated messages when you create a new message as well so i think that's pretty straightforward uh all message ids message map message message id you know it's the same code so that's all there really is to that and we just added this created that because that didn't exist on the front end so now we've got this friend subscription now i might just completely [Music] um actually i might do this friend subscription so this is going to be equal to this dot friend dot subscribe and this is going to be a friend this is going to be at the top any because it's not going to be the type user because it's not going to contain some of the properties that we need so basically what we want to do is we're setting below when we open a conversation we're setting the friend so that means here um we can subscribe to that um but initially we have this empty object now in javascript we can't just say does empty object equal empty object uh you need to do something a little more sophisticated but not too tricky um because essentially what we want to do is we want to determine if the um friend has been updated but it's also it's not the initial state of the empty object so basically what we can do here is we can just say json.stringify friend and initially it's going to be equal to an empty object but because we're stringifying it we can say if it's not equal to the string version of the empty array so empty object it's not the cleanest solution but it definitely works um it's definitely one of the simplest ones um so this dot chat service so if we make it to this stage we're able to join a conversation based on this dot friend id so that's cool uh we can close that now in this friend subscription we can say this chat service get friends subscribe to the friends we set this friends equal to friends now this is where we just want to have some and if check so if the friends dot length is greater than zero this dot friend equals this dot friends so you get the friends um and the friends you get back the very first one that you get which you know is this one here we can set the friend here to that first one that we get back and we can also say this dot friend the observable version we can actively call the next method on that and pass in that friend and since it's not going to be the initial state of the empty object we can go ahead and we can join the conversation and join the conversation means for our user we created a active conversation in the record so then when we create messages create new messages everyone in those active conversations we'll be able to have that websocket communication so basically what's going to happen now is we know that the friends length is greater than zero so we can say for each of those friends what we can do is we can say well a friend is just a user and for each friend or each user we can call this.chatservice dot create conversation because when we get the friends we need to create the conversation for each one of those friends so that's going to happen based on the friend and then after that's done we can say this dot chat service dot join conversation this dot friend dot id you can save that now um another thing we might want to do here is for this chat service when we leave we want to say leave conversation so we can do that now for open conversation we get the friend and index change the select account now first we want to leave so this dot chat service dot leave so we leave an existing conversation and then we set this friend to friend this friend next we actively call we subscribe to that and we just want to set the messages back to empty so they'll they'll change the ui messages back to empty but then as we're joining the next conversation we're retrieving um so this will fire basically and then it will retrieve the next messages that we need so that's how that's handled so that's the open conversation sorted now we just need to finish the on submit method and then we can go to the ui and so we get the message we can say let conversation user ids equal to this dot user id this dot friend dot id um let's sort those so we can compare them directly so then we can say [Music] this dot conversations four each now for each conversation of the type conversation what we can do oh and this is just conversation we can let the user ids so we're cycling through each of the conversations that we've retrieved and we've set and we're saying we need the users for that conversation so we can do a similar sort of thing as before where we map through the conversation.users and we map through those and for each user what we do is we turn the array of the user ids we can save that there and we can just go ahead and just sort those and now that they're both sorted we can compare them because it could be that you get them in a different order so here what we can do is we can compare the objects again so we can say json.stringify conversation user ids is this equal to the json.stringify of the user ids that we just created and if that's true we can say this conversation is equal to this conversation that we're on and then we can say this chat service send message for this conversation and then we can just go ahead and we can reset the form all we need to do now is go to our template and um work through it so you know we've got our main container and then we got our left hand side our right hand side we've got our list here and then we've got our messaging sort of stuff here this is just to get the visual representation of the html we're looking at so select connection this was all sort of going to stay the same um we still need the name to change here and we also need the image oh that's a point we're going to need to determine the image so one more method that we'll need in our chat component is deriving the full image path derive full image path and this should be the very last bit of logic that we need so i'm just going to let the url equal to http localhost 3000 api feed image after that spelled okay and if the user id if that is equal to this dot user id what we can say is we can say return this dot user full image path else if the user dot image path if that exists then just return the url plus the user the image path else if this dot friend dot image path return the url plus this dot friend dot image path otherwise just return the url and it means there's no profile so we can just say blank profile blank profile picture dot png okay so i use the one they don't have a profile image so it's going to handle that case that's the logic there now let's return to our template let friend of friends that i equals index open conversation you pass in the friend you're passing the index uh you've got this ng class so that's all well and good then you've got this iron grid here so what we're going to do is we're going to have uh two different iron images here so two different cases so in the first case if the friend image path actually no that's fine that's fine so if there is an image path for the friend otherwise use the blank profile picture um that's actually okay because that's on the left hand side let's see yes that's the left hand side so we want to work on the right hand side in this chat section here basically [Music] what we can say here is we can have the friend dot first name that's not there we can have some text just saying uh loading and we can have the friend dot last name and if it doesn't exist i'll just speed blank and then this will just be loading um so obviously if you click on something it should be there um i'm button chat messages so let's see here this iron avatar i'm going to get rid of this if check here and i'm going to pass in derived full image path and this takes the user so on the message we have the user iron coal so here uh in this p tag here i'm going to have a span i'm going to say style equals to font weight of 500 let's say the message dot user dot first name message dot user dot last name i know we're doing this in two different ways with the first name last name from the left and right side but you will need the message anyway and on the left hand side you need to retrieve the friends so basically after this span here you can just have a little dash here and we can just say message dot created at and then we can just use a date pipe here and we can specify the short type and then in another paragraph here we can have our message okay and this message is actually message.message so i think that's just about it i'm just going to go ahead and start the server here and then i'm going to check things work on the front end and i wouldn't be surprised if a couple of things were to break but hopefully we can get lucky and just run an ionic serve here so wait for that to load so let's go ahead and sign in here okay let's navigate to the messages here now we've got john 2p here so that's looking good look at the server hasn't broke no conversations exist okay so let's try it out hi john 2 this is john 1. awesome that works hi john 2 this is john 1. it's got the date here appears to be working nicely so let's go ahead and open a new tab incognito mode and we'll see if we can have our conversation work nicely so i'll go to localhost 8100 now i'm going to sign in as john 2. see the image is working nicely here i can navigate to messaging hi john 2 this is john 1 click on john 3 click back on here hi john 1 this is john two and then we see with the logo that got sent yay now just to make sure anything weird's not happening we can go into room three here we can say hey i can click on room three here say hey it doesn't appear in here but if i go back to this room here we see the conversation ends up working and just to triple check i'll check the database is working okay so [Music] we can see our messages here and everything appears to be in order so thanks so much for watching this youtube series guys if you made it this far big big congratulations uh that's probably going to wrap up the linkedin clone we've learned a lot role based access control image upload crowd upload infinite scroll and chat communication for different conversations um so yeah hopefully this has been exciting for you let me know what you want to see next thanks so much for watching cheers i'll see you in the next video
Info
Channel: Jon Peppinck
Views: 412
Rating: undefined out of 5
Keywords: nestjs, angular, websockets, socket.io, websocket, nestjs8, socket.io angular, angular 12, register form angular, angular material, login form angular, nestjs websocket, angular lazy loading, angular websocket tutorial, socketio, nestjs 8, socket.io connection angular, socket io, angular socket.io, how to connect to web socket in angular, angular 7 socket, how to connect to socket.io in angular, using socket.io with angular, listening to socket.io events in angular, ionic 5, ionic
Id: HqoitVcjTEE
Channel Id: undefined
Length: 190min 27sec (11427 seconds)
Published: Tue Oct 19 2021
Related Videos
Note
Please note that this website is currently a work in progress! Lots of interesting data and statistics to come.