Short URL: caseywatts.com/electronworkshop
-
-
Save onstatus/6e763b2eef21033855d5541d45250e2b to your computer and use it in GitHub Desktop.
Electron is a framework you can use to make desktop applications using javascript. You may have already used an Electron app if you have the Slack app or the text editor Atom. With electron, making a simple desktop application is as easy as making a simple web application. Learn to make your own Electron app in this 2-3 hour workshop :D
Electron provides a set of cross-platform APIs you can use to interact with the desktop computer. You can use these to have an icon in the tray/menubar, to display notifications, register keyboard shortcuts, and much more. An Electron application has a node “server” running locally doing most of the work, and often additionally a browser process running locally to display things prettily.
No node experience required, but basic javascript experience would be helpful. If you want to play with Node ahead of time anyway (or brush up on your Javascript), I'd recommend NodeSchool's two command-line courses: Javascripting and LearnYouNode. If you'd enjoy reading/skimming a book, I might recommend Eloquent Javascript (free to read online).
You work remotely. When pair programming or video chatting online, sometimes the connection gets bad.
You're never sure if it's your side or their side having issues.
Sometimes you smartly have a terminal open and have ping google.com
running - then you can tell whether your connection is good!
You'd love it if:
- You could tell at a glance (without an additional terminal window) whether your ping is currently good or bad
- You could get a notification when your wifi goes out, or comes back
In this workshop, you will create from start to finish a tray icon that will display whether or not you are currently connected to the internet. A node process inside of the Electron app will ping google.com every second. We will then:
- Use the tray api to change the app’s icon to reflect the current state
- Display a notification when the internet appears to go up or down
You can see two example applications like this, here:
And an unrelated application that I've written:
An electron menubar app uses RAM and CPU resources comparable to other menubar apps.
Casey's electron app electron-ping
uses this amount of CPU and RAM to run:
- 0.9% CPU, 70.6 MB of RAM
Spot-checks of some other menubar apps running in the background:
- Dropbox 0.3% CPU, 165MB
- Screenhero 0.2% CPU, 124MB
- Google Drive 0.3% CPU, 151MB
We’ll use the HTML5 browser notification API
Goal: Make a notification that says "ping!".
- Use a normal Chrome window. Do NOT use an incognito window for this - notifications are always blocked in that environment.
- Open the developer console on any web page
- Requst permission, then make the notification trigger.
Notification.requestPermission()
should return a Promise (which we'll ignore), and display a request for notifications permission dialog (which you should accept)- then doing
new Notification('yay')
should display the notification - An Electron app will not need to ask for permissions, it has them granted automatically.
- If you want to un-set the permission, you can do so by clicking the lock icon or (i) icon to the left of the url, and setting "Notification" back to "Ask (default)"
- Try controlling whether or not it makes a sound
- (some people report that it doesn't ever make a sound for them somehow? I want to know more about this...)
- Try displaying an icon (use an image url from anywhere online just to get going quickly)
- Some websites (like gist.github.com) have CORS enabled and won't use external images. The mozilla.org com page above doesn't have CORS enabled.
- Create A New Repo. Suggested:
- Name:
electron-ping
- Public
- Initialize with README - yes
- gitignore:
node
- license:
MIT License
- Name:
- Clone your newly created repo (
git clone REPO_URL
) - (commit changes along the way)
npm init
to create apackage.json
(used for npm packages AND electron uses this too ✨)- all defaults are okay
npm install --save-dev electron
(If you want to do git init
locally and want to push it up to github after that's okay, I just don't have instructions for that :) )
- For a linter I recommend “standard”:
- commit these changes (and all other changes along the way)
- Additional
style
challenge: Don't use anonymous functions. All functions should have a name.
The index.js
(node) file in Electron can't make notifications itself, but it can make them using a browser process. We’ll need to make a (secret, hidden) browser window in order to use the HTML5 Web Notification we practiced earlier. (thinker: why can’t node do notifications directly?)
Communication can happen between our main.js node process and browser windows using the electron tools ipcMain and ipcRenderer. Sidenote: a very similar communication protocol is how Chrome Extensions communicate between processes.
For now, let's let a 3rd party npm package to take care of creating a hidden browser window and sending notifications to it. If we have time later, let's look at its source code and try to use ipcMain
and ipcRenderer
directly.
npm install --save-dev electron-main-notification
- Try the basic example from their
README.md
: https://github.com/MaxGfeller/electron-main-notification - Goal: Try making your app send a notification "hi!" every 1 second.
- You may google how to make something happen every [amount of time] in javascript
If you are ahead, try these:
- make the notification not make any noise
- get an icon to appear in the notification
- you can use our tray icons from earlier for this
- You'll want to use an absolute path for these. Use the two Node ~filesystem tools: __dirname and path.join()
By default, browser notifications close after a few seconds. What if we want it to close after only 1 second? We could do that in the browser process using notificationInstance.close()
, but unfortunately electron-main-notification
doesn't give us a way to call that from the main thread index.js
. But you could do it yourself with ipcMain
and ipcRenderer
!
Challenge:
- Get this working without using
electron-main-notification
- Get the notification to close itself after being open for just
500ms
We want to ping google.com periodically to determine whether our internet is currently working. The system ping utility is great for this, we just need a way to interface with it in node, so we'll use the npm package node-ping
.
npm install --save-dev node-ping
- skim https://github.com/danielzzz/node-ping
- try a simple version, using the promise interface
ping.promise.probe()
- Yay promises! For now, just go with it. Later you might want to read more about promises in general
- Use
console.log()
at first, then try ournotify()
from earlier
- Goal: Every
1 second
,ping
google.com
and display thelatency
in a notification- this is just a sanity check that our ping is actually happening every second
- since this is too much information for the user, let's turn this part off. We'll make better notifications in our next step.
- Goal: every time wifi state changes (goes down or comes back on), display a notification about the change
- we can do this based on the [last-seen ping's alive/dead state] (which we'll have to store somewhere)
- test this by turning your wifi off and on
- Style challenge: use named functions instead of anonymous functions
- Change the tray icon based on the most recent ping response (whether it was alive/dead)
- If/when you do the bonus challenge from earlier where the notification goes away after
500ms
, then change the ping's timeout to be500ms
too.
Goal: Package your application for your operating system, and use it to install this package on your own computer.
The tool electron-builder
will automatically create the most common 3 builds for you (OSX, Windows, Linux)
- Install electron-builder
npm install --save-dev electron-builder
- Run it using
node_modules/.bin/electron-builder
- Add
dist/
to your.gitignore
- Open
dist/
folder, and install your packaged app to your computer ✨
Now that you've completed the workshop, here are some additional things you could do to this application to make it even more useful:
- In a browser window
- Configure the server we ping (currently hard-coded to
google.com
) - Configure the interval time (currently hard-coded to
1000
ms)
- Configure the server we ping (currently hard-coded to
- Display ping data in a browser window (like the Dropbox client's window)
- Summary Data
- % of pings dropped in the past 60 seconds
- average latency in the past 60 seconds
- If we’ve dropped any number of pings recently but we’re back up now, display a different icon than our success/failure icons
- still - if the most recent ping was a fail, then display fail
What will happen to our app if ping is >1000ms (or what was set in the configuration)?
- Can that happen?
- Would ping notifications potentially come in out of order?
- Does our use of setTimeout give us any issues?
- what can we do to avoid issues?
Pings of over 300ms are pretty bad - count them as if they’re dropped
For the Applications folder and for icons in the Notification (alive ping / dead ping)
Of course, feel free to do any of your own modifications too. Comment below if you think others might be interested in those ideas, too!
- quit role button’s title is ugly because the name of the project is dasherized "electron-ping" not title case "Electron Ping". Can it be title case here somehow?
- Should everything be
--save-dev
in electron, since we always compile it anyway? (I think so, but I'd love to be more certain) - Should I continue to recommend absolute paths in context like I did - or wait until the error in post-packaging to do it, and then it'll be more motivated?