Electron Course - Code Desktop Applications (inc. React and Typescript)

freeCodeCamp.org · Intermediate ·🌐 Frontend Engineering ·1y ago

Key Takeaways

This video course by freeCodeCamp.org teaches how to build desktop applications using Electron, React, and TypeScript, covering topics from setup to advanced features like data visualization and secure communication.

Full Transcript

welcome to this comprehensive course on building robust desktop applications using electron react and typescript throughout this course you'll learn everything from setting up your development environment to implementing Advanced features like data visualization secure communication and custom window frames all while ensuring a seamless user experience Nicholas Zimmerman developed this course hey there I'm Nicholas siman I'm a full-time software engineer from Germany and I absolutely love web development which is why I'm so excited that there stuff like electron that allows me to combine my web Def and my Nots skills into One Singular crossplatform app that will run on Windows Mac and Linux and we're going to build an app like this right here today which is going to be a resource manager inspired by the Windows Task Manager performance tab so basically some pretty graphs about your CPU RAM and storage usage but before we'll do that let's take a look at how electron actually works and why you might want to use it all right so what actually is electron well as I already mentioned it's a tool to build crossplatform desktop apps with JavaScript HTML and CSS or alternatively something like react to replace these three things and some popular apps that already use electron are for example Discord or Visual Studio code which you probably already know that it exists all right so how does electron actually do all of this well let's try to visualize an electron so this big blue box is going to be our app and we of course going to need to have some way to open up a window so electron calls these things browser windows so actually a window of an bundled browser that can display our HTML CSS and JavaScript and electron creates this window because it actually bundles a whole version of chromium so basically every time you download discorde vs code or whatever electron app you will actually download a whole chroman browser that electron controls under the hood to display and hide windows and interact with them so basically what this browser window does is just one window of a Chromium browser that you downloaded with your electron app but of course rendering isn't enough we also want some information about the system like for example how much RAM we using and the way we can do that is using the main electron process and this main process is basically just a nodejs app so you can imagine that when you start your electron app this main process will boot up and it will do some of the tasks that it's designed to do the main task of course in the beginning is creating Windows slash interacting with electron so basically interacting with the electron Life Time opening Windows reacting to Windows being closed closing windows itself whatever you need to do all of this can be done and you can even create multiple windows so you could also have like three of these if you needed in our case we just need one but it is possible then again electron can of course do much more for example it can interact with system apis so system apis could be something like the system tray to create icons like the ones you see up here on the right on mecos or down on the bottom right in the taskbar of Windows systems and the most important part for our use case it is of course just a node app so it can use node modules so basically most of the things hosted on npm and of course the buil-in node stuff as well so the fs module the OS module or even something like Express if you wanted your electron app to also host the web server for whatever reason or whatever in our case we're going to use some helper libraries to get more information about our system like the memory usage for example but most of this could also be used using node standard libraries but what's important right here is these are separate processes so you can imagine that there's basically a thin line between the two that separates them from each other so this guy can't use code from this guy and vice versa so how can I actually get data from and to the electron app using the browser window well there's actually one thing in between these two or multiple things but mainly one thing and this is the IPC event bus which is basically a third really really tiny process that allows both the main process and the window processes to publish events under a certain name with some data and then listen to these events to basically get some data back so you could imagine that maybe the window Publishers hey I want to delete object number 42 and then the main process tells every window hey entty 42 was deleted please update your UI so that it isn't displayed anymore stuff like that and this is inherently how these two different processes can communicate with each other and now that we understand how electron does all of this let's now get into what technologies we're actually going to be using to first of all develop our app and then actually turn it into an exe because electron actually can't turn stuff into exe dmgs msis app images whatever you need an extra tool for that so let's try to visualize our text tag so down on the bottom both for the front end and for the back end we're going to be using typescript then we'll separate the back end and the front end into two separate Stacks basically so right here we've of course got Elon and on the other side we've got react now the question is how are we actually going to turn all of this into a fully fletched electron app well first of all we need to bundle our react app to actually have HTML files in the end and for that we're going to be using V which is just a bundler that allows us to really easily and effectively create HTML out of our react that is fast and small which is really helpful for our use case and then we have of course also need to bundle basically all of this into a finished electron app and for that we actually going to use electron Builder which is one of many tools that can be used to basically convert your normally node.js electron app into a fully fledged exe app image whatever you need for your specific system needs so you could create an installer you could create an a portable app whatever your users want to use and this is what electron Builder does so basically we can write our electron and react app in typescript we can take the react app turn it into HTML using V embed that into electron and then build all of that using electron Builder to in the end have a runnable project and honestly don't worry if you don't understand all of this yet our next step will be building this little Foundation right here that will basically set up our react app build it into HTML embed it into our electron process and then building this electron process into an app that can run on your system so an exe a DMG or an app image depending on what system you're using and once we've built this all of this should be a lot clearer and of course you can just jump back after building this to this part of the course if you still don't understand everything and want to try to understand it better using your new knowledge that you've gained by developing the actual thing and after we've developed this Foundation we will of course get into actually developing the stuff we need for the resource manager and also doing some developer experience improvements but before before we start I would like to add one little disclaimer right here which is that your operating system will most likely show you one of these warnings when you try to open the build app we're going to create now which basically tells you that the app wasn't created by a trusted developer and that is the case because for the operating to actually trust you as the developer you would need to add a code signing certificate to your app and this is something you would need to purchase your app will still open just fine when the user just clicks yeah I trust this app I accept this and many production especially open source ones actually do that but I just wanted to mention this because I don't want to add anything into this course that you need to pay for so we will not go into code signing certificates because those can only be obtained with money which is something that I just don't want to add into this course because depending on how you get it and what operating system you're targeting the process would differ anyways so just look up the process that you need for your users if you want this warning to disappear and yeah if you just don't care then that's even better then you can just follow this course and everything will be just fine and now that we're in our IDE we can actually start by creating our UI first so let's run npm create V Dot in an empty directory so the dot just says I want to create our V project right in the directory I'm currently in and then we'll choose react because we of course want the react project in typescript and now we just npm install this shouldn't take up much time so let's just talk over it real quick as you can see the files were already created right here and now we've got our note mod modules and we can hit npm run def to actually start our project now if we just control click onto this link right here we can see here is our re and react project but we might want to optimize this a bit because right now everything is directly in the source directory but of course we'll also have some electron code which will also live in the source directory so let's try to split this up a bit by creating a UI folder in here and basically moving everything that's currently in the first directory into that UI folder now just doing this will of course not work yet because V needs to know where our code is now so let's just head into the index.html which is basically the entry point for V to know where all your files are and here we can see we've got a script tag that references Source main. TSX and of course this needs to be Source /i/ main. TSX now and now if you just hit npm run def again we should be able to see see that everything is still working just fine but of course we've also got this public folder right here which we don't really need right now either because we're not going to have a FV icon or whatever if we basically embed our app inside of a desktop app so let's just get rid of this thing right now by removing it and also heading into the app. TSX right now because it also references it removing this a inside of this and now if we just look into a w again we should be able to see that our VAP is still fully functional even though we of course removed the FV icon and this icon right here now let's also change some stuff about the build because if I just run mpm run build right now we should be able to see that it creates a this directory that contains all of our output so basically the finished HTML and JavaScript this is good in some ways but for our use case it isn't as good because electron Builder will also create a this directory so these will conflict which we don't really want so let's just change the V config a bit more by saying build and our build will have an out deer so basically where do I want to put my finished output and this will just be this- react just so we know where our code is and now let's just quickly remove the current disc directory run npm run build again and now we should be able to see that this react is created and it's still contains all of this normal code right here of course right now it's actually tracked by git which you don't really want so let's also head into the git ignore and add this react into the ignored folders just so you never commit it because that wouldn't really make sense and to now combine this UI with our electron app we of course need to first of all install electron so let's just run mpm install D- save def electron we can actually install electron as a def dependency because it isn't needed in the final bundle of our app so the exe or whatever it is only needed to build it so even though we will be importing stuff from electron it isn't actually required to install it as a normal dependency a dep dependency is plenty and do also make sure that you set type module in your package Jason this should be set automatically by V but sometimes it isn't for specific V versions so just make sure that it is set because otherwise we can't use es module syntax instead of our electron code which we will need to do and now let's actually get into configuring our electron app so let's create a new folder inside of our source directory called electron and in here we'll add a new file called main.js we will turn this into typescript later on but for now JavaScript is easier and here we can just import from electron and we'll need two things first of all the app and the browser window and here what we can do is basically interact with the app so as soon as we import it electron will do all of its magic to basically create an app that we can interact with and here we can say app.on SO waiting for an event on the app and we'll say once the app is ready we want to run this Arrow function and then here we can say okay the app is ready now let's create a window and that window will be our main window and it is a new browser window this browser window can be configured in many many ways so we can set stuff like withd or whatever all of this isn't required for now we'll get into it later so for now we'll just add an empty config object in here and leave it at that and then we can just say main window. load file and the file that we are going to load is of course our index.html file inside of the dist react directory so you can imagine that people will have this project in different directories on their computer because once you distribute it you can't be sure that it's under whatever directory your code is under it definitely won't be so people could have it under program files on Windows or applications on a Mac or maybe even in the downloads folder if you distribute it using an exe with nothing else so you need some way to know where your project is currently running from and the app object actually gives you a helper for that which is called app.get app path and now we of course just need to append slist react SL index.html to this and everything is fine right well not actually because these slashes only work on Mac and Linux while Windows requires back/ sport paths but we don't need to do this ourselves because node actually includes a nice module called path so you don't need to install this because it's already included in the standard Library set and what it can do is do all of this magic for you so you can just run pa. join and we can just say I want to join our app half with/ dis react SL index.html and now we of course need a way to run this as well right so let's try to configure that as well by heading into our package.json and first of all telling it where our main script is so this main attribute is basically telling electron which script to run when booting up and this will of course be Source SL electron SL main so our main file right here will run once electron starts and of course we will need a death SCP for that as well so let's just rename the first def script we got to Def react and add another one which is going to be def electron and it will just run electron dot so start an electron process in the current directory and take this main script right here and now let's just try that out real quick npm run def electron and as you can see we got a blank window well why is that we can just head to the view up here on the Mac or if if you're using Windows then this should be in a menu bar right under this bar up here and we can just run view toggle developer tools to see the normal developer tools and get our error and we can see it didn't find a file so what did it try to request let's just reload real quick and you can see it tried to get file assets index blah that isn't what we need because of course the assets directory isn't on the root of our computer which is what a try to get right now it is actually in a relative position to our index.html so if we just head into the index HTML right here we can see it tries to require SL assets SL index which is exactly what we saw but of course on a Mac slash means the root of the file system which is not what we want we actually want/ Assets in both of these positions right here so how do we actually solve this well it's actually quite simple we can just go into a v config right here and say the base path of project is slash instead of Slash and now if we just npm run build again to basically recreate this index HTML file and wait a moment for it to be created then we can now run the electron app again wait a few seconds and we should see that our UI is now fully loaded inside of our window and of course all the react state is also still working and now what we've got is basically a local electron app that can use our local UI with some local state of course as well that it's fully interactive using react and yeah it just creates this awesome little window so next up let's get into actually configuring this with typescript to prevent some errors in the future and luckily it's actually quite simple to convert all of this to typescript especially because we've already got typescript installed because as you can see the V project already contains a TS config we are not going to use this typescript config though because it expects frontend code which we don't really have in our electron app it's just a note project so the first thing we're going to do is tell the project so the V project basically to exclude Source SL electron which means that we're going to Define our own typescript config for the electron code right here so go to the electron directory create a new file TS config do Json and this way we can basically now tell typescript what it should do to exactly those files inside the electron directory so let's just turn this file right here the main.js into a main.ts and Define a little type in here type test equals string just so we've got some typescript code here that would create an error if it was tried to run using JavaScript because JavaScript doesn't know what a type is and then we'll just add some config in here so this config will simply just be all of this so basically what should the typescript compiler do to a code and what should it expect from our code so what this will do is basically tell typescript okay here are all of my electron typescript files please convert them to JavaScript using the following directives and put them into our out Dear so let's go through all of this first of all we are enabling strict mode this is something you should do with basically all of your new projects because strict mode enables you to have null safety and all of that stuff which is really helpful and when you're creating a new project you should basically always set strict mode to true then we're telling typescript to create es module syntax so basically Import and Export instead of require and module. Export this is because electron expects us to use es modules in the newest versions especially because our V project also says that it's a module so we of course want typescript to generate an es module which is best done using ES next as a Target and then we'll also tell typescript that our code will be written in is module syntax by using modle so basically we're telling typescript hey we're going to create a note project using ES next basically and this will tell it hey convert my es module code to es module code don't convert anything basically just remove all of the types then of course we need some place to put our generated JavaScript files because typescript will basically just copy our typescript code remove all the types and paste it somewhere else and this will be two folders up from RTI config so one folder would be sorus two folders is basically on the same level as this disc react code as you can see I've already played around a bit so we can actually see this electron right here and then we'll also have ignore errors from dependencies so basically skip lip check will say if any of our dependencies don't match these rules right here so for example they aren't using strict mode then we of course still want to be able to build our code and for that to work we will need to tell it hey don't check if the libraries support all your typescript recommendations they are JavaScript anyway so let's just ignore all of that and use them just the way they are you wouldn't want your project to not build after all if you just have a dependency in there that doesn't support what typescript expected to now we will of course also need to somehow tell typescript that it should run this config and for that we'll just add another script to a package Json which will be transpile electron and transpile electron will just run the typescript compiler using the project we just defined so Source electron TS config which will basically just compile all typescript files inside of the electron directory which is a really simple way to handle things so now let's just TR it out so I'll just remove my existing dist electron directory and run npm run transpile colon electron and now after actually saving we should see that when it runs our new directory is created which is up here this electron and it contains our main the JS so now we of course also need to tell electron that it should run the generated file and not the r on the directory which is really easy we'll just change this main bit right here from Source SL electron to dis- electron SL Main and now if we just npm run def electron we should see that everything is still working even though we now actually have typescript code and if we just head into our main.js we can see it's basically the same code as before just without the type we just Define so now there's just one more thing we need to do so that we don't commit anything that we don't want we need to exclude this electron from our git track files by heading into the git ignore and basically doing the same as we did with this react just adding this electron into here saving and now our electron project can just use typescript without us needing to worry about just about anything which is really really helpful and now there's basically just one more step we need to do until our app is ready to be developed on which is setting up electron Builder so let's npmi save def electron Builder and after it's done we can actually already created a config foot so in many documentations you will find that people add a build property into the package Json and put the config in here this is fine but it's not as easy to differentiate as if you had a separate config for electron build so this is what we're going to do we're going to create a new file called electron-builder dojon and that config will contain a few settings it's going to be an app ID the app ID is basically a normal standardized way of naming your app so it starts with com then your company or personal name in my case it's going to be n- cmon and then your app name so something like uh prepare for my course preparations or something like electron course for example like this then we're going to Define what files should be included in our app so of course this should be our source code so our JavaScript that gets compiled from our typescript and our HTML so a disc react folder and then there are just three more settings basically which are the operating system specific settings so what should my Mech build create so a DMG which is basically an installer for Mech apps Linux I want an app image so a portable Linux application that runs on most Linux Theos and because Linux also allows you to categorize your app so that it can be displayed in a different manner I also want to say that my app is a utility and then on Windows I actually want to create two apps so a portable exe and an MSI install hey future Nicholas right here I just realized that there's a really minor issue with this configuration right here because it works perfectly fine for mac and Linux builds but there's a really small issue with Windows builds preventing electron Builder from actually creating a finished build because for some reason electron Builder will require you to always set a desktop icon for Windows builds and just won't use the default electron icon so you can just go ahead and say icon is do/ whatever your icon is called as you can see I've created one right here it should at least be 255x 255 pixels and ideally a PNG and as you can see the build does actually set this desktop icon for my project right now even though I'm of course on Max so the build work before as well but this should be enough for you to continue following the course even though the rest of the course will of course not show this icon right here but the finished project on GitHub will of course contain it just so everything works on every operating system let's move on and now there's just one more thing we actually need to do here which is heading back into a package Json and adding the required Scripts to it so if we just head back to the scripts section we can add these and you can see most of this is actually really similar so we first of all transp electron then build our HTML and then run electron Builder with Mac arm 64 so basically I want to create a Mac build for arm processors so an M1 for example and then for Windows basically all of this is the same I just say I want a Windows build for x64 so a normal Intel or AMD processor and the same thing for Linux as well and now we could actually already try to run one of these if you're on Windows then you might need to run this as administrator because it will need to install some stuff on your system for the first time and if that doesn't work properly then just try to run it as administrator so now let's just go npm run disc Mac and we should actually see that it can't currently create our project properly because there will be a little really tiny error which is that even though electron understands this main bit up here perfectly fine electron Builder will actually expect you to add a file extension here so let's just call it this- electron main.js run this again and if you're still having trouble then please make sure that the system you're targeting is actually the same type of operating system that you're using right now so if you want to create a Windows build do try to create it on Windows and not on Mac or Linux it will most likely work to create a Windows build on all types of operating systems but sometimes there can be issues with exporting to different operating systems so just try to use the same one you're exporting for when creating your builds all right so now it's done it might have taken some time that's no issue whatsoever it will download some stuff it will compile some stuff whatever but now if we just check out we should have this disc folder right here for my case I created a Mac build so here I will see a Mac RM 64 folder and also my dmgs right here so there are different ways of running this now depending on what you build on Windows you should just see an Exe on Linux you should just see an app image and on the Mac you see a few files but the most important one is still the DMG and of course depending on what you configured here these things will differ so for example the portable will create in XE the MSI will of course create an MSI but now test this out let's actually head into a folder right here I'll not install the DMG for now because on Mac there's actually a full version of this thing in the Mac arm 64 folder that I don't need to separately install I'll just open it and now we should see that our electron app is booting up just fine so we've now actually created a fully working electron app as a bundled project that can run on any separate machine without needing to install note or whatever other software which is really really awesome in my opinion and now if you just get some info about this app we can see it's 240 MB big and that's because as I mentioned before it bundles a whole Chrome browser so this is one of the reasons why many people don't like electron because it creates large apps because of course bundling Chrome will take up a lot of space but for our case this is totally fine we just want to create an app with an awesome user experience using our existing knowledge and this is something we've just done we've just created a simple app using HTML CSS and JavaScript or rather electron react and typescript so now let's get into actually developing the app but first let's try to do some DX improvements so our development will be a bit moover without needing to run 50 scripts to get everything done transpiling stuff and all that mess we'll try to optimize out of this a bit now so let's do it and of course our most important development experience Improvement will be that you don't want to run as many scripts to get developing and to do that we'll actually use v hot modu reloading when we're in Death Mode instead of just building the front end and bundling it inside of electron because that's just requiring us to reload every time we change the front end which is not really effective so to do that we'll actually need one dependency so npmi D- save def cross- n and this is basically just used so that we can set environment variables on both Mac Linux and windows because on MEC and Linux you can just go ahead inside of your def electron and say nodecore n equals development and then run electron dot and the node and will be set but on Windows you will need this cross n bit in the front so cross en then you environment variable which by default should be node and because that's just standard then we'll call a development and run electron all right so now we know if we are in development right but how does our code actually know that well let's just create a new function in our electron code right here called util dots and we'll export function is Def from here and this will basically just return a Boolean and it will return process. n. note n equals development so basically when we're in development so when we are running using the def script then this will be true otherwise it will be false so this way we can basically feature toggle different tools when we're in development or when we're in production mode so for example we can use the V HMR so hot modu reloading server when we're developing but use a pre-built application when we're in production and that's exactly what we're going to do now and the first step to achieve that goal is to actually set a fixed port for the V server so we're back in our V config right here and we're going to configure our hot modu REO server so the server is going to be on Port 5,123 you could choose any port you want but I just thought that number was nice and we'll tell it that if the port isn't available then please warn us because what we're going to do now will only work when exactly this port is being used and now if we were to actually head back to a package list and run n PM run def react then we should see that it now opened up on Port 5,123 and if we just check this out then we can see here it is everything worked just fine perfect and now we obviously also need to get this running so now let's just try to use our changes right here so if is Def then do the following else use the stuff we did before so if we're in Death Mode then we want Main window the load URL and this is basically the URL with the port we defined for V so basically on a local machine use the V port and give me whatever it outputs and then if we're not in Def then we of course want to build with our production files so this- react index.html so now to try it out let's first of all go ahead and transpile our electron app to actually turn all of these new typescript changes back into JavaScript that can run in electron then let's just spin up our electron server and we should see that it actually creates a blank screen why is that well the V server is currently not running so HTTP Local Host 5,123 doesn't return anything yet so let's close this electron app again go into a second terminal start the V server again now let's go into the main terminal and start electron again and now we can see that our app is actually running and we can also try out the hot modu reloading real quick by basically just putting these windows right next to each other and just inputting a one right into here and we can see the hot module reloading works so we now don't need to rebuild our front end and restart the electron app when working on front end code we only need to restart when we change the electron code which already quite a nice DX Improvement but now let's actually get into not needing all of these commands because now we also need two terminals that's starting to get ridiculous right now so let's optimize all of this a bit and trust me this is the last thing we'll do until we finally get to development I just think this is such a good DX Improvement that I can not tell you about it so let's install one last package npmi save def npm run all which basically allows you to run multiple npm commands in parallel while still being able to all of them simultaneously using control C which is really helpful trust me and now let's just do some improvements right here so first of all we can say whenever I want to start the def electron app I'll first of all compile it so basically when I run def electron I want to say npm run transpile electron and when that's done then I will start the development server using cross en node development and electron dot so this way we already spared one command now let's also create a little def script right here and this Dev script is extremely simple it will just run npm run all-- parallel so run all of these commands that are coming up in parallel and then we just run def react and Def electron make sure that none of our commands are still running in any of these Terminals and now if we just hit npm run def we should see that both of these things spin up simultaneously and now our electron app is spun up right here and we can try out the hot modu reloading Once More by just heading back into our app right here opening up the app. TSX and just removing our little testing one that we inputed and we can see it's working absolutely fine and I can both of these simultaneously so I won't spin up like a th000 V servers or whatever which would be annoying and especially with the strict P stuff you can get in really confusing situations with that so now we can basically just run one command which is npm Rune it will spin up our front end it will compile our typescript and then it will start up an electron app all using just one command so now we are finally ready to get developing and first of all we of course going to need to pull all of the system resource so CPU usage RAM usage and storage usage from our machine so to do that let's first of all remove this testing type we created and create a new file to have all of this separated out a bit called resourc manager. TS and then here we're going to export function poll resources and this function will basically just be our polling that we're going to start to basically in an interval inform the UI that there were changes to the resources so that it can update and of course we're going to need an interval to poll it so we'll just Define a conent up here really quick polling interval and we'll say that we want to update this twice every second so every 500 milliseconds and now we can just go ahead and say set interval and whatever we put into this function right here will of course then run twice a second perfect so now we actually need to get our data and for that we're going to need a few functions and another Library which is OS utils so let's just go ahead and install it real quick this time not as a Dev dependency because we actually need it in the finished build so npmi os- utils which we're going to use to get our CPU and our Ram information so let's start off with our CPU utilization right now by defining a function get CPU usage and what it will do is basically just call Os utils which we're going to need to import import Os utils from OS utils it can't find a declaration for this module so let's just get that as well as a def dependency npmi D- save def at types slos utils this is necessary because OS utils doesn't bring their own types so if you want typescript and full type completion and stuff then we of course need to get the types separately which are available in at types slos utils this we can save as a dep dependency because it only contains types that won't be put into the finished bundle anyway all right so now we should get some good autocomplete as well and we can see there's already a lot of stuff and CPU usage seems really good and now this takes in a call back with a percentage so let's just try that real quick and we'll just console.log this percentage and then we just say that on every interval we want to get CPU usage now let's just try this real quick by running npm run def to spin up everything and then we should see that this is locked inside of our console now so let's just wait a bit and now we see that we don't see anything so why is that well we have Haven used the poll resource method any anywhere so let's just go into our app real quick so the main.ts and just start polling the resource as soon as possible quick fixing this by importing it and now as soon as our app is ready and the windows rendered we will start poing our data so let's just start this once more wait a few seconds until everything is ready and then we should now see that my CPU usage is being locked out perfect but now I of course don't want this inside of a call back I want to return this from this function so what we can do to do that is actually wrap a promise around this to basically turn this into an async function so to do that we'll just return a new promise and This Promise basically takes in a call back that takes in the resolve function and basically when you call the resolve function then the promise will be returned and you can basically await the function or value that was passed inside of this resolve so what we can do now is basically just say okay when OS utils CPU usage calls its callback we will just pass the value that is passed to the Callback back inside of our promise so now when OS u. CPU usage calls this resolve function with a percentage we will basically get a promise with that percentage that we can await so let's just try that by actually going ahead and saying I want to await get CPU usage I'll of course make this an async function to be able to use a weight and now we can just say const CPU usage equals await get CPU usage let's just console lock that real quick again CPU usage restart everything and now we should see that everything still works the same but now we've got a easier to use and more modern function to work with which works perfectly fine as you can see and now let's do the same for our RAM and it's even simpler honestly because it's actually a synchronous function so we can get our RAM usage by calling OS utils do free me percentage and subtracting the value from one because this is a value between 1 and zero and subtracting one from the 3% usage will of course give us the used usage so basically 1 minus 3 is used and now we can just do that as well and just say const RAM usage equals get RAM usage like this and then we'll just coner loog in object real quick so CPU usage and RAM usage Ry this once more and now we should also see our Ram utilization appear in here let's just wait a bit and yeah here it is perfect and now let's get into actually getting the data about our dis as well for that we're going to use the builtin node module FS which will only work for this starting with Note version 18 so if your version is older than that then I'd first of all recommend you update because those versions aren't supported anymore and also if you don't want to update then just use any other library that can do that there are many others out there on npm but we are just going to use FS to make our life a bit simpler so import FS from FS first of all like this and then we're going to define a little function that I'll actually copy because it's easier to explain than to actually life code it so here we got our get storage data our storage data will use fs. stats FS sync which is basically a function that gives us the synchronous stats so basically without needing a weight or whatever about a specific region in our file system so we'll basically get the root file system because we want the whole disk space on our complete machine and then we can just say okay if I'm on Windows I of course want my C drive to be tested so basically where Windows is installed right and if I'm not on windows so on Linux or Mac I just want the root of my file system because on Mac everything starts at slash same on Linux and then we can basically use the data we got from here called stats to calculate everything else so our total storage space is basically the the size of a single block so on a dis everything is stored as blocks of a specific size in bytes and then I of course want to know how many blocks there are and if I now multiply the size in bytes of each block times the amount of blocks then I will know how many bytes in total my disk has and the same thing for the free space on my disc I need once again to know how big each block is and then I can just get how many free blocks there are if I now multiply the amount of free blocks with the space that each block takes up I know how much space is free on my drive so now to get my total space on the disk which we're going to use later on I can just say I want to divide my Total Space by 1 billion which is basically to get my storage space down to gigabytes so dividing bytes by a th would be kilobytes by a million would be megabytes then we get gigabytes right here and then we just flaw this because I want a singular value without any decimals so for example something like 240 GB instead of 240. 267 or whatever and then to get our usage I'll first of all go ahead and say three over total so how much of a percentage is currently free on my disk and then once again just like with a ram I of course subtract that value from one to not get the free space but the space that's used up currently and now we can just get our storage data up here as as well so const storage data equals get storage data and here we can now just go ahead and say storage usage is equal to storage data. usage so let's just try that out as well and now we should see that all of these three values are locked out and we should be able to continue working with them from now on and you can see all of this is locked out and of course the storage usage is a permanent value right now because why would it change I'm not really installing anything right now so no reason for this value to change right now perfect so now we're getting all of this Dynamic data about our system but we might also want some static data like our CPU name how much memory there is in total how much storage there is in total all of this data won't change so we can just get it once and basically just display it in our UI to make it look nicer and to do that we're just going to export another function called export function get static data and what get static data will do is first of all use one function we already have which is the storage data so con total storage is equal to get storage data do total so now we got the total storage in gigabytes then we also want our CPU model so const CPU model is equal to OS which we're going to need to import this is another note standard module so we actually don't need to install it just go import Os from OS and then we can actually go ahead and say os. CPUs we're just going to expect that there's only one CPU in the system so we'll get the first one and get the model from that so this would basically be something like apple M1 or i57500 or whatever basically just the name of your CPU which is really nice to display in some like this resource manager we're building and then lastly we also want the total memory in gigabytes which we can get through using OS u. total mam dividing this by 1024 because this is actually stored in MIB bytes and we want jibb bytes so basically to get the 8 gigabytes which are actually gtes we just divide by 10,24 to convert these mtes into gibes and then we flaw this result once again just so we don't get any awful looking numbers we just get as singular number like 8 16 32 whatever and now we can just go ahead and return all of these three values real quick so return total storage CPU model and total memory in gigabytes and now we also get some static data so next up let's actually try to get all of this data inside of our UI to actually display it and build a nice looking interface around it but of course to actually allow or back end to send data to the front end we need to somehow understand how the back end and the front end communicate using this IPC event bus right here that I've talked about earlier and to understand that we've got this little illustration right here and this illustration basically shows us how the app and the UI communicate using the IPC bus in the middle and what this IPC bus basically enables us to do is to have our app basically first of all send data to the front end or to also have our front end send data to the app and even wait for a response from the app so basically we've got a bidirectional way of communicating between the two apps using a custom protocol and this custom protocol will be called the IPC renderer object in our front end but the issue is that this IPC renderer object is non-standard so it's not part of the default apis in a Chrome browser so the app will need some kind of a way to basically send this object over to the UI so that the UI will be able to use the IPC event bus and there are actually multiple ways of doing this the most common one you'll see unfortunately is actually setting up some unsecure settings and then using window that require electron in our UI this seems absolutely fine because the IPC render is part of the electron object so it can only be imported inside of an electron app and this would enable us to use it inside of the UI which does seem good but using window. require will allow us to require every node module there so also something like FS so somebody who would be able to interact with the UI using something like xss for example would also be able to interact with the whole file system and basically every note module that is installed which we of course don't want but luckily there's a more ideal solution which is called a preload script so basically we can register a script that has similar permissions to our app and runs before our Windows initialized to then basically append data to the window object inside of our browser window so you can see here we can import electron we can get the IPC renderer object from electron and then we can actually Define a little object right here that will then be appended to the window object inside of our UI so there we will have something like window. electron and then access to this little object so window. electron request static data could be a wrapper around the IPC renderer function that will then interact with the IPC event bus to basically not give full access to all the electron apis or even worse all the system apis inside of our UI but only really limited access to those functions that we actually want the user to be able to use so basically our preload script will have some kind of a wrapper around the IPC bus using IPC renderer that will be used to request static data from a back end and this singular function that will rep this functionality can be exported to our window so that the UI is actually able to use this specific method and get some static data and of course I know that all of this sounds really complicated right now we've got all of these different scripts and it would be awesome if you could just require stuff but of course for security reasons that doesn't work so we set up a preload script in between our app and our UI to basically allow us to add this little Bridge of content that we want our UI to be able to access using this little object right here and then this bridge is everything that the front end can use to actually interact with his IPC bus to receive and send data to the back end and really don't worry if you don't understand this yet we'll implement it shortly and as soon as we've implemented it you can either look back on this little explanation to better understand it or even better you might already understand what it is after having implemented it yourself so now let's get into actually implementing it to make this as easy to understand as possible all right so now before we actually implement the IPC part let's start off by actually loading a preload script and getting to know how all of this works because that's the initial part we need for IPC process to actually be available inside of our browser window and to do this we'll actually need to now change the settings we are adding when creating our new browser window so in here we can do a lot of stuff like setting a width for example so the initial width of the window or the title all of this stuff can be configured in here but what we want is the web preferences so basically everything that is connected to how our web browser will have permissions and all that stuff so in here you could do something like turning off context isolation or enabling note Integrations both of the things I just told you not to do because those can cause security vulnerabilities because they will basically allow you to access everything in node.js from your browser window which is not something you want to do in many cases what we actually want to do is adding a preload script now the unfortunate part right here is this is actually a path so you can't just add a function in here or whatever this expects a string which is a path to a file and because this path will be different in develop and production mode we'll actually add a new file for ourselves right here which is the path resolver TS and our path resolver will have a really simple script which is get preload path and our get preload path will of course need to import a few things so let's do that real quick import path from path because we of course want a normalized path that works on Mac and windows then we'll also need the electron app so import app from electron and lastly we'll need our is Def method that we defined previously let's import that and now let's get into what the function actually does so we are joining a path in a crossplatform way so basically we get the path of our app which electron gives us a little helper for called get app path and then when we are in development mode we are adding one dot so basically in the directory we are currently go to this electron and when we are in production we actually need to go up two directories and then we of course want this electron D preload do CJs so why is it called CJs well electron Builder will actually bundle all of our typescript files inside of this electron folder into a singular file in the end and because we actually need to load this using the file system this can't happen so we'll just add another extension and write this in commonjs to basically avoid this and this little workaround right here is also the reason why we're going up One Directory when we're in the production build we will later on tell electron Builder to actually handle this file separately thus causing it to be placed in a different directory so now let's just create a new file called preload do CTS because you of course want typescript and compile it down to see JS later using the typescript compiler and here we can now do whatever we want basically so we can just go const electron equals require electron remember we are writing common Jaz right now so this needs a require keyword and now we can basically do whatever we want so electron dot and now we have a lot of stuff right here like a context bridge for example and this context Bridge can now be used to basically Al bridge data between our electron process and our main window and the way we're going to do that is by calling the expose in main World function and what Expose and Main world does is basically just append whatever we're adding here to the window object so what we want to do is say we want to add an object called electron so this name right here to our window and then this thing should have the following content and here we can now just add just about anything we want I'll just go ahead and add these two methods right here because those are quite close to what we actually want later on so we want one method to subscribe to our statistics so basically our backend will constantly send us data every 0.5 seconds and we want to subscribe to that using a call back and whenever the call back is called our statistics will basically be sent to the front end using that callback right now we are just calling this call with an empty object because we didn't Implement any IPC stuff yet but we can do that later on and then we add another method called get static data where our front end will later basically go ahead and tell our back end hey I now want the static data so basically what's my CPU name how much storage do I have how much RAM do I have please send it to me and here we'll also just go ahead and say We'll conso log the word static and now this should theoretically when we save everything and actually use it everywhere so get preload path should of course be used here as well if we now use all of this and actually run it then we should be able to already test it out inside of our def script so npm run def wait a second and now here's our app we can just head over here to view and toggle our developer tools and now we should see window. electron dot and you can already see that these two methods are here so let's just get the static data and we can see we conso locked static and now if we just subscribe statistics and for example add a call. console.log one then we can see this callback was called and our one was locked so essentially what we're doing here is we are telling our main window please run the script before you open the window so preload it and attach everything we want in the context bridge to our window under an keyword called electron and just allow us to use these specific things now you could of course still do some stupid stuff in here like saying okay I want everything in electron to be compatible so I just import all of electron here that would in itself of course be another security vulnerability depending on your app so it's best not to do that this is why we adding these functions that basically wrap some logic so we don't give too much power to the UI basically so what we're doing is basically just saying these two methods can be used by the UI they do this so please expose it to the window and then the UI can just use it in whatever way it pleases so we just check this out real quick using our UI right here head it to the window and just set something like window. electron dot what was it called get static data then you will first of all see that there are type issues here which we're just going to ignore for now so at TS ignore we'll get into that later because all of this process isn't type save in itself anyway so we aren't adding the types right now we're adding them later on but don't worry it won't take long now let's just reload this and we should see that our UI now automatically locks this value because it can just access it on the window and here we can see static was locked two times because we're running in react strict mode which of course runs everything twice but unfortunately if we now actually go ahead and build this using npm run disc with whatever operating system you choose then we're going to see that this won't work in exactly the way we want so let's just try it out real quick by building it and waiting for a second and we can now see that there's an error because nothing is rendering so let's just take a look at our developer tools real quick and we can see okay cannot read property of undefined reading get static data and that is because our preload script didn't actually run which you can see here unable to load preload script and that is the case because we didn't actually tell electron Builder to include this file remember we intentionally called the CTS so that it isn't bundled in the normal one file fits all solution basically because we need to import it as a separate file right here in our get preload path method basically and now we need to tell electron Builder to actually put that file into that position so that it can be imported at runtime and how do we do that well let's just head into electron builder. Json right here and here there is actually another flag or another category we can set called extra resources where we'll Define every file that that needs to be imported separately from our code so basically as a unique file this could be something like an image you want to include or also something like a preload script for example so let's just add the path to our preload script right here which will of course be dis electron prel load. CJs remember dis electron is the directory where all our compiled typescript JavaScript code will end up so basically all of these typescript files are converted to JavaScript inside of this electron and of course our preload do CS will be converted to a preload CJs right here which can then be used by electron Builder to actually give us the desired result so now if we just compile all of this down into a executable once again and wait for a few seconds then we can now see that everything is spinning up perfectly fine again and if we head back to our developer tools real quick then we can see static was locked and window. electron contains our two functions right here which is exactly what we want so we now actually got some communication between our main electron process and and our main window using both the death and the production build and now the only thing that's left to understand is of course to really go into why these dots are different right here in our production build and if we just take a look at our build using the terminal this time we just CD into the this directory and because I've created a Mac build we can actually dig really deep into the files so let's also do that by just doing LS and here is an Mac RM 64 directory that we're going to go into and this now contains a w so this is the actual executable you can just click on in your terminal so we just do open Dot you're going to see this thing is our electron course that we going to click on to open the app but because we're using the Mac build right now we can actually go deeper by actually CD into this little file right here so CD electron course so there and as you can see there's content in here and now we can just CD into the contents and this then will contain more stuff for example the Mec folder that will once again have the application in it and the resources folder which will actually contain all of our code in stuff so let's just go into resources right now and we'll just open this using the actual file explorer to have a better way of understanding what's going on here as you can see there are multiple file here there's a desktop icon there is an app. Asar file and there is also a disc electron folder somewhere in here and this disc electron folder now contains our preload CJs file and this is basically what we're doing when we're defining extra resources these resources will land directly in ins of the resources folder under the same folder structure there are in our project so basically when you're looking at the root directory we've now copied the disc electron folder but the only content we're covering is basically the preload do CJs so basically everything you put inside this extra resources bit will be put inside of the resource directory of your app okay so all of our extra resources land inside of this resources folder where we're basically already located when running locally because it basically mirrors our current file system in our app but where is our code actually running why do we need need to go up one directory well this is where it becomes kind of tricky and where also the reason comes in why we use a CJs file all of our bundle code will actually be located in this app. as and this is not a real directory it's basically a fake directory because this is kind of a bundle basically that will contain all of our code so we can't actually use file system reads for preload script for example to read stuff from inside of here but everything will basically be bundled in kind of a custom format and we can escape from this format by basically just going up One Directory so this isn't really a directory but for all codes sake we can treat it as one so our code is basically located inside of here and if you want to access anything outside of this directory we just need to go up one directory which is what is happening in a path resolver so basically when we're in Def mode we are accessing this this electron folder right here from our root of our app and if we are in production we'll basically leave the app. asrr using these two dots and then accessing this electron p. CJs inside of the Resources directory right here so now that all of this is clear let's get into finally sending some data over the IPC event bus which was the whole reason to set up this preload script so that we can finally display something inside of our UI let's go so to finally now let electron send our P data to a front and we first of all of course need to tell it which window should actually receive the data so this will of course be our main window which is of type browser window and of course this main window will need to be passed as a reference to whole resource because we are currently not in the scope where it exists and now instead of console loging our data we can say main window. web contents so everything that electron needs to interact with the actual window is inside the web contents and here we can now say send so send an event given a name the name of our event will be statistics and the payload of our event will of course just be this big piece of data that we're currently logging so let's just copy it over and now we are basically saying on an event bus called statistics we are sending this data every 0. 5 seconds and if the front end wants it can listen to it if it doesn't want to then that's absolutely fine we aren't expecting anyone to listen in case that nobody's there to listen because the window for example hasn't opened yet so now we can just save this head over to our main file because of course we need to pass the main window to a poll resource method now and after saving everything is fine so far we can now just head to our preload script and actually use the call back we defined before in a proper manner so now in here we can now say electron do IPC renderer so basically the UI part of the IPC protocol and here we of course want to add a listener so on receiving the statistics event which we'll just copy from over here real quick on receiving the statistics event we want to call the following function and to now get data there are actually two parameters the first one is the event you can basically use this event to get information about who published this event in our case this will always be the main process so we aren't really interested in the event right now but later on we will actually do some checks to make sure that events aren't coming from untrusted sources but not right now that's something for another time so we can just say the event isn't read that's important right now so we'll just call it underscore and then we'll just add our data and our data are actually the statistic ICS we're currently sending so what we can do now is actually just go ahead say call back and call or call back with the statistics so stats right here and now this should already be working so let's just head to our UI remove this window. getet static data bit right here and actually add a use effect in here to do the actual subscribing we are doing this in a use effect of course because given an empty dependency array the use effect will run only on the first render and not on every render and we of course don't want to subscribe a million times we only want to subscribe once so now in here we of course need again to add TS ignore all of this because again we didn't Define the types that will be the next step coming up and here we can now just say window. electron dot then we'll just copy subscribe statistics from over here and then we can just say this is our stats and and we'll just conso lock them again this isn't much different from what we did before but now we aren't logging them in the electron back end we actually logging them in the front end so let's just try that out real quick by running npm run def waiting a few seconds and then we should see that our UI is now popping up and if we head into the developer tools we can see our usages are already picked up and everything is working just fine so communication from the back end to the front end in basically a UDP style man so we're sending data and not really caring about if it's received is now working so now let's get into the other direction of saying okay I'm the front end I want to get the static data please respond to me so I expect you to answer so let's take a look at how to do that by actually heading into our preload script once again and now saying electron IC render do invoke now if we just leave this right here we can see that invoke resolves with those response from the main process so basically while send just sends data and doesn't really expect a response invoke is there to expect the response so here we can now just say I want to Define an event called get static data we actually don't need a second parameter because invoke will actually just return the value that the IPC main process returned as a promise so just defining what event we actually want to listen for is completely plenty so now to actually listen to this event we'll of course need to go back to to a main process say IPC Main and then we could do something like on for example but remember on and send are for more of a UDP type of communication so sending data and not really expecting anyone to listen for it what we actually need to do is to handle an event because the UI expects the back end to respond so it needs to handle it and now we of course need to handle the same event that's coming in so get static data and when this event comes in we will call another call back again here you can get the event and we will verify this exact event later on but not quite yet because without any types all of this is really not as helpful as it might be at a later point in time remember the types come next so don't worry and now we can basically just go ahead and say I want to return get static data which we already defined before remember it just returns our total storage CPU model and total memory in gigabytes and now if you just try this out as well by restarting our UI real quick and just calling this into the console because given that all of this is async it would be too much effort to just mock it real quick we can just run it inside of this console and everything is fine and now if we just run await window. electron doget static data we can see that after scrolling up a bit because all of this loads really quickly we get my total storage of 245 GB a CPU metal of an apple m 1 and my total memory of 8 GB so we can now request data in more of a synchronous fashion sure it's still async but we are expecting a response or we can just send data to the front end I don't really care if anyone actually gets it which is especially important because we might send data at a point in time where the front end hasn't loaded yet for example so expecting a response would of course be detrimental in that use case so these are the two types of communication that we are needing for our app right now so let's now actually make them type safe because all of the NES and T signor are of course not really best practice as you can imagine so let's optimize this now and to start off with our refactoring we'll first of all handle the window object so basically the type safety between our preload script and our UI and then we'll go over to the IPC process so to start off we'll first of all need to define a new file to save our Global types in we're going to call it types. DTS and this file will basically contain all types that we share between our front and and our back end because importing the same file from the front end and the back end can get really messy real quick so we'll just Define everything in here do be careful to not Define too much in here though as Global types do tend to be kind of an anti- pattern in many cases so just be careful here and to get started we first of all Define a type called statistics and that type will of course have all the fields from our statistics object so to do that we'll now just head over to our resource manager again and just check out what object we are putting in here and as you can see the object is just this right here and for now we can say we got a CPU usage that's a number we've got RAM usage that's also a number and of course we need to close the object so that all our errors disappear and we've got the storage usage which is also a number now let's do the same with a static data just heading back to the resource manager once again copying over this object and defining what it contains so our total storage is once again a number our CPU model is actually a string because it's the name of the CPU and then our total memory in gigabytes is of course also a number and now we can just save this and now we need to somehow add it to the window so to do that we'll just go ahead and say interface window and an awesome part about typescript right here is if we Define an interface that already exists like the window again then we basically add stuff to the existing interface so everything that's already part of the window will stay part of the window but everything we Define in here will be added to it so now we can just Define electron in here because as you might remember in our preload script we called our Bridge electron so our object will of course also need to be called electron and then we can just add these two methods we're defining here so our window will have a subscribe statistics method which will return void for now we will later on also add add an unsubscribed method as a return type but for now we'll just stick with that and of course we'll need the type from before again so this thing takes in a call back that will be called whenever the subscription gets updated and our callback will of course get the current statistics so the statistics have the type statistics right here and it just Returns the type void again and then once again we also copy over the get static data method right here and get static data will of course also be a function but this function actually expects a return value which of course is the static data but because we need to await the static data as you might remember we of course need a promise of static data right here and now we basically already defined our window so if we now just head into our app and try to remove this T signore we can see electron is still not defined well why is that we didn't actually tell typescript to use this types. D.S file yet so let's head into a TS config at the root level so the one for UI and inside of the compiler options we'll add a new field called types and types is basically just an array of files we want to include that contain Global types and in this case this will of course be do/ types. D.S remember a d. TS file is for defining Global types so something like defining what a module looks like for example that didn't have type definitions or for our use case just defining Global types and now if we head back to app we can see electron is now defined And subscribe statistics is also defined and now even our stats have to type statistics which is really really awesome and of course the same thing now goes for our other method the get static data method but of course this is only one side of the equation we also want the other side to be types safe as well so let's head to the T config of our electron project so remember Source electron T config tojson and in here inside of a compiler we'll do just the same so add Global types and of course this will be types and our types will be do do/ do do/ because we have two directories underneath our root directory and it will again be types. D dots okay so just so you know I did a little mistake right here this is actually something that vs code understands perfectly fine but the typescript compiler sometimes has issues with don't call this do/ types. D.S just call it do SL do/ types and the D.S will basically be inferred from typescript so um I've already implemented some function right here which you're going to see shortly later on but just so we know that it's actually working I'll just go ahead into the types. D.S right here please ignore all the other stuff right now and then I'll just say that the CPU usage is of type never so that we just see that it is still breaking stuff if the CPU usage isn't right and we can see type number is not assignable to type never so defining it like this will still work but it will also work while transpiring because without this change mpm run transpile electron would actually not work which is something I didn't realize because I seemingly didn't do a build so if we just rename this to D.S again then we're going to see that npm run transpal electron will run an exception but calling it types will not cause an exception and of course please don't forget to do the same thing inside of a UI right here where we also got do/ types. DTS which of course just needs to be/ types and now if we just save this and head to our preload script we can now say that this object that we are pending to our window will of course need to satisfy window and from the window we want the electron object and now everything is working just fine and we can even remove this any bit right here or even the complete type right here and our call back will still be statistics statistics because this is implied from the satisfies keyword down here if you don't know what satisfies does by the way it basically just tells typescript that we expect this object to just be of this type but instead of using as where you basically just overwrite the type you tell typescript this object needs to have this type and if it doesn't then please throw an error you could also Define a variable that has the Type window electron and then assign the value to that but because we aren't really using any variables right here we're just just passing this as a parameter satisfies is the right way to do things and now we've basically already achieved a type save communication between our preload script and a UI even though this invoke method for example isn't typ save yet so if I just went over to the invoke method real quick so in our main process we've got this get static data right here we could for now still just return an empty object and nothing would complain even though the code is of course false now so that will be a next step but what we did at least achieve is making all of this between the preload script and the UI typ save because if I now went ahead and just commented this out and returned something like null for example then will complain because null is not assignable to a promise of static data so we did improve our type safety quite a bit already so now let's actually make the IPC communication type safe as well using more Global types of course so that everything is just working as optimally as possible and so that we can just prevent as many stupid mistakes like confusing strings for numbers and whatever as possible but before now finally getting our IPC communication to be types safe let's take a look at what it currently looks like and what we're going to do about the issue that it has so right now we've got our electron site right here and we've got our preload UI so basically our pre-load script or our UI on the other side and in the middle we've got IPC which currently always uses any for any payloads we're sending over it independently of what event we actually using so what can we do about it well because IPC doesn't really allow us to well actually Define any types at least not easily we'll actually use adapters so basically what we're going to do is we're going to create adapters for sending data from one side to the other and for receiving data on the other side and of course also wice Versa sending data from the UI to the electron will of course always have adapters as well and then what we're basically going to do is we're going to say we only use APC inside these adapters these adapters will have full type safety from one side to the other and this way what we're doing is basically encapsulating the IPC process in a type save shell so that no any types are actually escaping anywhere inside of our app we are always having full type safety from one side of the adapter to the other and basically just ignoring the any types in between and the way we're going to do that is by basically defining a mapping of types so event one will have type one event two will have type two and then we're basically just defining a function for example we're going to call it send and it will basically just WP IPC renderer docent for an event name and a payload but because we are using generic types in this scent function we can then just basically go ahead and say for a given event name I will always have this payload because the event name is a key of this mapping right here and of course if you don't fully understand this yet then don't worry we're going to implement it really quickly right now and yeah it will probably make a lot more sense then and if not then yeah you're free to just rewind a bit watch this again and after having implemented it it will hopefully make a lot more sense so let's get into actually implementing this stuff and now start off our implementation we're first of all going to head into our types. D.S file so Global type definition and Define our event mapping that I've just talked about so I'm going to call this event payload mapping and what we''re going to have here is basically the key is going to be our event names so statistics and get static data and the values are going to be the types that are sent as a payload so of course for statistics that's going to be our previously defined statistics type and the same for the static data and what you can now imagine what we're going to do with this mapping is essentially automatically generate functions like this so we've got a function right here that says handle get static data so it's going to be a Handler for a get static data function that we already have remember it's basically the IPC main. handle for a get static data event which will then just get the static data from our resource loader and basically what we're going to say here is whenever we want to interact with our get static data method we essentially expecting a call back that's basically looking like this so we don't get any parameters and we return static data but of course we don't want to define a function like this every time even though this repper is already type safer than just using IPC main because it at least checks that everything is right if I were to return null right here then we would get an error because null is not of type static data but of course we want a generalized solution that works every time and actually acknowledges our type mapping so now let's just get rid of this and of course also undo these changes right here by just undoing this and now we're basically back to our normal situation with our type mapping intact and we can actually get into implementing our generalized solution let's head into a Ule file and actually Define a function so IPC handle will basically be a wrapper around IPC main. handle and IPC main. handle takes an an event name which we're just going to call Key and it will take in a call back and right now this call back returns something we don't know what it is yet so basically we aren't really interested in anything about it right now so let's actually pass through these little things so the key will of course be a string because our event name will of course always be some kind of string and we'll use the mapping shortly after we've basically implemented a basic repper and then we of course want the Handler so let's just call it Handler real quick and our Handler will basically need to return the event type mapping right so we aren't using this yet so let's just call it any now we of course also need to call the Handler so we can basically just say I want to return the return value of my Handler so now how do I actually make this type save well first of all we need to make this generic because whenever you don't know what type something will have until you've got a parameter so basically we will only know the return type of this function once we know what key we have we will basically always need generics for something like that so let's create a generic called key right now key is basically an any type so it complains because key is not assignable to type string it could be anything it could be a number it could be whatever to solve this now we can say key extends string and now we've basically done exactly the same thing as before because the key is now just any type of string but of course what we actually want to do is we want to say that the key can only be one of the strings in the keys of our event payop mapping so either statistics or get static data because those are the events we've defined and we don't want the user to actually use any events that he didn't Define before or basically we don't want any of us developers to do that because the user doesn't Define events anyway so now how we can actually solve this is instead of saying string we can say I want the key of my event payload mapping and what key of event payload mapping essentially does is if we just Define this right here type test equals key of event type payload mapping then this is essentially limiting my options of what types something can have to the left side of this object right here so statistics or get static data and this is basically now what I've limited my key to so if I now wanted to call IPC handle with something like get St data or statistics and that would work if I passed X and a call back then that won't work because X is not a key of event pay of mapping so now we've already limited it quite a bit but this is still any so what we can now do is basically say the return type of our Handler will be event payload mapping and from that we want the key so basically if I pass key from the event payop mapping so for example if I pass statistics then please get me the return value of event payload mapping statistics which is of course statistics object and now if we try this again so IPC handle get static data for example and now it expects me to pass in static data right here and of course an empty object is not a full static data object so it now complains so basically what we've done now is made IPC handle completely types save because it is forced to always use this mapping right here as long as we always use this wrapper and not IPC man. handle directly we can Ure that the Handler will always do the correct thing in the background and of course if we now do the same thing for the invoke function in the front end which we're going to do shortly then both sides are types safe and we can basically ignore the fact that IPC main. handle itself isn't because as long as we always use our reppers everything will be completely type safe which is exactly what we want so now let's just go into exporting this real quick head into a main.ts from electron and here we've got IPC main. handle right now and here we can essentially just go ahead and say IPC handle Quick Fix import and now this thing is completely type safe because it Returns the return value of get static data and if we just take a look get static data of course returns static data so the type safety is now complete and if we just do a little s it check right here by returning null for example then we can see that null is not assignable to static data so this part of our project is actually type save now so now let's do it for all the other parts as well and of course given that we already are on the electron side and not the front end side yet let's also do the other function we're going to need for our back end basically which is going to be sending data to the front end using the send function so web contents. send because remember when we want to send something to the browser we basically use the web contents all the browser window object to send the data and yeah I've just copied this function right now as you can see the key is the same so key is just the generic parameter then we need to reference the web contents that we want to send something to so basically which browser window do we actually want to send this data towards and of course the payload is again event payload mapping key so basically the exact same thing so if I wanted to use statistics as a key then the payload would of course also need to be be of the type statistics so that everything is working just fine and now just to make sure that our naming is coherent we just rename this into IPC main handle and this is IPC web contents sent just so we always know what's happening under the hood because this thing wraps IPC main. handle and this thing wraps web contents. send so now let's just head over to our resource manager where we are currently pulling our data because if you remember this thing runs main window. webc contents. send and now we can just replace this with IPC web contents sent and the second parameter will of course need to be main window. web contents and now we of course also need to import it from util and okay we are seeing some issues right here what's going on type unknown is not assignable to number okay that's interesting all right so the problem isn't actually that IPC web content sent is not type save the problem actually is that get CPU usage so a little wrapper that we wrote around OS u. CPU usage doesn't actually know what type it returns because right now it returns a promise unknown which is of course not what we want we actually want a promise number so what we can essentially just do right here is force the return type because we know what it is so let's just say this is a promised number and now all the types are safe we basically just wrap this little Library so this shouldn't really be an issue and if it is then we'll find out shortly so yeah we've basically now both made sending data and handling events on the electron side typ save so next step is of course our preload script so basically our UI to also make the other side typ save and then the full process between back end and front end will be completely types saved so that we don't have to worry about any of this anymore and we can essentially just add new events by adding them to the event payload mapping so now let's get into actually using this stuff on our front end and of course remember all of our front end electron stuff happens inside the preload script because we want to expose as little of electron as possible to the front end so basically in our preload script we can now Define more IPC reer functions these need to be defined inside of the preload script and not inside of the utils function because unfortunately because of the way that electron Builder handles all of this we can't import the util file inside of our pre-ot script because remember all of these extensions right here a CTS file will not be able to import the TS file and vice versa so we'll basically Define these functions in Here and Now what we can basically say is I want an IPC invoke function that once again takes in this generic key and it will return a promise with the payload mapping because remember invoke is async so it will need to return the promise of the mapping instead of just the normal type because every time we're using it we need to await the back end's response basically it's basically just like a fetch request we just also always async and then on the other side we of course got APC on which is basically used for polling or static data if you remember and here we've also got the key as a generic and then we're going to say our call back that basically gets executed every time the backend sends data will of course send a payload and that payload will have to type event payload mapping with the key so again for statistics that's going to be our statistics object and then we're basically just repping electron IPC renderer Doon with key we are basically discarding the event for now and then our payload which is normally any will just be passed to a typ save call back and now if we just use these up here so basically IPC rro on will now just be IPC on with the statistics object and the event right here will be removed just so everything becomes even more typ safe and of course get static data will also be IPC invoke get static data and now all of this is fully type save if I change anything about anything basically so for example if I now head into my type definition again so into the types. TS file and set this thing returns null right here then we can also already see the pre-o script becomes corrupted because promise static data is not assignable to promise null because in our window we Define what get static data should have as a type so basically this little types. TS file now is a contract between our front end and our back end that will always make sure that all our types are completely correct so now let's just fix this up again by saying this is static data and now all of our contracts work and as long as we never use IPC directly but only use the adapters that we've predefined everything should be fully type save from one side to the other and of course because we basically only wrote raer functions now everything should also still work just fine which is just awesome so now let's finally get into actually validating events so that nobody can send any malicious data which might even have wrong types which would be even worse so let's get into that but what data do we actually have to validate something well as I mentioned before we've got this event right here that's basically a parameter to any Handler or on function that we're running either on the back end or the front end but of course given that we basically only need to really validate on the back end right now we're only going to do it here and what our event actually has is something called event Dot and you can see the sender frame and from the cender frame we can basically get some information about what location somebody is currently in in their browser to basically verify that our code sent the event so basically what URL is currently opened in the electron process that is sending this event and as you might remember our current US L if we are in a production environment is basically this so our dis react folder SL index.html and of course we need to now verify that this is our current location that we're accessing the files from so to do this we are basically now going to go ahead and generalize this a bit by heading back into a path resolver that we've created previously and saying export function get UI path and here we're going to return path. join app . get app /d react index.html and now if we just head back into here we can of course use this over here and import it and of course we want to return this from get you IU so now we've basically generalized our uip path and we could now inside of a util function check that event. Center frame. URL is equal to get your ipuff right well not quite this is where it gets complicated Course once again we are using a different URL in a de environment than in our built environment because remember on development we actually talking against our V server instead of the local file so we basically need to differentiate here to make sure that it works both on the def environment and on the production environment and for that we'll just copy over some code that I've previously created and go over what it does because this will simplify the process by quite a bit and it is also reusable for this we'll need to first of all also import something which is the path to file function from URL so what do I actually do here well first of all I will check if we're in the def environment and if we're in the def environment then I'm basically checking that the url frame. url is under the domain Local Host 5123 which should be the port we've defined previously inside of a v config right here so this is the case and I'm basically wrapping the frame inside of a URL object and then getting the host attribute from it just so I can basically only get local host 5123 without the path without the um HTP bit in front just the domain which is basically all we need to test on development because if we running on this port then we can make sure that everything is just fine and then the next step is to say if we are not on dep so if we're basically in the production build then we need to check is our frame URL not path to file going to get into that shortly get UI path so basically we're getting our UI path once again remember this is pa.in app.get app this react index HTML and what we basically need to do here is say I need this URL globally so not like uh Slash dis react slash this slash that I need to actually get this as a file URL on my system with file colon SL SL Etc and I need to turn this into a string just so I can compare it to the string that is web frame main. URL and now the important part is if these two don't match up so if my request came from something that is not my UI file so not my index.html essentially then I need to throw a new error because this is a malicious event that's coming from some malicious site that was opened up in whatever way we might be able to imagine and of course this is not a foolproof solution uion if you have multiple files in your electron app then this will of course not work if you have for example a renderer in your electron app that renders different paths so basically a client side router then you will need to do something like checking if URL starts with this and then just ignore the rest of the path or do more complex checks right here this validated Ram frame method can become really complicated over time because yeah validation is always complicated right this is just a really simple way of validating by basically going ahead and saying okay is my frame URL something other than my index.html file that gets generated whenever I build my reactor and if so please throw an error and don't let the event through this is not a foolproof solution obviously but it is a starting point for you to basically build upon so that you know how to validate events and how to prevent somebody malicious from entering inside of your system which you probably don't want so to now actually use this we you can now just say I want to validate my event frame by going in here and saying before I actually handle this event I can just say validate event frame Event Center frame and because we've already generalized the IPC main handle function this is actually everything we need to do because now whenever we run IPC main handle the event will already be validated which is also why we're doing this now and not a few steps before because a few steps before we didn't actually generalize this so it would have been more stuff we need to copy over and now it's already generalized and every IPC main. handle function will always validate frames all right so now let's actually test this out and for debagging purposes I will just cons lock the frame URL just so you can actually understand what is happening right here so we'll open up the app go into developer tools and because we didn't actually call this in any part of our app yet we are just going to go window. electron dot and then we're going to get the static data and as you can see the URL is currently HTP Local Host 5123 and if we just scroll up to where I did my call we can see it's a pending promise because again every handle event is async and now let's just go ahead scroll all the way down again and await it just so we can make sure that we actually get the data that we want and we did actually make a little mistake because we of course need to return the value from our Handler and because I add added these parentheses inside of a call back we of course don't automatically return anymore so by just adding this return Handler function real quick we should now see that if I open it up once again and Target developer tools we should now see our static data right here so now let's also test that our validation is still working by changing this to 5125 so basically we're now checking that the URL is not the one that we actually want and we should see an error pop up if we now try to actually get this data and we can see error invoking remote method gets the data error malicious event so we're basically now limiting the amount of access we've got to our electron back end which is always nice for security purposes so now let's also test out the production build and as you can see the production build is also still working just fine because as I mentioned the URL is of course correct here as well so our event validation on a basic level is now working and you can now extend it to whatever level you want in case your app becomes more compc ated than mine currently is so now let's get to the final Eventing part for now which is going to be unsubscribing from events to prevent them from piling up on the front end and luckily unsubscribing is really simple to implement but why do we actually need it well if you've ever done something inside of user effect like for example a set interval or something then you might have realized that if you don't stop this interval then every rerenders for every change to the dependency array will create a new interval so in our case this isn't really an issue because our dependency array is empty right but let's say that we are calling this inside of a component that can get unmounted so basically when the component doesn't render anymore we of course also don't want to do any State updates because that might cause null pointers or just unexpected Behavior because when you update state of a component doesn't exist anymore that's really not something we are intending to do right so basically what we can do here is say when this use effect stops so when this component rerun or unmounts we want to unsubscribe from this Statistics thing right here and for that we will of course need to return some kind of a function so a common pattern is to do something like um const unsub equals window electron subscribe statistics and right now unsub has a type of void so we can't really use it so let's fix it up by first of all defining a type for that so type unsubcribe function and an unsubscribed function has a really simple type it's a function that doesn't take in any parameters and doesn't return anything so it's basically just a side effect function and what we're going to do is our subscribed statistics function will just need to return an unsubscribed function and now we can already see that our preload script now throws an error because we don't return anything so we'll return the return value from IPC on and IPC on also doesn't return anything so let's change that as well and and luckily electron IC render Doon also has another version of it which is called electron IPC render do off and this basically takes in the same parameters and as soon as the key is the same and the reference to the function is the same it will basically unsubscribe from a previous subscription but this means that we can't actually do this right now because these are two separate functions right we are creating a function up here and we're creating one more down here so we need to define the function top so our CB our call back is going to look like this this is going to be of the type electron IPC render event and our payload is going to have an any type because remember everything inside of our adapters is typed with any anyway so now we can basically say electron IPC render on is going to take in this callback and off is also going to take in this callback but we of course don't want to subscribe and immediately unsubscribe so we're going to turn this into a function that you can call to unsubscribe from our electron process and we're going to return this function from our IPC on function so basically what's happening now is if somebody calls IPC on then IPC on will return the unsubscribe function so another way we could write this is basically this right here return the parentheses return the return and now we basically directly return the result of IPC on which again is our unsubscribe function so now if we head back to our app we can see that unsub now is an unsubscribe function right here and we can just say when the user effect is done we're going to return unsub because if you return a function from the user effect then it will either run when the dependencies change or when the component unmounts so basically if it isn't rendered anymore and now we're basically already cleaning up after ourselves every time that we don't need the data or the event listen anymore so we're saving on unintended consequences of multiple event listeners and we're saving some memories so there won't be as many memory leaks only the other ones we might implement later on which we hopefully won't do all right so now we can finally get into developing our UI and as you can see I've already prepared a bit because we're going to use the library recharts which you're going to get by running npm install recharts so like this basically and because I don't want to go all that deep into setting up the graph we are seeing right here I'm just going to explain what it does and then as soon as we've gone over how recharts works and how we're going to use it we can then actually get into filling this chart with our actual data to then display the data in the UI in a really nice looking way and as you can see I have done something really simple right here just so you can see what I'm explaining we've got this base chart right here that I set up and we're just basically passing a few data points in there so 25 30 and 100 which you can see right here it's a really slight slope in the beginning and then it goes basically exponential when going to the value 100 so let's just take a little quick look at what I've set up here just so you understand what the code does and and then we'll move on so first of all we've got an area chart right here an area chart is basically a chart with a line that's got a filled-in area underneath it just so it looks as nice as possible then we pass our data in there that's basically just an array of objects that have a key called value this value is defined right here so if we were to rename this to something else then our data right here would also need to have another key but because I think value is a really nice key we're going to use that then we've got first of all this cartisian grid rate here and the cartisian grid is basically this little grid that you can see behind the chart so if I went ahead and for example said I want this in FFF then you can see that it now becomes a lot different a lot lighter but uh I think 3 through3 looks really nice so basically using this thing we can control what the grid looks like it's just for a bit of a visual kick basically we don't really need it but I think it's quite awesome then we Define what our actual area and line look like so so in this case we want the fill of the area to be slightly transparent so you can see the line behind it so it has a transparency of o.3 if we were to set this to one for example then we would see that you can't see anything behind it but I think .3 looks really nice then we Define the color of this uh background then we define the color of the line how thick the line should be what type of line it should be so should it be a straight line should it be smoothed out a bit all that stuff then we we again Define the data key that I've talked about before so just calling it value and returning of animations because I think the animations are a bit over the top with this library and for our use case we just really don't need it anyway because we're trying to recreate the Windows task manager in many ways which also isn't really animated anyways then we Define our x-axis which we're defining with a height of zero because we don't want it to be visible and they also defining a stroke to be transparent just so we also don't see any line at the bottom right here if you didn't set the height then even though we set everything as transparent it would still take up space which we also don't want so we basically just setting the height of the xaxis to zero and the width of the y axis and then we're also saying that it should start off at zero and go to maximum of 100 which is of course the case because we're displaying percentages right here so we can't have a utilization less than zero and we of course want our maximum utilization to be 100% which you can see really nicely here the line ends at the top because the highest value I passed through is 100 and the lowest value that we can display is of course zero and then we've basically got this area chart that contains all of this data and a responsive container which is basically just used so that our chart knows what size it should have and in this case we are just setting its width and height to 100% so that we can externally control the height using a div so this thing will only work when put inside a div so if we just remove this right here then it will behave quite quite weird because it doesn't know what size it should have so it always needs to be inside of a div with a fixed height making this responsive is really hard honestly so you will need to Tinker a bit if you want to use this library in a responsive manner but in our case that's not really the case so when we're actually using this chart we can just set some certain values for the height of the bounding box so basically the diff that this thing will be inside of and then we can just use it in any way or form that we like and now that we've gone over how thises grid will work let's actually now put our data from the electron backet into the grid so that we can finally get rid of this basic UI that we've always been looking at and actually get our updated values from the electron back end inside of this awesome UI and first of all of course remove this little bit I've created right here so that our app basically has no differences now whatsoever it's basically just the same as your app should be and the only difference we've got now are the base chart that I've created that we've just gone over and of course the installation of recharts using mpmi recharts so let's now get going by actually implementing our electron data into this grit and to do that we'll first of all need to actually now get our data inside of our react State because right now we're using a use to subscribe and unsubscribed from our data but we're only con logging it and not storing it anywhere so to do that we'll create a new file called use statistics. TS and in here we'll Now define a custom hook that will do this for us and we of of course export it so that we can use it elsewhere and for now we can basically copy over the use effect we already have and import that real quick and now we of course don't want to console.log this what we want to do instead is Define some state to house it so use State and a use state is actually going to be an array of Statistics because we of course don't just want to display the statistics at one point in time we want a range of uh data points that we're going to Define as a parameter to this hook so basically we're going to say data point count and that's going to be a number and what this will do is we'll basically say I want at Max this amount of data points to be displayed in my grid or in whatever use case I'm having and we'll take care of basically preserving this many data points and if we go over or under then we'll basically either need to pad the end so that the data points match up or if we go over then we of course need to remove the earlier bits so that the newest bits still only are at position 10 for example if you only want 10 data points so now we can say value and set value right here because this is a custom hook we don't really need an awesome name and we of course want to now return value so the return value of this thing is now a statistics array which we can also Define right here just to make sure that we are doing everything right inside of this hook and now instead of going console log right here we of course need to set value and we of course need the previous value to be able to update our current value so the simplest solution would of course be to now say we want all of the previous values plus the new stats like this but now we didn't really use the data point count because right now if we for example say we only want 10 data points and we already have 10 we would add 11 and 12 and 13 at the end and not really clear up after ourselves so the easy solution we're going to have for this is say const new data equals this and then we're going to return new data from our set function right here so we can now access a previous data inside of this callback and the return value will of course be the new data that is actually being set and what we can do in here is say if new data. length is bigger than our data point count then we'll just say new data do shift and if we just take a look at what shift does it removes the first element of an array so basically as soon as we get over the data point count we'll just remove the first items so the one that we got at the start of the app running and then when the next items comes in we'll basically remove the second one and so on and so on and so on so that at most we'll always have data point count of items and this is basically just the hook we're going to use for us so now we can basically get rid of this real quick and try it out by saying const statistics equals use statistics and now we can just add a data point count I'll just say we only want 10 values and we'll just conser log this so let's just go npm run def real quick and we should see that we're now logging this to our console so Tuggle developer tools and here we can see the array becomes longer every time the polling takes place and at 10 we're stopping because we're basically always replacing the first item with the second and so on and the last item will be our new one so now we've got an array that's basically portrays an amount of time that has passed so this is going to be really awesome for our visualization in our graph so now let's actually get this into the graph by also defining a new function for that in a new component so new file we're going to call this chart. TSX and it's of course going to build up on our base chart we of course need some props but those are going to come shortly and here we're just going to return a base chart of course and the base chart only has one property which is called Data so what we're going to need to do here is of course manipulate our data so that it fits and right now if we just take another another look at our statistics real quick which are right here we can see this is a variety of different numbers and let's say we want a chart that only visualizes our CPU usage so what we've basically got is going to be an array of numbers because we got an array of statistics for each statistic we're going to get the number of CPU usage and this way we'll basically have an array of numbers which each represent the CPU us usage so let's just get into our app and implement it real quick so that we can work with it so const CPU usages equals use memo to basically optimize on performance and this use memo is going to go ahead and say statistics. map and for each stat we of course want stat. CPU usage now let's just add the dependency array we want this to refresh whenever the statistics change and now we've got a number array so let's just try adding this chart already including a diff of course because as you saw before we actually need this diff with some kind of a height which I'm just going to do using inline Styles real quick we're going to fix it shortly and now we'll just add this little chart right here and now we need our props so export type chart props and we of course need data and our data is just going to be a number array so let's now put props do data in here and say our props out of type sh props so let's take a look type number is not assignable to type value number undefined okay so that's something we're going to need to work with but this conversion will take place inside of this chart so let's first of all now go ahead and say data equals CPU us and the next step is of course to convert our data so we need some kind of way to manipulate this and the easiest way is actually to go ahead and memorize this as well so const prepared data is equal to use memo because whenever we want to manipulate State into something else we basically use use memo and in here we can now say props do dat. map and we of course want each data point so point to be converted into an object that looks like this so value has the value of point and now we of course need the dependency array in here which is of course props do data so whenever our props the data change we want to update this and now we can pass the prepared data into a base chart and we should see that it's now working just fine so let's just npm run def once more and we should now see our chart popping up hopefully and as you can see we've got this data right here and it visualizes something but it's really really tiny and that's because our data is currently in a range between zero and one but our child is in a Range between 0 and 100 so the easiest way to solve this right now is to just go ahead and convert this into percentages by going Point * 100 then we'll just open up our electron app real quick by hitting npm runev again and now we should see our data points actually popping up on here and we can see there's a really slight wiggle in there because my CPU usage is of course changing because I'm running recording this app is taking place all that stuff and yeah that's actually working already so that's really nice but as you might have been able to see if we just restart the grid looks kind of awful in the beginning because we add one data point then two then three and it becomes wider which isn't really what we want so let's just go ahead and also add Max data points as another parameter to a chart and just say const points equals this and we of course need to return the points again and now we can say if these points don't have the right length so if points do length is less than props do Max data points then we can just append more data points in the end or another solution for that which is even simpler is actually just going ahead and saying I want to spread my points and then I want to spread an array do from and then we say the L of the array that I want to add to the end is actually props Max data points minus points length so basically we are creating an array at the end right here and the length of this new array that's basically just a whole lot of undefined is going to be the length that is missing from our points so if we want 10 points but we only have five then we add an array of another five in the end so we now got an array of length 10 but of course this thing will now complain because value number or undefined is not assignable to type unknown because it just doesn't know what type this thing has so we'll just map all of these new items and for each item we'll just go ahead and say value is undefined so now what we've basically done is gone ahead and created the missing amount of items for our array to have Max data points of length and set the value for this piece of data point is undefined now we of course also need to say props Max data points is part of our dependency array because it might change it probably shouldn't but it might and now we'll just go into our chart and say Max data points is of course 10 we'll restart our electron app and now we should see that it looks a lot better so the P first point pops up here and then all the other points go over flowing to the right and as soon as every point is there we have this really nice flowing graph which is exactly what we want and of course changing this up is really simple as well because I'll just do it in a crappy way if you for example want our RAM usage to show up we can now just go ahead replace this with RAM usage of course also rename some stuff and then we see our RAM usage popping up here which is basically pined 100% right now because I got a Mac with 8 GB of RAM I'm recording something I have electron open I have vs code open I also have some other stuff open in the background so yeah I'm basically always using the swap file so this thing is full when it comes to memory but uh you can see where I'm coming from and yeah we're now basically properly using our electron data inside of our front end which I think is really cool and it's even reusable so if we wanted another graph somewhere else we could just use the use statistics hook again to get the data and everything would be properly functional which is exactly what we want so this is of course a really big achievement already but there's something that you might have realized when debugging all of this especially because we still console logging stuff which is actually not really necessary so let's just get rid of this console lock real quick but if you did look into the console then you might have seen electron security warning insecure content security policy so this is going to be the next thing we're going to look at now because we of course don't want any security issues in our app and the security policy is actually something quite interesting that we can now look at to make our app even more secure against foreign entities trying to somehow inject data inside of our app again this is not really an issue with our kind of app but somebody like the developer of Discord of of course need to put a lot of effort into security so we'll of course still look into how to make everything as secure as possible and trust me insecure content security policy can be a big issue for many apps as soon as you have some kind of accss vulnerability so let's look into now actually solving this issue as good as we possibly can let's go so what exactly is this insecure content security policy well you can see that electron actually has their own section in the security section about content security policies and they recommend they' be enabled for any site loaded inside electron and that is because it's able to restrict what kind of data can be loaded by your app so if your index HTML file in our case restricts electron from only doing request to certain websites any crosy scripting vulnerability your site may have is limited by what it can send data to so let's say for example somebody malicious gets inside of the um UI of another user and they want to send some data about the user to their server well if you prohibit the UI from communicating with that server or loading scripts from that server then you're already quite a bit safer so let's just look at this little example right here here we've got somebody that basically just wants this message to be gone by basically saying my content security policy is everybody can do anything and here we've got one that is labeled as good which says my script source is self and htps apis example.com so basically anything on the domain I'm currently on so basically in our case the index.html or Local Host 5123 can do scripts so I can load scripts from these URLs and of course also this API directory because maybe there's a script we need as well but I couldn't do a script from let's say Google for example so I've prepared a little example right here as well which is how you would do this inside of an HTML file so we create a metatag and this metatag has an HTTP equip equivalent so this basically if this was an hgp header what would it be called and in this case we setting a Content security policy and the content is my default source so basically for any type of value I want to fetch so an image a script whatever I first of all only allow myself to be the source so I only accept images CSS scripts Etc from myself then I say okay Styles I also only accept for myself but I will allow the UI to use inline Styles so basically I will allow my JavaScript code to Define scals I will allow inline Styles in the HTML which is not by default activated for some security reasons but especially when doing something with react often times you will want unsafe inline to be enabled just so you have more options of using Java script in your code and then of course for scripts you can say something like self and maybe you embedded Google analytics then you would of course also want to enable scripts from analytics. google.com so that Google script text can also be loaded and then send some data about user to Google which in our case is of course not needed for debugging you can also set the HTTP equivalent to content security policy report only because this is of course really restrictive and if you already created your app and now try to set this header then some restrictions might get into effect that cause your site to misbehave like for example you didn't set unsafe inline in here and now your CSS doesn't load which would be really devastating well by setting your reports to report only all of the ISS issues will not be prohibited but you will get locks in the console about what policy restrictions actually would have taken effect if you were to enable them so basically what scripts did load that should have not been able to be loaded so for debagging purposes you can just add this report only look in the console and if anything is locked by this policy then you can know that your policy is too restrictive and that you need to change something about it so in our case let's just take this little metatag right here and actually add it to a app by heading inside of the index.html of course and now inside of our head we can just add this metatag of course we can just remove this analytics.google.com because we don't add any Google analytics here and now our app should still be working just fine and this warning should now not load anymore which is already quite the benefit you still see some other errors in here because um recharge didn't really do all the improvements we need for newer Rea versions that are coming up but I'm sure at some points they will so these arrrow are only a temporary thing so now let's try to actually break this by making a policy that's more restrictive for example we remove this unsafe inline bit right here so basically we now only allow CSS files that don't do any inline styling and we can see we definitely do need this inline styling because this basically just broke everything react uses inline styling for some of it stuff so we actually do need to enable this and now by doing that we do have of course see that everything is still working and the same thing of course goes if we for example said script source is not self but um nothing or maybe just google.com for example I don't know if you now try that then we can see no script is loading at all so yeah we are not fetching anything from google.com of course it's not loading we only want to accept scripts from ourself to be sure that no external militia scripts can be injected into our app and now I may need a few reloads I don't know let's just close the app and open it again and then we should see that everything is working properly and by now actually committing this meta attack right here we are now also safe about our content security policy which is another great step towards a more secure app even though as I already mentioned a few times for our app this isn't really as necessary but for many apps it is so let's just Ed it anyway so that if you try to build anything based on this tutorial you have a good starting point that you can build all of your up upon now we could theoretically at least already finish our app we've got a front end a back end that communicate in a types safe Manner and we also already got all the information that we need but of course that's not everything I want to show you I also want to show you features like hiding items to the system tray so basically when you close the window it doesn't just close the app it basically just pauses it and hides it in the system tray so we of course need to add an icon to the system trade to do that and before we are now going to start implementing this we're first of all going to need to go over some requirements for defining an icon so I've actually defined two icons right here and we're going to go into the windows one real quick so as you can see this is a colorful icon that I have just defined so that the contrast is really good on Windows and if you just look at its info real quick we can see it's 16x 16 pixels and that's not because I just thought I didn't really need much resolution that's a default icon size for electron so both of these icons are 16 x 16 pixels but of course you might want to have more resolution to create something like this V media player icon right here which of course does use more than 16 x 16 pixels and to do that you can actually add extensions to your icon name so this right here is my current icon name icon.png and if you just added at 2x to your icon name then electron would know okay this is not 16x 16 this is 32x 32 or Forex would be 64x 64 and this way you can basically Define bigger resolutions in your icons and still let electron know what resolution to actually use electron unfortunately doesn't just read the resolution of the image and scale it appropriately so if we were to use a really large icon in here then it would just overflow and basically get cut off at the tray so you'd have a really really awful looking icon because it would use it as if it were 32x 32 pixels and not scale anything which is of course not ideal so if you want to create a large icon please remember to just add the add 2x 4X 5x whatever to the end of your icon and you might have already seen that my other I icon which I'm going to use on Macos is called tray icon template and template is actually the same thing so you can add template to the end of your icon to turn it into a template image but this will only work on Macos and template images have a few other requirements they can only consist of black and Alpha Channel pixels so basically a PNG that has a transparent background if you just take a look at this then you can see the background of these pixels is transparent and all pixels are completely pure black so z r z and z g and yeah that's basically how you define a tray icon that's a template icon but what does it actually do well if you just take a look at these mecos icons you can see they are basically always black and white and that's because there's actually a build-in feature mecos that will change the color of your icons depending on the background color so right now I've got a really dark desktop background which means that this bar up here is black and therefore I of course want all my icons to be white so what Trey icon template will basically do is electron will replace all the black pixels with white ones if the background is dark so that there's a really good contrast and the same thing of course goes for a light background or a white background we basically still keeping all the black pixels so this icon right here from the VLC media player will turn black if I use the VLC media player on a light background and the same thing is something that we want of course as well so electron actually offers us this feature if you just call the icon template in the end and of course you can still combine this with all the other features for for DPI density so larger images by just calling it icon name template add to x.png and this way you can basically Define large icons that are still consistent on Mac OS because they change color if you need them to this is a lot of theory about just defining an icon but it will allow you to create a really good looking icon on any operating system so basically on Windows I'd always recommend using a bit of color to really have all the contrast that you need because electron just doesn't really offer any temp feature on Windows and on Mac you just use a template feature because it gives you a really coherent and good looking icon so now that we've gone over this Theory let's actually get into using my two icons in our electron app so that we can actually Implement a height to tray functionality let's go and of course first of all we need to now tell electron where our acid files so our tray icons actually are because electron Builder will need to know that it needs to include these files in the final app and I added these files on source assets and then I've got my two pgs right here so we can just head into the electron Builder adjacent file and here under our extra resources so basically everything that is not code we can now just go ahead and say I want to add Source SL assets SL star star so basically in a recursive way add every folder and every file that is under the assets directory into the final bundle and now we can already go ahead and actually Define a little function in our path resolver to actually access these asset files so let's just add this little function right here we've got this get asset path and we're of course joining the app path again on dev we stay in the directory we are on production we go up one directory as we are with everything else basically you can see the same pattern up here and then we're just going Source SL assets and now we can actually already use this in our main.ts to now create the tray so we're just going to create a new tray which we can import from electron and this tray actually just takes one parameter which is the tray icon so now we can just say get asset path and we of course need to append what path we want so we need another path. join to make it work crossplatform and here we can now just join tray icon.png we're going to implement the differentiation between Mac and windows shortly but for now let's just try this out so that you can see if the colorful tray icon now pops up on the top right here and we can now actually see that the icon is popping up up here but of course we are now using the colorful icon that we want to use on Windows and Linux and not the black and white one so the template icon for Mac OS so let's change that up so that it is coherent with all the other icons we've got there as well and to do that we actually just need to do one simple little thing which is to check if process. platform is equal to darwind so basically when we're on Mac OS I want to display one icon and when we're on Windows or or Linux or basically whatever other platform there is then I want to use the other one so if we are on dvin so meos I want to use the Trey icon template.png and if I'm on Windows then I want to use Trey icon. PNG so now let's just restart real quick and we should now see the correct icon popping up up here which it actually does so we've got that handled and that's actually a great achievement already right we can now actually control which icon we want to show so let's just switch that real quick just to make sure that everything is also working the other way around so if I were to say that only Windows has the black and white icon then we can now see the colorful one absolutely perfect and of course we also want to test the proper template functionality so let me just npm run def again and open up the system settings to now change my desktop background to something light so we can also see that the template icons react responsively to the changing of the background color so as you can see right now I've got this space wallpaper so something that's basically as dark as possible close to the top so let's just switch to a solid color like this one and now we can see that the icons all become black because the background is light so that's basically the purpose of using template icons because they respond to changes right here and if I now had an icon that was only white for example then it would of course look terrible on this really light background so what do we do now well right now we've got a tray and we of course want a hide tray functionality and for that to work we need to somehow enable electron to say when I close all windows I don't want to stop the whole app I want to still have the try icon and be able to basically click on it to open the app back up and right now if I just close this window right here you're going to see the tray icon also disappears because the whole electron process stops so our next step before actually implementing more of the tray will now be to basically go into how electron handles closing windows to better understand how we can basically Implement a hro tray functionality and of course the easiest way to first of all try to implement this hyr tray functionality is basically to tell electron to not do its default behavior when all windows are closed so basically when I close the window please don't stop the app and to do that we'll first of all try to go ahead and say main window do on and I want to listen to the close event and I want to say when I close the app I will basically get an event and then we say event. prevent default so basically when you get the event to close the window just prevent your default behavior and do nothing and even though this sounds really great if we just try it out we're going to see it doesn't really work way we expected to because now I actually can't close the app at all and it's not even just that we actually can't quit the app using quit electron curse up here either because that will actually go ahead try to close all windows and then quit the app and if it can't close all windows then electron will just give up so right now we can only close it using contr C in the terminal right here so what events will we actually need to use here well it's a bit complicated so let's get into some more Theory right here and to Now understand this we first of all need to get into how electron knows when to actually close and there are multiple ways to close an electron app first of all the one we want to prevent which is closing all windows if we close all windows and didn't Implement anything then electron will just quit then there's of course a way of calling app. quit which will first of all try to quit the app then close all windows and when that was successful then the app will actually terminate which is basically what we saw when we hit close electron codes and lastly there's the automatic way so there are features an electron like for example an auto updator that will basically when you start the app check is there an update and if there is then it will directly close the app install the update and restart the app this is not going to be something we're going to tackle in this course but it is of course a feature we need to keep in mind so that we don't break it and functionally it's basically the same as app. quit because if you just take a look at the event order then you're going to see that depending on what reason electron uses to close itself the event order will be different so if we close all windows then first of all we of course going to get the event that the windows closed then a before quit event will be triggered which basically tells us that electron is about to quit if everything is successful and then the app will actually stop but if way two or three happens so basically if the app is quit directly or automatically then the before quit event will happen first then electron will try to clean up everything by basically closing all windows and if that is successful then the app will stop so basically what we need to do is check did the before quit event happen before my main window. close event happened and if so then actually close the window and stop the app but if my window. close event happened first and then the before quit event happens then I don't want to quit the app because then the before quit event only happened because I did close the window so basically check if the before quit event happened first and if so close the app if not then prevent the app from close and that's going to be exactly what we're going to implement now so that you can actually understand what I'm talking about here and so that we can get into a simple but still a bit complex way of how electron handles event and how event order can change depending on how the event was created so let's get into it and to do all of this cleanly we're first of all going to now Define a function which is going to be called handle close events which is going to need our main window which is a browser window and we're going actually going to do all of this inside of this function just so it's a bit cleaner and of course to now persist what EV happened at what point in time we also need some kind of a variable to do all of this and we're just going to call it let will close equals false because by default we don't want the app to close before we now move on let's actually just use this app right here and call this function using our main window and actually implement the hi to tray functionality in a way where we actually hide the window because right now if you just try this out again then we're going to see that the window doesn't close but it also doesn't get hit hidden away or whatever so to do that we're actually going to call one more function which is main window. height and Main window. height will actually do everything we need on Windows and Linux but not quite everything on Mac OS so let's try this out using npm Runde and if we now do this and click on the X then you're going to see the windows gone the try icon is still there so the app is still running and the task bar item is also still there which we probably don't want to happen remember this only happens on Mac OS on Windows and Linux you probably don't see a task bar icon and that is because there actually is a variable on the app that you only set on Mac OS so we can now say if app. doc so basically if there's a doc property on our app that is basically responsible for handling the Mac OS doc then we want to call app. do. hiide as well we basically just need to do this if check to make sure that height actually exists so that we don't get any null pointers and if we now try this out once again using npm run def then we can now see that our hiding functionality is actually working so let's click onto this then we can see there is no task bem for electron anymore but the tray icon is still there so now that we've got that properly set up let's actually get into our will close functionality and of course the easiest thing we can do for now is say okay if I do actually want my app to close then all of the functionality we just built is basically irrelevant so we'll basically just add a return statement early before any of this happens so basically we don't PR anything we don't hide anything we just let electron do its thing if will close is true so now when do we actually set will close to true and when do we set it to false well we talked about this before quit event previously so let's just add that there's actually an event on the app because well it runs before the app quits and it doesn't really have anything to do with the window directly so here we can now say when before quit is run we just say will close is equal to true so basically if close happens first and before quit happens after that then will close will be false for this if Clause the default will be prevented and will Clause will just be said to True afterwards but nobody listens for it so this is fine and of course if before quit ran first then this will be true the default will not be prevented and electron will just do its thing and close the app so everything is fine here but let's just say we hide the window and then we show it again well now will close is actually true and independently of how I close the window the app will close so that's not something we want so we also need to reset this value when our window gets shown again so basically when we click on the tray icon and tell it to open our window back up and there's also a function for that which is called main winner. onow so basically every time we want to show our main winner we just reset this variable by saying will close is equal to false so now let's just spry this out right let's just hit npm run def right here and the easiest thing we can do now is actually just go ahead hit on electron right here and say quit electron CES so basically directly calling app. quit and you can see the trade icon is gone the app is gone so we actually directly quit the app which is the same as the auto updat doing this or us directly calling app. quit so now let's try the opposite by now closing our window and seeing that the app doesn't quit so I'll just click this right here we can see the tray icon is still there there's no dock icon because we called app. do. height and now we can of course just not close the app in any way or form whatsoever because we didn't implement this yet so the next step will of course be to now Implement that so that our Hy to tra functionality actually still allows us to close the app and of course also to open the window back up again because right now it's just lost honestly and to get started we're first of all going to take our trade right here and move it into a separate file just so all of our concerns are a bit more separated so to do that let's first of all create a file called tray. TS and create a function in here called export function create tray this will need a reference to our window which we're going to use later on so let's do that already main window browser window just so we always have access to everything because remember we need to show our window when we click on the tray so of course we need to reference it somehow and now we can just take this new tray method right here and put it inside of here we'll fix that up shortly but first of all we're going to now create tray instead of a main function again just where we created a tray before pass a main window and then we can just import everything we need in here and now let's just hit mpm run Def and we should see that our tray is now still popping up but it is put into a separate file for convenience s so now we can actually reference of tray by saying conray equals the new tray and then we can say tray. set context menu so basically the menu that pops up when you click on the tray and this context menu will now take menu which is another import do build from template and in here we now put an array of options that we want to be clickable so to get started we'll just add one option with a label called quit and when we click so the next parameter is called click we will call this callback which just runs app. quit so now we do of course need to import app in here which isn't currently imported so let's quick fix that remove the semicolon and now we should already be able to close the app from the context menu so let's just try that out real quick by hitting mpm run def again waiting a few seconds and now we can see that there is a quit option right here and if we hit that then our app closes because of the handle close functionality we implemented before let's try the other way around as well where we now close our window first so that we basically minimize it to the tray and if we now hit hit quit then we just see the context menu disappear and our app is of course still closed perfect so of course we also need a show functionality now because we also want to be able to show our window again when we hit it to the tray so to do that we'll just add another label right here called show in a new object add the comma so that everything is still working and then for our click we're going to do multiple things first of all we're going to need to do main window. show and if we do that then we should already have some something working on Windows and on Linux so let's just try that out real quick and we should see that I can now hide it to tray everything is also gone down here and now if I hit show we can see the window is back but our dock icon is still gone which we probably don't want so for MEC OS functionality we of course also need to say if app. dock so if we are an assistant that supports the dog then we of course want to say app. do. show as well so that that will also pop up again now let's just hit npm run Def One Last Time we'll hide it we'll show it again now we see that the item is in the dock again and we can also quit the app by clicking right on here so this is basically the whole tray functionality we're going to need the most important part is of course the functionality of when to close the app when not to close the app actually setting the context items is pretty simple and there are even more things you can do with context like you can add sub menus and stuff by setting a type basically and this type has multiple options you could add a checkbox in here that you could read out of you could say it's just normal so basically the thing we've got right now a button basically you could turn it into a radio button where you can read the state out of it you could just add a separator for the app to look nicer or you could also add a submenu so basically you would have a menu inside of your context menu to add more options under a specific headline for example let's say you have a functionality that switches views you might have switch view as a headline and then all the views underneath it but of course for our use case we are going to do that in a different way of implementation so we just need a show and a quit functionality which we've now implemented what we are going to do now is actually stay with this menu. build from template functionality because we're actually going to create a custom menu for it so basically that's going to be what you see up here on the left on Mac OS or directly under your frame where you also close the app on windows so let's take a look at how we can actually create custom menu items for that as well and luckily creating a custom menu is actually quite similar to creating a tray menu so to get started we're first of all going to move this into a new file again so menu. TS and this is going to export a function called create menu which for now is not going to take in any parameters and here we can now call menu from electron once again do set application menu so basically the menu of our whole application and not of a specific window and in here we can now just like with the other menu just say built from template and pass the array of things we want inside of it but here we're now going to get to the big difference between the application menu and the tray menu because if we just compare vs code for example we can see we've got a lot of different menus right here with sub menus so basically for a tray we basically just defined one layer of items and that's it and here we're going to always need a menu first of all that will contain a submenu because otherwise our actions will not work properly so to get started we'll first of all create an object with a lay called app so label right here is going to be app and the type is going to be submenu and now we can just Define a submenu in here like this and the submenu is basically the same as a normal menu so now we can add options in here like for example saying I want a label called quit and then we'll basically do the same thing as before so if we click on it then we're just going to call app. quit like this now let's just import the app real quick by importing it from electron and now we should be able to actually add this thing to electron and see that it is working so let's try this out by actually calling create menu right here without any parameters for now and hitting npm run def once again and then we should see that our menu is now popping up on the top left and we can see it's actually called electron and it's not called app but you will not see this if you are testing this on Linux or Windows but let's first of all take a look at if it works so let's just hit quit right here and we can see it does work so why does it behave differently on Mac OS well Mac OS has a default behavior of basically limiting you to your first item always having the name of your app that's just default behavior and we can only get around this by using some hacky work arounds for example if you really really didn't like this then you could go ahead and basically just add another item in the front right here with a label called um placeholder for example or just an empty space I don't really care and then setting the visible property which we haven't really used before to false so basically this is going to be an empty item that we always going to hide if we now just reload this real quick then we're going to see that this first item is disappearing so the one that was called electron previously and we now only see the app so this is a workaround that you can use but I really don't recommend it because it can break in future updates for example you basically add custom behavior for one platform that isn't really told to be for that platform so you can't really see that from the code I really wouldn't recommend it and as I said it's default behavior for the first item to have a different name so a much better way would be something like process. platform equals Darwin question mark then don't set a name at all and just leave meos to set the name itself and otherwise call it app so on Windows and Linux now going to be called app and on Darwin it's just going to take the default name and independently of any electron or Macos updates you will always have the default mecos behavior in here and electron won't overwrite anything so this is this is actually the way that I'd recommend of handling this especially because you will probably add some basic functionality like quitting the app in here anyway so that's an awesome feature to just add to the initial option and then you can just add a second option right here which you might call something like view for example where you can then be able to change the view so we could say something like I have a view for the CPU we'll just remove the click for now because the functionality will actually be implemented afterwards then I'll have one for the RAM and of course one for for the storage right here and now if we just retry this again then we can see that we now got two items in here one of which being electron if I just get it to work properly sometimes the highlighting doesn't really work and you don't get the menu That's Just electron things basically behaving not the way they should but just the way they do and now we can see we've got electron up here and then we've got our view with CPU RAM and storage and of course the quid functionality on our electron bit still works but now what we've done is we've actually gotten rid of the functionality to open up the death to which we were able to do previously so let's also Implement that again but also in a slightly different way because you probably don't want your production use to be able to access them so we are going to implement a custom functionality where those def tools are only accessible in Def mode so on click we will now need the main window so main window is of type browser window and we'll just pass it through real quick so main window and then on click we can now just call a function that runs main window do web contents Dot and then we should have something like open def tools and if we now call this then our def tools should open up in the main window and we should be able to still access everything the way we did previously so let's just try this out real quick and then we should see that everything is still working properly let's just hit show here again and now we're going to call def tools right here and our def tools are going to open up but of course we also need the visible feature which I already showed you and we just say this is only visible in Def mode so now if I just try to build my app and run it real quick then we're going to see that this is now called electron course because as long as we're not in Death Mode this is going to be the actual name of our app while in Def mode it's just going to be called electron at least on mecos and we can see that we now only got the quit functionality while running in Def will allow us to actually see the def tools and access them which is probably what you want because you might not want your users to just Tinker around in your app as much as possible so enabling the def tools only in Def mode does make sense of course but I did of course promise you to also show you how to disable the menu completely so let's just do that real quick as well by ni building this create menu bit right here and then you need to do this following bit outside of the app.on ready function because for some reason it will only really work if you do it before the app is initialized afterwards you will have to do some more tinkering to actually disable the menu because electron will already have initialized it and the setting it to null bit will not work properly so I'll outside of app.on ready we'll just call menu do set application menu again and just set it to now and if we now try this out hitting npm run def again then we're going to see that our menu is now reduced to the absolute minimum so on Mac OS that will be basically just our quit bit right here so you can still close the app but nothing else and on Windows or Linux you will basically not have an application menu at all which is also probably what you want so that's just a little workaround for basically disabling this menu but in most cases you might at least want some basic functionality in the menu so I wouldn't really recommend completely disabling it anyway especially because you probably at least want your def tools to work in Def mode so you might at least want to call this conditionally or something but anyway this is essentially how you can set a custom application menu we are now going to comment this in down here again of course and then our next step after importing again is of course going to be to now actually add the functionality for our CPU RAM and storage view change again but this will need some IPC Comm communication so this is going to be our next step to Now set up a communication to basically tell the front end to switch between the CPU stats the ram stats and the storage stats in the basic UI and of course because we've already set up a lot of IPC helpers this is actually quite easy to do so we're going to define a click functionality for a CPU real quick and here we can now just go ahead and say IPC web contents send in here we're now going to need to Define an event so let's just get into our types real quick and Define our new event as well so first of all we need to define a payload this is just going to be a type called View and view will be CPU or Ram or storage just like in any other location where we're using these values and then we can just Define a new event so change view is going to be our event name and of course the payload will be a view and now we'll just add it to the window by basically allowing the frontend to subscribe to the change view event so subscribe change view could be a good name then the type is of course going to be view the parameter is going to be called View and it will of course just like any other function return an unsubscribe function all right now we can see our menu right here we can now Define what event we want we of course want to change view event then we need to reference our web contents our main window is already parameter here so we can just pass that through and then our payload will of course be the word CPU now let's just do the same thing for Ram and storage as well by just copying over these names at the end right here and now we are already sending the events but of course we also need to receive them and if you just take a little look at our preload script we should see that it now throws an error because we didn't subscribe to the change view event in here so let's just do that real quick by hitting this and saying subscribe change View and then we of course need to listen to change view in here and now all the types are complete so we can just hit into our front end and actually try to subscribe right here app. TSX and in here we'll just add a little use effect and in here we can now call window. electron do subscribe change View and here we'll just add a call back that for now console. logs The View so let's just do that real quick calling this over here View and of course adding our dependency array which doesn't contain any dependencies we just return the return value of subscribe change view which of course is the unsubscribe function itself so the cleanup will be done automatically this way and now if you just try this out we should see that when we click on change view something should be locked to our console so let's just try that out real quick and now we just hit storage over here and we can see our value is being received on the front end so now let's actually Implement everything we need so that our frontend will actually update the current view so that we no longer see the CPU statistics but for example the storage or Ram so let's just copy all of this a few times call this RAM usage and and the variable is going to be RAM usages and then storage usage and storage usages so what we're going to do now is Define a u State const active view set active view equals use State and in here we can now just say this is of type view and the initial value will be CPU so when you open up the UI it's always going to be on the CPU and now we'll just add a use memo con active usages is equal to use memo for optimization sake and then we'll actually Implement our memo and of course we need a dependency array and in here we're now just going to implement a switch case for our different values of active View and now we can just say in the case that it's CPU I want to return CPU usages and now we'll just copy this for all the other cases and we need to add all of these to the dependency array so they update nicely as well so now let's just add all of this in here and then we should be able to actually just pass our active usages in here to our chart and if we now just restart real quick by hitting npm run def once again we should now see that we're able to switch our views just like we'd expect ourselves to be able to and I did of course forget to update my user effect here because when the view changes we of course now want to set the active view instead of just console logging it so let's just try again by restarting our app and now we should be able to see that our view will update over here so let's just fix this up by actually seeing the correct menu up here and then we'll just click on RAM and we see that this thing is updating real nice so next up after now going into two native features so the tray and the menu we actually go away a bit from native features because we'll now basically remove the whole frame from our window so that we can have a window that see through or just a custom window that has a custom frame like vs code for example which does doesn't use the default Mac frame because we've got the search bar here so let's just play around with this bit right now and creating custom window frames is actually an awesome way of showing how electron sometimes uses really tiny features to basically still enable full functionality whilst customizing the app as much as possible because we do of course need our window to still be dragable for example even though we removed the normal window frame from the system and implemented our own one but before we get into that let's first of all actually get to removing our frame so if you just first of all hit our app up with npm run def then we're going to see we have this default frame around here right on Mac OS we have these items on the left on Windows we have them on the right we've got a title all that and we're basically going to get rid of all of it do remember though that on Windows and Linux this will most of the time also get rid of the menu so if you do want the menu then you either need to reimplement it completely yourself or you will need to use the default frame unfortunately but anyway we will now get into actually removing the frame which is really simple when in thisiz our window so the main window creation basically we can pass a parameter called frame and just set it to false so that is a really simple way of actually completely disabling the frame but now we are really limited we can't move this window we can still resize it but we can't close it we can basically not do anything at all so we can still quit the app of course so that's nice but we do need to now create our own frame so that we're still able to interact with the window at all so to do that we'll actually head into to our UI right now and I will copy over some code because yeah just defining some CSS isn't really as interesting we will go over what it does though first of all we will just add a header into our app that will contain three buttons one to close one to minimize and one to maximize just like with any other normal app and this will be inside of a header just so you can easily style it and because it's basically the header of the app right the menu bar or the frame is essentially the header and then we can basically just head into our CSS right here and now style it you could now remove all of the default CSS we're going to do that later on though when we actually try to make our UI look pretty for now we'll basically just add some more CSS on top of here and clean it up later so what we're going to do is basically first of all style or header so basically the header is going to be positioned at the top because all of the default styling really shouldn't apply we don't want to center it or whatever that would be really unnecessary right now so we position it absolutely on the top right at the edge of the screen with a full width we'll give it some padding and set its box size into border box just so it doesn't overflow the screen then we'll text the liner to the left so that all the buttons are left centered because I'm on a Mac if you wanted to build for Windows and you might want to text the line to the right and we're going to set a nice looking background color so let's just try this out real quick by hitting npm run Def and you're going to see what I'm really doing here so you can see I've basically now got a dark bar right here with a few buttons inside of it and of course we also want to style these buttons now so let me just copy over some more stuff because this is actually first of all going to need to style our buttons all at once so basically every button is first of all going to remove all the default styling it's going to be round it's going to have a width and height of one ram and a margin so that I spaced a bit and then I have basically just colored all the buttons with nice looking colors similar to the Mac OS colors just so they can be differentiated if we just take another look we can now see here are our buttons and yeah yeah if you click them right now then nothing happens because we didn't add any on click functionality but anyway these are now at least here and we can then later go ahead and actually implement this functionality but for now the interesting part now will be how do we actually tell electron that this is our header and we want to drag the app when we drag this header well this is actually really simple as well we just need one property which is the webkit app region if we just add this to our header right here so that webkit app region is drag then we can now drag our app using the header so this now enables us to still drag and drop the app and if you now say for example that you don't want to be able to drag on these buttons because well you want to press them and not accidentally start dragging the window when you actually want to close it you can just copy this over add it to your buttons and just say the webkit app region for this is no drag so basically now we can't drag on the buttons so we will just click them but if we drag on this bar then it is still possible to drag the whole window and this is basically what I mean when I say that electron will enable you to still have a few really tiny UI features that are electron specific to basically give you the full functionality of a normal app even when you try to override some of the default electron behaviors like for example hiding the frame and also there's one more piece of information that you might want to know about because if we just try to enable our frame Now by setting frame to true then you will realize that electron will actually take some power away from you because now we can only drag the window on top right here and our webkit app region will actually not work anymore so that's just something to keep in mind you can't really use this in any window that does have a frame now let's get into actually enabling IPC communication so that these buttons will also work and the interesting part about this event is actually that we now send data from the front end to the back end without actually expecting anything return we basically just let the front end tell the back end hey I want to maximize minimize or close the window and you just do your thing I don't care if you respond so before we now actually implement this we of course first of all need a new type in our mapping right here which is just going to be a Frame Window action so either close maximize or minimize and then we need this in our payload map so here we can now say send frame action and our frame action will just be a Frame Window action and of course we also need something on our window right here which is just going to be called send frame action and that's just going to be function that sense a payload in the form of a Frame Window action and now we of course need to implement all of the bits to actually make this work so our preload script is already read because we didn't implement the sent method yet so let's just do that real quick sent frame action send frame action takes in a payload and then it needs to do something with it which we didn't Implement yet because we need some way to actually run a send event from the front end because right now we can only invoke and on in the frontend and not send so let's just copy the IPC on real quick we don't need a call back we just need a payload right here so we can get rid of this bit call this IPC send all of the other stuff with the generics of course stays the same then we don't need a call back we don't need a return statement we can now just go ahead and say IPC r. send key with a payload and now we've basically already implemented a type save send repper and now we can just go up here where we right now aren't doing any anything with our payload and then we can just say IPC send with our send frame action and of course the payload and now everything is typed properly because of a type file which already says that this is a Frame Window action so now let's actually already go into our app right here and give all of these buttons the proper actions so on click I want my close button to of course go ahead and say window. electron do send frame action and here I want to pass close and of course want to do the same thing for the other buttons as well and now the front is actually already done so we just need a listener on the back end to actually now Implement our functionality and for that we'll just head back to our main.ts file and where we're currently doing IPC main handle we can now do IPC main on which doesn't exist yet so let's Implement IPC main handle will just be copied over here and now we'll just call this IPC main on and we'll of course need to implement everything so we will not return anything from here so it will return void and we will need a payload in our Handler which is of course of type event payload mapping key because we of course need to pass through our value and now we will just call this Handler with our payload we will of course still validate the frame just so we don't do anything that we don't really want to do so that the sources are always correct and now we can just go ahead and say the Handler is forwarding the payload the event is already right we can just rename this to on instead of handle and now we've got an IPC Main on listener R here and now we can just import the new method and actually add all the missing parameters so first of all we got the send frame action and then we need a listener with the payload and here we can now just really easily go ahead do a switch case for our payload and just execute different commands depending on what payload we got so let's say our first case is close in this case that's the easiest one we can just call Main window. close this will then trigger all of our close logic down here so it's basically the same thing as if you were to click on the actual close icon on the window if it were to exist without a frame and then we can just break next up we need to do the same thing for maximize which will actually call really similar code it's going to be main window. maximize and the same thing of course goes for the minimize function which will also just call Main window. minimize and now we can basically this out and we should see that all of these buttons should now do what they expected to do so the minimize button minimizes something the maximize button maximizes and the close button closes it without actually quitting the app because it of course runs into a custom handle close events function down here which is basically exactly what we want so now we've got a few different IPC events some sending data from the front end to the back end some going the other way around and some doing an actual full communication from front end to back end and back so these are basically the main versions of communicating electron which we've now all handled and as you can see electron actually offers a lot of custom functionalities we can create a tray we can create custom menus we can get rid of the default functionality at all basically by just removing the default Mac OS windows or Linux frame and just building our own we can actually really tap into a lot of functionalities and we didn't really go into nearly all of them you could of course also use some system default uploading or downloading functionality for files Etc all of these features are of course really well documented in the electron docs and we don't really have the time to go through all of them because that would take forever and of course new features are added regularly so what we're going to do now is actually go away from looking into all the specific features of electron and go into how you can actually test an electron app because that's not as simple as you might think it is so let's look into that but of course before we can actually get into writing our test we first of all need to take a look at what different types of test we can actually write and of course for that we first of all got unit tests component tests and end to end test these are the main three types of front end or full stack Code test and of course we can now go through all of them and check if they makx sense for our app so first of all we got end to endend test so basically completely starting up the app and running test inside of it this is basically the main way we are going to focus on writing tests because we of course want to test the integration of a UI inside of electron and also test some of electron's features so having a full endtoend testability basically that allows us to really test all the electron features in combination with the UI is of course the best for this course and also for many functionalities that involve electron and two of the main libraries you could use for ENT test are Cyprus and playright Cyprus though doesn't really offer a functionality for electron embeds right now so basically for really opening up an electron app and maybe even showing multiple windows play R though does actually allow you to start everything up in an electron context where you can actually open up the window and do some special electron site code actually so we're going to use play Red here then we've got component tests as you can see these can also be written in Cyprus or playright or with testing library in a unit test kind of way we actually not going to focus on these for one because um when you're using Cypress or playright the code is actually basically the same that you're writing you just mention that you want to render component and also as the name suggests you're basically mainly testing front and code and as we want to test our electron code right here this really doesn't make sense for our situation even though you could of course use it if you build a full app with complex components that you want to test and lastly we've got unit test which you could write in just v test mocka whatever and these can be used on the front end or the back end we of course going to focus on the back end especially on features that we can't test in entest because our playright into anest library for electron doesn't support all the features that electron offers like for example our tray so these are basically the decisions we're making we're using v test for unit tests for features that can't be tested using n end tests we using NN test for General functionality of our app and we are not using component tests because those are primarily used for complex UI features which we don't really have and don't want to test r so let's start off by now writing a little end to end test to really get started and finding out how all of this really works but of course before we can actually get into writing any test we first of all need to set up playwright and configure to work with electron and V and to start off we first of all need to run the installation wizard for playright using npm in it playright and this will basically ask us where we want to put our test files I'll just call the folder e2e so end to end because we will have unit tests and I don't want to get anything confused here then it will ask us if you want to set up GitHub actions the default is actually no here and we will not get into GitHub actions in this course anyway so we will stay with saying no and lastly it wants to install the default browsers so Firefox chromium and Safari for all use case though we basically only need chromium for electron and honestly not even that but some browser needs to be installed for all of this to work properly so we will just go with chromium so we will say no and then we can just go ahead and run npx play right install chromium which for me will happen instantly because I've already installed it but for you it might take up some time because it will install up to 100 megabytes which is also why we aren't installing any other browsers because it's a waste of resources and now we've basically got this end to end folder right here and also the test examples test examples are basically just an example of what syntax you can use but because we'll Implement everything ourselves we can actually directly delete these and now we can actually already head into a package Json and add a new script called test colon end to endend or whatever you want to call it and here we'll just call playright test to basically run the default test which will just go to playright dode and check some default settings of the playright side so now if we just run npm run test end to end we should see that we are now running the tests and that everything is working just fine but we are actually getting four errors right here which is because it tries to run the test in Firefox webkit and chromium so basically each test in here each of the two runs once in Firefox once in webkit and one syn chromium so how do we fix this we only got one browser installed well we head into the playright config right here and here we can see projects so basically for what browsers do I want to run my tests well I basically don't need any of these browsers but as I mentioned it won't run properly if you don't have any browser set up so we'll stay with using chromium and then we just remove Firefox and webkit so you could comment this out or basically just remove it completely and now if you just start test again we should see that it passes with two green tests so now we can actually just do one more thing which is connect this to our V instance because if you remember what we're doing when we're starting our def environment is we are spinning up the V server we are spinning up the electron app and the electron app connects to a v server to basically allow hot modor reloading and this is also what we're going to do when using this app we basically spinning up the app in development mode and then telling it hey you basically now need to connect to the V server because we of course don't want to build the app every time we want to run a test so what we'll need to do is basically tell playr okay you spin up the electron app but before you do this please run the web server that the electron app will need to connect to and to do that we'll basically comment in this web server bit right here then we'll need to tell it what command runs the web server so if we just take a little look right here that's npm run def react which which will basically just run V so let's just put that here and then in our V config we can actually go ahead and see the port which is 5123 so let's paste this and try it out and what we're actually going to see is that it won't work it will basically never do anything because it will now wait for the V server to start up and will only run any tests if the server actually started up successfully which it unfortunately did but we don't see this here so what it actually does is it tries to get to this URL and if you now try to open this URL then it won't work which is the case because V actually expects you to use Local Host and not this IP address we could configure V to use it but it doesn't really make any sense for us so we can just rename this to Local Host 5123 quit the script and run again and now we can see that the test should start up again perfectly and the weed server actually starts up in the background which is exactly what we need for electron app to be running perfectly as soon as we get to actually implementing it now so let's go and of course before we can actually run an electron test we first of all need to initialize our electron app and the main window before every test and luckily there's a helper for that called test. before each and playr and here we can just pass an asnc function that should execute every time before a test runs and here we can now use another little helper from playr which is called underscore electron which will allow us to interact with our electron app and to start off we first of all launch electron using certain arguments so as you can see we first of all pass the arguments which is Dot and we pass an environment the environment is of course our note environment which we're setting to develop just like in our package Jason and this dot is actually the same thing we're doing in our package Jason as well so if we just take it another look in it then we'll see that when we run npm run def electron we first of all of course build electron then we set the environment to develop just like we did in our launch script and then we just run electron with a DOT to basically tell us hey our electron app is this directory please run it right here and that's basically the same thing we're doing here as well and then we can just do one more thing which is await electron app. first window and by doing this we're basically now waiting for electron to open the main window to then start the tests so we now just go ahead and run npm run test end to endend we should see that our tests Now open up this window wheel cck before then closing because the normal tests run through and everything is fine now you do also see that I've got these check marks right here and this play button and these will actually allow me to run tests separately from each other which is really handy as well and you can get that by actually installing the extension playright test for vs code I think this really helpful and it might make debuging a lot easier for you as well so I would recommend that you install it but that's just a little hint on the side so now let's actually get back to a test because we also need to clean up after ourselves because every time we run a test it will now open up an electron app and not really stop it properly which might cause memory issues so what we'll do here is basically clean up after ourselves using test. after each we pass another Asing function and then we just await electron app. close but now our electron app is of course not available because it's only available inside of the before each so the simple solution for that is of course to go ahead and say let electron app appear here and now all of this works but our electon app has an any type and to solve that we could now either try to import it which is actually not as easy as you think or we can just set this type to be the return type of the type of uncore electron do launch and and this will almost work because we now get the return type of this launch function but as you can see we need to await it so the launch function actually returns a promise and to now convert This Promise of a electron application into an electric application we can now just go ahead and say I want the awaited return type and now this is basically of the type electron application and we can actually do the same thing for our main page as well because of course when we want to interact with our electron app we will run queries against this page and to make that work we of course need to get the page outside of the test before each as well so to do that we'll actually just do the same thing real quick and as you can see this is really similar we just replaced electron launch with electron app. first window so basically building up on this type we can now get this type and use the main page any way we want as well and as you can see our after each is now working perfectly as well because if of course now can access the electron app so now let's get into actually writing some tests on our electron app instead of on the htps playwriter def set and get started we'll actually interact with the frame we've just built by clicking our custom minimize button in in the UI we first of all need to run a click event on a certain tag on our main page and because we've given the tag the minimize ID we can now just interact with this item by running main page. click with the hashtag minimize to get the tag with the minimize ID and click it and of course we also now need to find out if the app was actually minimized so for that we can actually run electron app. evaluate which will basically give us an option to not only interact with the UI but also with electron internal code and this is done by actually passing a call back with the electron object right here which we can now interact with and to do that we can basically now go ahead and say okay from my electron app I want to get the browser window apis and I want to get all the windows and because we only have one main window I want to get the first one and is minimized value and now I can just use this is minimized value outside of this evaluate block the only thing you need to be careful with here is that this can only return Json formattable stuff so you can't pass something like a function in here and you can't pass something like a date and now that we done this we can now use this is minimized function in an expect block so we expect is minimized to be true after minimizing and if we now just try this out by killing our terminal and running this again using npm run test end to end we should see that the window opens we even saw it minimize and now if we just move this up a bit we can see our test passed so testing against our UI now works perfectly fine awesome and of course we can do something really similar to actually check electron internal stuff as well so here's a little test for checking if our menu is working so first of all we run electron evaluate after our window has opened and basically just get our application menu that we've configured and get it as a variable here so this is now an electron menu or null but because we've set it up we know it's not null but just to make sure we'll first of all check that it isn't null and then we can get the items from our menu which should be two items get the sub items count how many they are and then we can run tests on our label and as you might remember our label is actually not called select it is called view so let's just change this up real quick and now if we just run npm run test end to end again we should see that our test pass right let's try this out and we can see that our test passed perfectly fine but at some point you might realize that some of these tests are a bit flaky because sometimes they tend to like flicker a bit so sometimes they are true sometimes they are false and that is the case because this electron app. first window will actually check if our browser window was initialized which is basically the first thing we're doing we're not even loading a URL or whatever we aren't doing any of this code down here and many times we will actually not reach this create menu all the way down at the bottom and to prevent this issue from ever happening we can actually go ahead into this before each block and basically wait for a preload script to have been loaded and after all of that we'll be really sure that most of our electron code will have at least loaded once so we won't have all that flickering that will sometimes happen in this case we were lucky but it will not always be this way so let's get into implementing that and luckily this can be done in quite a simple function as well so we'll create a function that will wait for our preload script to load and because we'll basically pull for the preload script we need to wrap this inside of a promise because as you can see I'll create an interval that will run every 100 seconds and when you want to await something in an interval you'll basically need to create a new promise and then once you've reached the desired value we basically just resolve the promise so that your promise will not be awaited anymore but actually return something and what we'll do here is basically now just go ahead and say main page to the value so we will not go ahead and evaluate something in electron but actually run code inside of the HTML of our main window and here we can basically now just go ahead and say I want to get the window inside of my main page here we actually need to fake the type a bit because we can't use our Global type that we def find in the T config because playr just doesn't use the T config and what we'll do here is basically just get our electron object that is created using the preload script Right Here and Now using this electron bit we can basically go ahead and say okay I will save this as an electron bridge and if the electron Bridge was initialized I want to stop the interval so I will stop polling and resolve this promise so that I can stop awaiting it and actually move on with my test so we basically go ahead and say okay every 100 milliseconds I want to check did the window now load the preot script and is my electron object initialized and if so please stop poing and just resolve this promise with through so that we can move on and by basically now doing this we can just go ahead and put this inside of our before each block and now the test should stop flickering I can of course not show you really that this is now not flickering anymore but you just need to trust me that this will make your test a lot more stable and as you can see it's still passing just fine and it will pass even better if your test didn't work because as I mentioned it can flicker sometimes so for me it just worked but for you it might not have worked the first time and it should work now and this is basically everything you need to set up to test your electron app using ENT you can now test the UI you can test electron specific features but you cannot test every electron specific feature because this electron app. evaluate bit this electron thing can't access everything it for example can't properly access the tray so what we'll do now to actually test our tray menu is actually create a unit test using v test that will test this tray menu and make sure that it works as well so you can also see how you would unit test your electron apps let's go and to now actually test our tray we first of all need to set up v test which is going to be your unit testing library for this and first of all I'd of course recommend the vs code extension again just for better debug ability and of course also recommend that you install v test using npmi save Dev v test and after doing that we should be able to get everything set up and now it's installed we'll basically just need to add another script to our package Jason as well called test colon unit and this will just run v test source so that we basically just run every test in our source directory and now we can already go ahead go into the electron directory and create a new file called tray. test.ts which will now contain our test for the tray and and here we can now start off by importing some stuff from v test of course and using this we can now go ahead Define a name for a test and a callback function that should execute when we run the test and of course the main thing we're going to do now is actually create a tray so let's just import create tray from do/ tray and this is now going to execute our tray creation and of course if this now were an end test then we need to set up the app pass the main window and find some way to actually test stuff on the tray but because we are writing a unit test here we're now going to try to create a few mocks as few as possible of course to basically turn off most of the electron functionality create a custom main window that only has the functions that we need and basically just get us through so that we can get all of the data out of the trailer that we need and run tests on that and to get started we of course need some kind of a main window right here because create tray expects the main window to be passed and if you just take a look at the implementation we can see the main window is only used to run the show method so we need some kind of an object that has a show method for our test to actually work and we're just going to create one right here so con main window and show is going to be a vi. FN and what this will basically do is we create a mock function where can basically say okay this function should not do anything we can test if the function was run we can also go ahead and Define custom behavior for this function so it should have a custom return value it should have a custom implementation all of this stuff can be done in v test but for our use case it is plenty to just be able to check okay was this function called so did my implementation actually work and now we can just pass this main window into create tray and we're going to see okay there are still some type errors here because our main window is actually not of type browser window so because this thing can't actually be cast to a browser window properly we'll just say as any right here and to still make sure that we have a good def experience we'll also say that it satisfies a partial of a browser window which will allow us to still have good autocomplete like for example defining a height function on here which is basically inferred by knowing that this should be a partial browser window so we can get all the highlighting of a normal browser window while still having the types of any basically and if we now also want to know that this is a browser window down here because right now I can't know that there's a show function on here we can also just cast it to a browser window after casting it to any right here and now our type is a bit ugly but it will allow us to both have good autocomplete inside of this object and also outside of this object because now main window. show is actually something that exists and if you now just try this out real quick by going into to our create tray function and just removing the default implementation real quick and just running main window. show then we should be able to actually test that main window. show was called to make sure that everything is working properly so let's go expect main window. show do2 have been called if we now just run this test using npm run test colon unit which we defined before then we should be able to see that my test now passed and if I now go ahead and just stop this show from running by hitting create tray and just commenting this out as well then we see my test failed which is exactly what we'd expect so now let's actually reverse all of this by actually commenting all of this back in and getting to actually implementing all of the mocks we need for this to basically not actually try to run electron code but only run the code we need to get through here and basically interact with the menu that create right here because what my goal for this test is is to basically go ahead and check do the functions so the click functions inside of the show and the quit block actually do what I'd expect them to so we need some kind of way to basically get into this menu that built from template function so we'll basically mock it get the parameters that we pass to it and then run tests on these parameters but of course to do that we first we need to mock the tray because if electron will try to actually spin up an app to create a tray then that will fail because inside of the context of unit test we can't spin up an app and then we of course also need to make sure that a mocked tray does actually have a set context menu function because if that doesn't exist then we'll run into a null pointer here and once all of this works we can basically mock menu the build from template and then get the parameter of this Mock and interact with it inside of our tests and luckily v test also has a really nice mocking Library so we just have vi. Mock and here we now pass the name of the library want to mock in this case this is going to be electron and here we just pass in a function and whatever this function returns will now be the stuff that is exported from from electron so if we now just go ahead and pass an empty object here then all of the inputs in our tray will now not work because we're basically telling electron no you don't export a browser window or a menu or whatever you export nothing so we now need to go ahead and Define everything that electron should export so if we just take a look at our implementation again we can see okay we need a tray and the tray needs to be a function because a Constructor is basically nothing else but a function and this function needs to return a tray so let's do that starting off with defining the tray right here and because we want to have some custom implementation we'll Define this as a VI ofn and this will have a mock return value and basically whatever we pass into this mock return value will now be what is returned when we call Tray and in this case this should be an object and if we just take another look at the implementation this will then be the tray and the only thing we run on the tray is actually set context menu so our mock will need to have a set context menu method which once again is just the vi V and now we should almost be at a point where set context menu is called but if you just take a look at our test right here and make this a little bit bigger then we're going to see app.get appu doesn't exist because app isn't defined and the implementation actually needs this app because it's passed as a parameter to a new tray so we can't get to this set context menu function as long as this app isn't defined in get asset PA right here so we now need to Define an app and actually Define the function that is used here which is of course get app PA so let's all also do that so we'll just go ahead and say our app is an object and this object has a get app function which is also a VI FN with a mock return value of Slash and now we can see that we're actually failing when we run menu. built from template because the menu doesn't exist so we at least got to the point where our tr. set context menu was not called so let's actually get into also mocking this function that we hopefully can now go through this whole thing without running into any arrrow so let's just go in here and also Define our menu so menu is also going to be an object and the only thing we call on it is build from template and again build fro

Original Description

Learn how to use Electron, React, and TypeScript to build desktop applications. Throughout this course, you'll learn everything from setting up your development environment to implementing advanced features like data visualization, secure communication, and custom window frames, all while ensuring a seamless user experience. Course developed by @NiklasZiermann Code: https://github.com/N-Ziermann-YouTube/electron-course ❤️ Try interactive React courses we love, right in your browser: https://scrimba.com/freeCodeCamp-React (Made possible by a grant from our friends at Scrimba) ⭐️ Contents ⭐️ ⌨️ (0:00:00) Intro & How electron works ⌨️ (0:08:31) Setting up React ⌨️ (0:11:45) Setting up Electron ⌨️ (0:17:41) Adding TypeScript ⌨️ (0:22:47) Configuring Electron-Builder ⌨️ (0:29:07) DX Improvements ⌨️ (0:35:16) Reading System Resources ⌨️ (0:46:25) Communicating with the UI ⌨️ (1:09:34) Making IPC Typesafe ⌨️ (1:32:10) Event Validation ⌨️ (1:39:55) Unsubscribing from Events ⌨️ (1:43:55) Data Visualisation ⌨️ (2:00:24) Securing the App ⌨️ (2:06:53) Creating Tray Elements ⌨️ (2:14:40) Hiding to the Tray ⌨️ (2:27:31) Customising the Menu Bar ⌨️ (2:35:01) Enable View Switching ⌨️ (2:40:02) Custom Window Frames ⌨️ (2:51:00) Testing Theory ⌨️ (2:53:26) E2E Tests ⌨️ (3:06:27) Unit Tests ⌨️ (3:19:19) Finalising the UI ⌨️ (3:33:03) Outro 🎉 Thanks to our Champion and Sponsor supporters: 👾 Drake Milly 👾 Ulises Moralez 👾 Goddard Tan 👾 David MG 👾 Matthew Springman 👾 Claudio 👾 Oscar R. 👾 jedi-or-sith 👾 Nattira Maneerat 👾 Justin Hual -- Learn to code for free and get a developer job: https://www.freecodecamp.org Read hundreds of articles on programming: https://freecodecamp.org/news
Watch on YouTube ↗ (saves to browser)
Sign in to unlock AI tutor explanation · ⚡30

Playlist

Uploads from freeCodeCamp.org · freeCodeCamp.org · 0 of 60

← Previous Next →
1 React: Production Server Setup Part 2 - Live Coding with Jesse
React: Production Server Setup Part 2 - Live Coding with Jesse
freeCodeCamp.org
2 cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
cookies vs localStorage vs sessionStorage - Beau teaches JavaScript
freeCodeCamp.org
3 Browser history tutorial - Beau teaches JavaScript
Browser history tutorial - Beau teaches JavaScript
freeCodeCamp.org
4 Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
Graph Data Structure Intro (inc. adjacency list, adjacency matrix, incidence matrix)
freeCodeCamp.org
5 React: Parameterized Routing with Next.js - Live Coding with Jesse
React: Parameterized Routing with Next.js - Live Coding with Jesse
freeCodeCamp.org
6 React: Dealing with jQuery Issues - Live Coding with Jesse
React: Dealing with jQuery Issues - Live Coding with Jesse
freeCodeCamp.org
7 setInterval and setTimeout: timing events - Beau teaches JavaScript
setInterval and setTimeout: timing events - Beau teaches JavaScript
freeCodeCamp.org
8 Browser and Device Testing - Live Coding with Jesse
Browser and Device Testing - Live Coding with Jesse
freeCodeCamp.org
9 Last Minute Updates - Live Coding with Jesse
Last Minute Updates - Live Coding with Jesse
freeCodeCamp.org
10 Post Launch Updates - Live Coding with Jesse
Post Launch Updates - Live Coding with Jesse
freeCodeCamp.org
11 React: Setting Up Google Analytics - Live Coding with Jesse
React: Setting Up Google Analytics - Live Coding with Jesse
freeCodeCamp.org
12 React: Masonry Layout - Live Coding with Jesse
React: Masonry Layout - Live Coding with Jesse
freeCodeCamp.org
13 Load Balancing Digital Ocean Droplets - Live Coding with Jesse
Load Balancing Digital Ocean Droplets - Live Coding with Jesse
freeCodeCamp.org
14 try, catch, finally, throw - error handling in JavaScript
try, catch, finally, throw - error handling in JavaScript
freeCodeCamp.org
15 Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
Load Balancing: SSL Passthrough Setup - Live Coding with Jesse
freeCodeCamp.org
16 Graphs: breadth-first search - Beau teaches JavaScript
Graphs: breadth-first search - Beau teaches JavaScript
freeCodeCamp.org
17 React: Masonry Layout Part 2 - Live Coding with Jesse
React: Masonry Layout Part 2 - Live Coding with Jesse
freeCodeCamp.org
18 React: WordPress API Live Search - Live Coding with Jesse
React: WordPress API Live Search - Live Coding with Jesse
freeCodeCamp.org
19 Creating WordPress Custom Post Types - Live Coding With Jesse
Creating WordPress Custom Post Types - Live Coding With Jesse
freeCodeCamp.org
20 Dates - Beau teaches JavaScript
Dates - Beau teaches JavaScript
freeCodeCamp.org
21 Miscellaneous Front End Updates - Live Coding with Jesse
Miscellaneous Front End Updates - Live Coding with Jesse
freeCodeCamp.org
22 Merging a Pull Request from GitHub - Live Coding with Jesse
Merging a Pull Request from GitHub - Live Coding with Jesse
freeCodeCamp.org
23 React + Prettier + Standard JS - Live Coding with Jesse
React + Prettier + Standard JS - Live Coding with Jesse
freeCodeCamp.org
24 React: Sortable Responsive Table - Live Coding with Jesse
React: Sortable Responsive Table - Live Coding with Jesse
freeCodeCamp.org
25 Geolocation Sorting by Distance - Live Coding with Jesse
Geolocation Sorting by Distance - Live Coding with Jesse
freeCodeCamp.org
26 Tradeoff Matrix - Agile Software Development
Tradeoff Matrix - Agile Software Development
freeCodeCamp.org
27 The Definition of Ready - Agile Software Development
The Definition of Ready - Agile Software Development
freeCodeCamp.org
28 Getting first React job without experience - Ask Preethi
Getting first React job without experience - Ask Preethi
freeCodeCamp.org
29 React: Google Analytics Click Tracking - Live Coding with Jesse
React: Google Analytics Click Tracking - Live Coding with Jesse
freeCodeCamp.org
30 Submitting a PR to an Open Source Project - Live Coding with Jesse
Submitting a PR to an Open Source Project - Live Coding with Jesse
freeCodeCamp.org
31 Should I go back to school to get CS degree? - Ask Preethi
Should I go back to school to get CS degree? - Ask Preethi
freeCodeCamp.org
32 Hero Section CSS Changes - Live Coding with Jesse
Hero Section CSS Changes - Live Coding with Jesse
freeCodeCamp.org
33 Working Agreement - Agile Software Development
Working Agreement - Agile Software Development
freeCodeCamp.org
34 A day at Pennybox with Co-Founder Reji Eapen
A day at Pennybox with Co-Founder Reji Eapen
freeCodeCamp.org
35 React: Sorting and Filtering Data - Live Coding with Jesse
React: Sorting and Filtering Data - Live Coding with Jesse
freeCodeCamp.org
36 React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
React: Sorting and Filtering Data Part 2 - Live Coding with Jesse
freeCodeCamp.org
37 React: Building a New UI - Live Coding with Jesse
React: Building a New UI - Live Coding with Jesse
freeCodeCamp.org
38 Definition of Done - Agile Software Development
Definition of Done - Agile Software Development
freeCodeCamp.org
39 Getting started with jQuery (tutorial) - Beau teaches JavaScript
Getting started with jQuery (tutorial) - Beau teaches JavaScript
freeCodeCamp.org
40 Making a React Blog with WordPress Content - Live Coding with Jesse
Making a React Blog with WordPress Content - Live Coding with Jesse
freeCodeCamp.org
41 React, NextJS, CSS - Live Coding with Jesse
React, NextJS, CSS - Live Coding with Jesse
freeCodeCamp.org
42 jQuery events - Beau teaches JavaScript
jQuery events - Beau teaches JavaScript
freeCodeCamp.org
43 React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
React/NextJS Routing and WordPress API Custom Types - Live Coding with Jesse
freeCodeCamp.org
44 React: Working with API Data - Live Coding with Jesse
React: Working with API Data - Live Coding with Jesse
freeCodeCamp.org
45 React: Refactoring Components - Live Streaming with Jesse
React: Refactoring Components - Live Streaming with Jesse
freeCodeCamp.org
46 jQuery effects - Beau teaches JavaScript
jQuery effects - Beau teaches JavaScript
freeCodeCamp.org
47 More React Refactoring - Live Coding with Jesse
More React Refactoring - Live Coding with Jesse
freeCodeCamp.org
48 animate in jQuery - Beau teaches JavaScript
animate in jQuery - Beau teaches JavaScript
freeCodeCamp.org
49 "Finishing" My React Site - Live Coding with Jesse
"Finishing" My React Site - Live Coding with Jesse
freeCodeCamp.org
50 Starting a New React Project (P2D1) - Live Coding with Jesse
Starting a New React Project (P2D1) - Live Coding with Jesse
freeCodeCamp.org
51 React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
React Project 2 Day 2: Learning Material UI - Live Coding with Jesse
freeCodeCamp.org
52 The Agile Manifesto - Agile Software Development
The Agile Manifesto - Agile Software Development
freeCodeCamp.org
53 jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
jQuery: get and set with http, text, val, and attr - Beau teaches JavaScript
freeCodeCamp.org
54 React Project 2 Day 3 - Live Coding with Jesse
React Project 2 Day 3 - Live Coding with Jesse
freeCodeCamp.org
55 The INVEST approach to product backlog items
The INVEST approach to product backlog items
freeCodeCamp.org
56 React Project 2 Day 4 - Live Coding with Jesse
React Project 2 Day 4 - Live Coding with Jesse
freeCodeCamp.org
57 Chickens and Pigs - Agile Software Development
Chickens and Pigs - Agile Software Development
freeCodeCamp.org
58 React Project 2 Day 5 - Live Coding with Jesse
React Project 2 Day 5 - Live Coding with Jesse
freeCodeCamp.org
59 jQuery: add and remove DOM elements - Beau teaches JavaScript
jQuery: add and remove DOM elements - Beau teaches JavaScript
freeCodeCamp.org
60 React Project 2 Day 6 - Live Coding with Jesse
React Project 2 Day 6 - Live Coding with Jesse
freeCodeCamp.org

This course teaches how to build desktop applications using Electron, React, and TypeScript, covering topics from setup to advanced features like data visualization and secure communication. By the end of the course, you'll be able to build and deploy your own desktop applications.

Key Takeaways
  1. Set up Electron and React
  2. Add TypeScript to the project
  3. Configure Electron-Builder
  4. Implement data visualization and secure communication
  5. Create tray elements and customize the menu bar
  6. Write unit tests and E2E tests
💡 Using Electron, React, and TypeScript together allows for building robust and scalable desktop applications with a seamless user experience.

Related Reads

Up next
How to Speed Up Your WordPress Website with WP Rocket ⚡Tutorial 2026
Matt Tutorials
Watch →