Auto-generated transcript All right, we should be live now. This must be a chat. um I don't know how the other guys uh have this open but I'll just keep this over here I don't know let me just make sure that I am actually live you you okay not completely live yet Okay, there we go. Yeah, perfect. Alright, so it looks like the broadcast is working. So, what are we doing today? Okay. I was going to continue building the GoCord application. that I was building last stream. So this is the front end obviously. Let me also open up the back end as well. It's right here somewhere. There it is. Alright. So obviously I did a lot of coding off camera as well. So why not. There's a lot to catch up on over here. hmm I close this No need to eat up all of my internet access as well okay yeah so I built this loadenv function which all it basically does is load the ENV file into the OS dot ENV so I can access it easily I'll show it to you so all I really do is take the pad which is the dot ENV file pad open it up and obviously make sure to close it create a new scanner and then just parse the ENV file and when we get the keys and the values the environment variables we just set them in the environment that's all yeah we do some I guess skip the invalid lines so if the if a line is completely empty then we obviously don't need to parse it if the line starts with a hash symbol which basically means it's a comment then we obviously don't need to parse it we don't need to parse comments and you make sure that there's only two possible like values on every line one is the key and the other is the value and yeah then we just extract the key and value and then set that in the environment and that is all and I use this in a couple places I use it in the database connection to actually connect to the database I also use it in the JWT utilities over here to actually store the secret key which before was just hardcoded over here it doesn't really matter because it's a this is a like a test project just a learning project obviously when you deploy this to production you want to like store all these things in a separate environment variable or whatever but yeah this was a good practice so what else did I build off camera the main things I want to build right now are in these in this E-Types package basically the E-Types package has a bunch of event types and the way this this app is supposed to work is we have a WebSocket connection between the client which is the front end and the server which is this go back end and what we do is we just send a bunch of events from the client to the server and from the server to the client right so we have this event communication based on WebSockets And this event type is just a type that is the event type yeah So I want to handle all of these events. And right now, I haven't done all of them. So that's what we're going to do now. If I open the web package in servers over here, you can see that most of the events I haven't even bothered to handle yet. So we need to implement all of this for the servers at least. For channels, I'm pretty sure, yeah. For channels, I've already handled all of the events. So typing start, typing stop, message which fires when a message is sent, channel history which fires every time you open up a new channel. So you fire this event to get the channel history which basically just returns the last 50 messages in the channel right here so we ordered them by created at obviously to get the latest messages and then we just returned the latest 50 messages we obviously don't want to return all of them because like in a large channel there could be hundreds of thousands of tens of thousands of messages just like this code so we need to make sure to only return 50 but enough talking let me just run this thing and just start developing it. So I'll use air and as always I forget to start the database. Docker container start go core. I need to give it a minute. Databases always take a minute to start so yeah well wait. In the meantime we can obviously start the front-end as well and team run dev. I should probably make commits but I'm too lazy for that especially with personal projects like when it comes to personal projects I just like to like do one big commit instead of like having a proper history which I know is not good but you know and this should probably be in gitignore as well right here maybe something else as well the TMP folder there's no need to have that in in git and yeah that all I think do we need to go to some file in git I feel like that just auto from the go mod right I pretty sure that how it works Anyway the database should be running by now, so let's... yeah, there we go. Alright, this is the electron application, it's broken, obviously, we don't need to worry about that. And, okay, here we are. Refresh this get the web socket and there's my server and there is my chat interface and let me just open the console as well so obviously when I change the channel it fires the channel history event and gets the channel history the last 50 messages sent in this channel so I can obviously send another message as well hey and you can see that when I start typing I don't know why I got decided over here but when I start typing it shows me that someone is typing so this needs some more work obviously because if I'm the one typing I don't want to know that I'm typing right if someone else is typing that's when I want to see this message so yeah still needs a lot of work but as a basic functionality it's fine and um let me go back over there and obviously if you drop the connection or if you somehow disconnect from the app then it gets removed from this list as well and okay so now I'm typing if I just remove the input completely then I'm no longer typing so that's how that works this error is coming from let me see let me just do one thing beforehand there we go uh so let's fix that error this hydration error is coming from i believe it's coming from the message box component uh yeah over here so what's the error again you cannot be descending the p okay um so to fix that we just need to change this into where is it okay okay and I guess we don't need this and we can just change this to a div and there we go format this refresh let it recompile and and awesome so it fits that now i want to continue handling those events um i also have this little drop down here which does nothing um it just logs the selected um option i guess i have only one server over here i have a modale here that again does nothing it just has a very basic form for um creating a new server and i haven't configured this yet so we have a lot of work to do over here we have a lot of work where do we start um i hope the stream is going okay all right i guess we can start with this we want to be able to create new servers and before even doing that I want to have like some common form elements I can use to just to make this look a bit better so if I just do RFC they should obviously be a div or it could just be a field set just to like be more semantic I guess doesn't really need classes right now I'll have a label which will be a block element for the name which will be in the props over here and I guess we can have what else do we need over here label which will be the label over here then obviously need to have the input type text the name the ID will just be the name as well we need a value which can be value and we need an on change event which we can pass in over here so the value and the on change and then we just need to add a bunch of classes here so I can just say the explore provide to rounded, bj-gray 50 and border-border-gray let's say 200. Let's see what that looks like and then let create a props object over here Name will be string label will be string value I guess can be anything I guess Could be a number could be something else Actually no, never mind. Since this is a text input, the value will obviously be a string. I don't know what I was thinking there. BeyondChange will be a function that takes the event and returns nothing. and this will just be I believe and what does this fire anyway actually metal couldn't ever go on change I guess this is how it's done okay and then the props okay so that type is wrong. Oh change event handler. Okay. Change event handler from react. There's a nasty fly in my room. Okay what's the error now? Okay event handler HTML input elements. Okay. Didn't this just work then? I think they should just work now I feel like there's something I'm missing here and I'm not sure what uh oh oh oh i messed up this is not supposed to be a handler it's supposed to be change events there we go because the handler is the function but this e attribute this is the event not the handler you go away please okay uh okay so that's all we need and then i'll just render that over where did I create this option inside yeah okay over here so our servers are very simple we just need to have right why is there a fly in my room okay this is the worst distraction by the way you know just having some big nasty insect Right alongside you We have a server icon We have a server name We have a description and that it So I'm keeping this very simple giving this project very simple. I don't want to create the next discord all by myself That's just too much work So what I'm just gonna do is Not that this render the text input element over here there we go the label will just be the server name the name of the field can be named value and on change so we need to create this and to do that you know what just give me a second There we go. Problem temporarily solved. Okay, yeah, everything's working. So we need to create state over here. This will just be the server name and set server name. And obviously it's a string. So the value over here will be server name and the onChange will be... We get the event handler and then we run the... What do we do? We run the server name handler. And then we pass in event.target.value. There we go. That's all we need to do. me see what this looks like now. Come on refresh, refresh. There we go. Okay that's a lot better. We do need a placeholder field as well now that I think about it. So, take a placeholder and add that over here. And placeholder equals placeholder. This can be optional, because why not and then we just add that over here I don know what a good what a good placeholder for server names Umm I don know Umm friends group I don know Okay, so that's done. That looks like a horrible server name. I don't know. Okay, so what's a good server name? yeah we'll go with that we'll go with that uh okay so that's the server name now i need to have a similar element for the description as well and for that I can just create a text area input dot TSX which can be the same thing as this we'll rename this as well text area input there we go and we'll create a text area instead of an input and we'll give it name okay the value the placeholder and the onChange. And obviously the classes as well. I'll just copy those. I think that's all we need to do. Change my handle. Actually, no, text area element. Okay. Text area element. There we go. Now that should be fine. There we go. Awesome. So now let's change that as well. We'll create a text area element over here. It will have a bunch of properties. So this is going to be the description This will be desk The value will be I guess I need to create state again So with this Set Description there we go, okay Anyone change will be sets our description to e.target.value and that is all. Now we can remove this as well and just see what we have now. Refresh and it's a bit better but still need to improve this form a bit. okay so it's missing a placeholder that's for sure describe your group here thanks and i want both of these to have a bit more margin vertically m by two let's do that and i guess we can also do full width because why not same thing over here yeah i'm kind of just experimenting with the styles over here a bit and just see what this looks like yeah that's a lot better now uh i will want to increase the margin here so 2 is clearly not enough. Let's go with 6. How's that? Same thing over here. Okay, and that's a lot better. I also want to have some margin below the actual label. So, let's say mb2 over here. And here as well. And let's see. Yeah, that's a lot better. Awesome. Okay. So, we have all of this. Now I also want to fix this server icon field. This is going to be a bit difficult. Because we also want to display some sort of an image preview. and I completely forgot how they do that in TypeScript. We basically like take the image that the user uploads and we turn it into this blob and grab the data URL and then just render that as the image and so on and so forth. I'll just ask GPD to do it for me So I guess what I should do now is create an image input component And this will just be like another fieldset. And, I guess it can have the same properties as well. um, m by six, that's all, right, okay, a label, which will be for the field, the label comes over here, we want to have a label over here, and a name, then we need an input type file, which, wait oh one more thing um because we can't really like customize these these file input elements very much what we want to do is customize the label instead and just hide this input element so let me see how i'm going to do that as well um but first let's just render this okay and I think that's all we need for now. Let's create the props object as well. The label will be a string, the name will also be a string. Okay perfect. Now let's render this over here. I should obviously move this to its own like component as well but image input still gotta fly in my room image input there we go and now it's going to take the server icon as the label and the name of this will be icon the name of the field awesome okay refresh this and see what it looks like okay and obviously you can select a thumbnail or something over here with this now we can actually ask the GPD to do this for us So what am I going to ask What do I need over here building an image upload component when the user uploads an image I want to show a preview of it give me the code to do that so what we basically need to do is use the on change event handler grab the file that the user uploads, turn it into this blob and read it and then just get the data url of that and then use that as the source for the image element. So yeah there's a couple of things to do there. I'm not gonna do that. I'm just gonna get the bot to do it for me. Okay so how do we do this? all right everything fine up here okay yeah everything seems good okay so I need to have the image and the preview okay so let's build this in the image input element I'm going to have the image and the set image which will take will be a state also going to have the preview and set preview I think this is supposed to be a string yeah this will be the URL of the image that I was talking about it's a string or it could be null. There we go. Whereas the image over here is going to be either a file object or null. And for now it's just going to be null here as well, probably. Yeah, awesome. Now the preview is probably going to be local to this component, right? But when it comes to the image, we actually want that to be... we want that to come from the parent component so I will have to change that but first let's just get this up and running okay now we need to actually create the event handler for the on change event so I have an e which will probably be a change event for HTML input I believe And I guess we need to import this There we go Then we grab the file which is going to be in e.target.files I guess this can be undefined, yeah, as I thought That's not gonna help That is, okay, perfect And if it's, if we have a file, we set that to the file, we read it then okay okay got it okay super simple stuff see create the file and then why is that giving me an error Did I miss something? I don't think so... Oh! A dot. Okay, my bad. Alright, so we have this. The image is going to be the file. And this is going to be a file object, obviously. and then the next thing we need to do is create a reader I guess which will be what file reader I think and when it finishes loading the file we want to set the preview to reader.result as string so cast it as a string and then we actually read this and we read it as a data url see so we need to create a data url of all of this um and then we read the file i guess that's all we need to do right so we have all of this now let's actually create an image element we'll give it a size of let's say 24 we'll make it rounded and we'll give it a background of 200 just so like so if there's no image there at least we can actually see this right then I'll just render the preview over here as string there we go now format it and let's see what this looks like yeah there we go so this is what we're going to have now if I can just find a image uploaded it and okay that didn't do anything oh I forgot I have to actually have the on change event over here so we need to actually call this function obviously otherwise nothing's going to happen right? Okay, compile this again. Now let's choose an image file and there we go. So we do see a preview over here now. Now what we need to do is to make this whole element look a bit more pretty I guess and first of all water break. hmm hmm so the way to do this is to actually customize the label element not the input element we are actually just going to hide this I believe I can make it display none hidden and if I can't then we have a problem but we will just change this to hidden We'll just change this to hidden. And there we go. See? So even if I just click on the label, we can still like see it. So what we're going to do is keep this actual input element completely hidden. And we're going to style the label. So I'll give it, let's say, a couple of classes. Let's say flex, flex call, space y2. and inside here I'll have a paragraph tag which will actually render the label and then I guess we can have what's a good design for a file input element I guess we could have something like I don't know actually you know what we shouldn't even have a label over here we should We should have it over here. What we should do is div size 24 rounded full vj grade 200 Render the image inside over here and move this up here we say flex flex call and if the if there is no image then we will render the following and we will render the and I guess instead of a dip this should be a label. HTML for the name. We don't need all of this anymore. I can have a span element over here that will actually render the label. And this can have a div which will be, let's see justify end it's gonna have a size of 12 let's say and obviously rounded full rounded bottom full i have no idea what i'm trying to do here dj white and let's just see what this looks like we'll also give it flex and center the content inside it completely and let's just see what the hell I created here okay I broke everything okay so so what am I going to do now what I am going to do is we're going to create the what conditional am i adding here if the image exists then I want to render the image preview otherwise I want to render the this div over here there we go I can remove this one now and okay I messed it up oh there we go and I think that's all yeah and remove this there we go now let's see what I have over here okay still not enough what am I missing the height should be 12 the width should be full there we go yeah okay still not what I wanted to have we should probably change the background here to let's say grade 300 and see what that looks like okay I should probably also increase the size of this whole thing to let's say 36 and yeah then let's see what happens okay no I fucked up that's still broken where am I so first of all overflow should be hidden just why not right and what's the half of 36 I believe 18 right so 18 over here there we go yeah that looks a lot better and now what would make us even better would be if I had an upload icon over here so I believe I have one in blue side and if I don't then I should probably get one as well but let's create a flex column here and let's render the upload icon there we go so we do have that the size can just be 24 I don't want it to be too large let's see yeah that looks kind of better if you want the width of this whole thing should to be a bit bigger so yeah it does work let me just test it and there we go see and that's not fine yeah okay so the image should just take up the whole width and height of the container right there we go and upload it again there we go so we have it perfect yeah that's all we need I think so we can obviously keep improving this but for now I think this is good enough now this whole form I am going to move the whole thing out of here and into a component and we call it the create server form I guess Like this. Sweet. okay let's also add the missing imports all of these yeah that's all we need let's grab the state as well there we go import this that model open okay so we need them and all over here as well so the next question is whether we want to have the model in this server sidebar component or in the create server components that's what we need to think about where does this model actually belong Let's see. What we can do, actually, is move the whole thing away. Or... Hmm. Hmmm. What can we do over here? I mean both approaches are fine to be honest but... What's the best one? But one thing's for sure, this component is going to have plenty of other modals as well, so in order to not interrupt all of that, should just create it in here and just to keep things consistent i will change the name of this whole component to modal form just so it's clear it's super clear that um that we serve this form to a model just so that's clear now we can actually have that open and set open state and yeah open and set open there we go obviously be a boolean and we'll put all of this inside of a model as such we'll say set open equals false over here and then the open state will just be open and set open. That's all we need. Okay. So this should actually just be true by default because if we're rendering this component the first time we're rendering it, it should just open up the modality by default so create server modality form and let's see you can say modality open and end we render this component there we go so this modality open isn't exactly a modality open it's just the boolean to check whether we should open up the server the create server form or not so yeah and now we actually have everything perfect okay I do want to increase the width of this whole thing so let's give it a min width of let's say 2xl let's try that I'll see what it looks like that's a lot better to be honest okay everything is fine over here perfect now we actually need to handle all of these things so let create actually that before we do any of that let actually handle the event in here and what it called server create okay so in our go back and over here let create the server create event handler um handle server create and right now i just want to um let's get the event that will be sent by the client by the front end and then let's also get the connection the client object website client there we go so the first thing I want to do is just log all of the data that we get from the event and just see what it looks like and see what we can do with it all right so I'm just gonna do fmt.println. There we go. That's all we're gonna do. We don't even need to reply anything for now. Okay and over here let's call this handleServerCreate with ev and c as the arguments. Awesome. Now when the server create events is fired it will run this handler function so now we can actually go over to the server create okay over here so what we can do is const handle submit this will be a function it'll take I guess an event which I guess is a form event or submit event yeah I think it's a submit event okay and create server okay over here so the first thing we're going to do is call the handle submit function over here that didn't work and mouse event HTML button okay let's remove this let's just call it a mouse event of an HTML button element and I guess we need to have mouse event here as well no mouse event is not generic I guess this is coming from the react There we go Okay still not working Oh oh there even more types over here Element and mouse event. This is insane by the way. How many types there are in TypeScript. Still not, still not correct. Am I just missing another... yeah I think I'm just missing another one of those what am I missing over here this is a mouse event object it has an HTML button element and a mouse event then an element and a mouse event and then that mouse event in and of it in and of itself has another element in mouse event. This is insane. The amount of chains that you have over here. This is absolutely insane. Wait, still not fine? Okay, you know what? This just sucks. I feel like I added all of these but nothing hmm you know what I'm gonna just ask chat GPD to fix this for me because the error is just that I don't know what the exact type is over here and so I don't know what type annotation to add to this event so let's get the appropriate type from over here and then So I guess we don't need this then? Yeah, there we go. Well that was simple. Okay. Okay, so we have this. Perfect. Now we can actually grab all of the data. And before we do that let also grab the image they should come from value which will be the image file and then and on change event handler the value will just be a file or I guess it could be empty as well and then you can fire the on change event over here which will be let's see on change return nothing obviously but it will take it will take the file object okay file there we go we don't need that the set image anymore over here the value will be the image and that's all we need perfect so over here we can actually now have a bunch of state you can say conserver icon and set server icon will be use state this will be either file or null and for now it will be null icon and then the onChange will be it'll take a file and then it will set the file to the server icon as such this is probably yeah so it can't differentiate between null and undefined so I guess I'll just call this undefined for now because it doesn't really matter both of them are just empty types okay now we have all of this now we can create over here first of all let me just console.log everything and see what we get right so server name server description okay let's console.log this and okay there we go I'll upload an image I'll create some sort of a group name and some description here create the server and okay so we need to prevent the default event handler e dot prevent default let's do that but it does get printed I did I did see it getting printed so there we go upload something and there we go okay so this is the file object which is the server icon and this is the description this is the server name this is the description so we are getting everything we need perfect now we actually need to send this to the server now we communicate with the server through JSON and I believe in order to send files through JSON you need to encode it in base64 because JSON doesn't support bytes right it supports strings so let me double check it's been a while since I did this but I'm pretty sure sending files through JSON base64 pretty sure that's how you do it hmm the alternative is to just send the raw bytes in a separate event or a separate message but obviously that's just extra work and we don't want to do extra work so using what am I saying using base64 is obviously the easiest way to do this but clearly it is much slower so I guess it's fine either way, but I kind of don't want to do it just to have fast performance. I kind of just want to go with bytes. If we do build bytes, if we do this with raw bytes, how exactly would we build this architecture so we would have a payload over here okay and we could either just send two messages or we could send just one and but two messages would be extra complexity as well that just kind of a pain to decide between You know what? Who cares? This is a practice project. There's no need to add so much complexity to it. We're just going to use base64 and we can figure out the byte shelf later. We can obviously build, we can obviously accept bytes as well but the problem there is that we're gonna have to create two separate events for this and just accept different kinds of like different kinds of data not just JSON and that would just need me to change the architecture of this whole backend so much. So we're not even gonna do that. We're gonna use base64. Uh, how do you base64 encode something in TypeScript again? Um, let's see. We have all of this. We need to... I guess we need to get the WebSocket connection. So const web useWebSocket. And this is a custom hook I built. I'm not sure if I built this in the last stream we're just off camera but this is a custom hook that and a context which just creates one single global web socket object and then i can just subscribe to different events on unsubscribe from those events and just like send messages to the clients and everything and uh yeah so how are we going to do this i don't know why i keep checking obs again and again Give me a second. Yeah, alright. Okay, so, yeah. Let me just show you what this context does real quick. We basically have a type message handler This is a function that actually is called whenever we receive a message from the server and this message handler will receive the payload from the server event and just do whatever it wants with it right and we have a context type over here this is what actually going to be available to us so we can send events to the server which will be of type WS event which has a type payload and an optional message I I don't know why I created this message field I have it in the where is it in the back end as well if I show you the WS event there we go yeah I also have one a message field over here as well but I rarely use this honestly the only things I really use are the event type and the payload so this message field is pretty much dead and unused I don't know why I even have it but yeah so we have the send function then we have the subscribe and the unsubscribe functions and what these do is this is entirely client-side by the way this has nothing to do with the server but what they basically do is they take the type they take a handler and the handlers that you pass into this subscribe function they will be called whenever we receive a server event of that type so let me just show you an example of this close all of this I'll just show you an example so we're using the use web socket hook over here I'll extract the send function and once we get the data we want to send a WS event a web socket event object the type will be server create the payload will be let's say name will be the server name description will be the server description and icon will be the server icon so yeah that's what we need to do and what this is going to do is it's actually going to send the event to the server and then the server will obviously do whatever it wants but it will create the server but before we can do that we want to convert the server icon which is a type file into uh into a base64 encoded string so let me see how we do that in typescript again okay okay Okay. Wait we get a data URL Isn that what we already do But then why do I have a file object over here You know what, let's just send this. Let's just send this and see what we get. Okay, this is going to happen. Okay, okay. And in here, we're just going to print everything we get, whatever it is. So let's just send this event. I'll upload an icon. and this is a thumbnail from some old from the latest video I built actually so go watch that as well but anyway we'll have a server name we'll have a description we send the event and okay so the websocket crashed why did the websocket crash that's weird Do I not have the socket open? Socket is connected, okay. And why did it crash? There we go. Okay and the event should have fired now so if I go back over here. Yeah there it is. So here's the description, here's the server name and the icon is a map object, an empty map object so clearly we did not get the icon as expected. um okay so we do need to convert this so this is not being converted in typescript let's convert this to base64 and then send that What's the URL of this? Oh, okay. So I guess the problem here is that we're using a file object instead of just base64. Let me go back over here. Let me go to the image input. This is going to be a string instead of a file. There we go. Okay. All right. And this is going to be, the, where is it? Okay, over here. reader.result as string. That's what we're going to call the onChangeEvent on reader.result, which can be an array before a string or no. We're going to cast that as a string. I'm going to put this up here. That's all we need to do. The value over here can just be a string again. there we go and that's all we need to do perfect so now if I now if I do this again it's a socket connected yes it is there we go why did i console.log this um um websocket read limit exceeded okay so this is going to be another problem how much of a read limit do i even need for this this is going to be a bit of a problem because for most messages a couple of kbs is fine i believe for my uses i only have I think... Yeah, I only support 1kb buffer sizes, so 1024 bytes. I wonder if what I'm doing is even the correct approach, you know? Like, is this the best way to upload files, or should I have something else? No, the read limit. Okay. So the buffer size and the read limit is... They're two different things, I guess. Where do I set this? I set it over here. Okay, 4KB. That's obviously not enough. Let me see. Where is... Where is the... how much is the size of this again can i see the size 88 kb okay um yeah let try raising this to 10 mb um let say 1024 1024 that going to be 1 MB and then that let multiply that by 10 that going to be 10 MB okay and do I have a write limit as well no I don't okay I guess that's all we need let's refresh the page reconnect the web socket and then okay and now oh yeah there we go so this time it works perfect so we received this really long base64 encoded string and this is the file information obviously so here is the map this is the description of the server this is the icon which is going to be obviously a really long string of basics before and at the end I guess is the name of the server so if I this works now what we need to do is extract all of this and we can do that with we have the text well we have the name server name and the name okay if you can get from ewe.payload name that's what I called it right if I go back over there yeah we have the name we have the description, we have the icon. Okay. And the description and then the icon as well. It's going to be icon, it's going to be icon okay. And here I'm just using the comma okay idiom in Go. And the reason I'm declaring all of these together is so i can do this if name is not okay or description is not okay or icon is not okay then we're going to we're going to return an error and the way we can do that is i have a helper function on the client over here and what this does is it just creates a reply and it sends that to the front end so it calls this reply function which will just convert this the reply into a json object json bytes then it will just send it to the send channel which will then send it to the client so this is just a helper function here and what does it take it takes an event type i'll just return the same event type that i got the message will just be a malformed request let's say and then we return because we obviously don't want to continue executing the function if we don't even have the appropriate data right so we return over here now we have all these fields now what are we supposed to do with them we're supposed to let's see we want to create a server so first things first we want to let's just do this models.server and this is going to have a name which comes from name a description which is the description over here and oh these are any okay i mean these are going to be strings so i can just directly cast them probably not the best practice to do this directly like this we should probably just um um first the we should probably check this in a conditional and handle any error cases but for now i just want to get this done so yeah don't do this this is bad practice obviously check every type of session you make like if name dot string something like this if n okay and then if it's okay then you use the n value which is the string do it like that not like this this is bad practice but for now i just want to get this done So what else do we have over here? We have the icon URL and we have the owner. Now the owner is going to be the person who is creating this. So let me grab the user object. We're going to do gorm.g models.user. Pass in the database. Let me get the database as well. There we go. This is the database function which I showed you earlier It just returns the database instance and then we can pass that into into go on and then do something with it to select all the columns from the users table where the id of the user will be and we have the user object inside the connection and we can just say user over here actually we don't even need to get it from the database we already have a pointer to that user right here see that user there we go you can just do it like that okay now we need to have members channels we don't need that owner ID this will just be filled automatically with form base we don't need to worry about that, IconUrl, we need this. So in order to get this, I'm just gonna have a temporary variable over here, but we do need to actually parse the basics report and then configure this. So before all of that, let me just say object and error equals form.g models.server the database I guess we still need the database so database.connect.create we'll have the context and then we'll pass in the server over here and this will return an error we handle that as well we just print it error creating server creating new server printed over here return obviously and then what we're going to do next is somehow parse this icon which is a string into an icon URL before we can do that let me just see how do you parse a base 64 string in Go. What we want to do is parse this, convert it into bytes, and then we can save those bytes into a file on disk. So let's do that. And then once we have the file saved, then we can just grab the pad to that file and save that as the icon URL. And that's good enough for local. some local storage on your machine but obviously in production you want to use some sort of uh maybe an s3 bucket on some cloud service or something you want to use that so yeah it just depends on how you're basically or where you're saving this but for now i'm just going to store this on my machine so we get the base64 package we use std encoding decode string and this will will give us the output okay let's do this Okay, that's the wrong project. There we go. So grab the base64.stdencoding.decode. This will take... Okay. Decode string. Okay and this will take a string and it will return the bytes. we'll send the icon here, we'll add a typo session. Again this is not the best practice. You should use the comma okay idiom, not this. But yeah I believe this will return an error and also bytes. So I'll just call this icon bytes. Make sure to handle the error. log.println error parsing icon. Print the error. Return. Then we have the icon bytes. Now we can actually save this to a file. So how do we actually save this? I guess we should have a media folder. So, we're going to use the OS. Create dir or make dir. Okay, there we go. Is there a way to do this without... I don want to actually cause an error here here go create directory if doesn exist Actually I believe we can actually if don we have an exist Okay we do We do Okay door fs door entry read door. Okay. Okay. because what I don't want to do is like create a directory and then it sends me an error and that error says that their directory already exists I don't want something like that okay so yeah I guess we can just do MKDir all of them okay and it will just be a media directory and then OS.Mode.Kerm by the way I still need to learn what these different file modes are and what they do it's on my list I just haven't gotten around to doing it yet but yeah so this will return an error if error is not equal to nil log dot int ln error creating media folder error and return there we go there's no need to redeclare this okay and then okay by the way all of these file handling stuff we should move to a different package and we should use that. We should also probably create an interface for saving files so that we can create different kinds of like storage systems. So basically we can have one storage system that's gonna store files on our local machine, another that's gonna store it on some Amazon S3 bucket or something and and you can just keep adding them. Maybe a storage system that stores files on Google Drive or something like that, right? So we want to have a common interface for all of these things. That's something I will build in the near future future but for now let's just get this done let's just build this basic functionality so now we know that this exists perfect we have the media folder now we can actually create a file inside the media folder plus the file name okay the file name can just be the I Guess the server name As Server name plus Icon dot I guess JPEG Actually, I think we need to extract the extension from over here yeah so like data PNG or something right okay so we need to do that as well I guess so to get that data image splash PNG okay I don't even want I don't even want to do this on my my own let's just ask GbD to extract it for us there we go remove all of this extract the file extension from the base 64 string for me there we go So all we need to do is split the string at this semicolon, then all we'll have is this part, then we just remove the data, remove the image I guess as well, and then we just grab the PNG. There we go. So to be honest, let's just do it ourselves. To be honest, let's just do it ourselves. I don't even need to look at gpd. Okay, we'll have another function here. Extract extension. We'll just give it a string. There we go. And it will return a string which will be the file extension. So there we go and extract extension from the icon.string and then we'll render that over here as well. And then over here we can do fmt sprintf slash s and we can just add the file name over here then this will return an error all right no this will return a file and an error can i just write directly to a file creating it if necessary okay so this is what we need we can just have it write the file for us directly okay the content will just be the icon bytes i guess and the perm i believe can just be os.multi perm i think and this will just return an error so we grab that we handle that over here it's not equal nil log dot print ln error writing file love that return and yeah i think that should be it and if all of this works fix this if all of this works then the icon url will just be the file path so um let's grab this whole portion and just call this fpad. fpad will just be the media folder plus the file name. Then we save that over here. And there we go. This is all we need. So now we just need to extract the extension. So to do that, what we can do is we have this string. We can separate the whole thing. Split it. strings dot what was that strings dot split the string add this colon character and select the first element and that will be the um i guess the metadata of the base64 string why do i call it a const um yeah okay yeah i mixed up typescript and go over there but yeah okay so we can remove the um the mime type we can get the mime type by just removing i believe this is what you call a mime type but i'm not sure you can replace the data portion as such from the string there we go We just need to replace it once, I guess, right? Why is this giving me an error? String, string, string, int? What am I missing? Oh, oh, oh, okay. There we go. We just want to replace this with basically nothing. and then we can do a switch mine type I guess in case for now let's just have an image slash PNG if this then the file extension will just be PNG and the default case can just be return nothing okay okay there we go and over here to replace this with metadata that's all we need to do and now it should be fine so let's see what happens this will either work or it will just return a complete error and yeah okay mm all right this looks fine to me so let let run this and let see what we get Refresh this so we can reconnect the websocket Add the thumbnail, add this whole thing. Okay, event sent and error parsing icon. Illegal base64. Okay, over here, we got an error over here. Why did we get the error? hmm the base ssd code shouldn't be like that to me goes like this hmmm Oh Oh, so we need to remove this header got it, okay I guess we'll just use this function then Data URL, okay Decode base64 string this will return an error or the bytes We handling all of that already and yeah okay So now let's run this, let's refresh again. Okay. Watching media. Let's see. Okay, there's the media folder. And it did create... Oh, there we go. So this does work. Perfect. Yeah, and no errors. So I guess this works. What it did was it basically created the media folder, which didn't exist before and then it saved the icon over here the name of the file is just the server name then the icon and then the extension just like we we set over here so it did save the file perfectly i still have a nasty fly in my room it said it saved the file and it saved all of the other stuff as well go away saved all this okay so let me just go over to the database and make sure that this got saved as well because this is awesome. So what we're going to do is docker execute slash IT the go code container and we going to run binbash Let go And then we going to enter the Postgres shell CD into GoCord Select everything from... What table is this again? This is servers. Okay. And there we go. We have the server name. We have the description and we have the file path for the icon URL as well. And the owner ID which is just the user the user hassan which is me and uh yeah there we go so everything works the server got created the icon the files got saved perfect so that functionality is built took me a whole more than an hour to build that yeah i think i'm gonna end the stream there and we can pick this up later I think this is good enough for now I will probably do some more Off camera work on this Because I just have I've got some free time so why not right But yeah I think This is enough for now Yeah Good stream Good stream good coding Good work Yeah And if anybody's watching Bye bye you