Well, we have a working version of File Explorer that can be used to navigate the filesystem and open files with the default associated program. Now we will extend it for additional file operations, such as deleting and copy pasting. These options will keep in a dynamically built context menu. We will also consider the capabilities of NW.js to transfer data between diverse applications using the system clipboard. We will make the application respond to command-line options. We will also provide support for multiple languages and locales. We will protect the sources by compiling them into native code. We will consider packaging and distribution. At the end, we will set up a simple release server and make the File Explorer auto-update.
Deployment and updates
Built-in capacities for auto updates is one of Electron's most prominent advantages over NW.js. Electron's autoUpdater module ( http://bit.ly/1KKdNQs ) utilizes the Squirrel framework ( https://github.com/Squirrel ), which makes silent possible. It works nicely in conjunction with the existing solution for multiplatform release servers; in particular, one can run it with Nuts ( https://github.com/GitbookIO/nuts ) using GitHub as a backend. We can also quickly set up a fully-featured node server based on electron-release-server ( https://github.com/ArekSredzki/electron-release-server ), which includes release management UI.
Electron-updater doesn't support Linux. The project maintainers recommend using the distribution's package manager to update the application.
For the sake of brevity, we will walk through a simplified autoupdate approach that doesn't require a real release server, but only requires access to static releases via HTTP.
We start by installing the package:
npm i -S electron-updaterNow, we add to the manifest's build field--publish property:
"build": {
"appId": "com.example.chat",
"publish": [
{
"provider": "generic",
"url": "http://127.0.0.1:8080/"
}
]
},
...
Here, we state that our dist folder will be available publicly on 127.0.0.1:8080, and we go on with the generic provider. Alternatively, the provider can be set to Bintray ( https://bintray.com/ ) or GitHub.
We modify our main process script to take advantage of the electron-updater API--./app/main.js:
const { app, BrowserWindow, ipcMain } = require( "electron" ),
{ autoUpdater } = require( "electron-updater" );
function send( event, text = "" ) {
mainWindow && mainWindow.webContents.send( event, text );
}
autoUpdater.on("checking-for-update", () => {
send( "info", "Checking for update..." );
});
autoUpdater.on("update-available", () => {
send( "info", "Update not available" );
});
autoUpdater.on("update-not-available", () => {
send( "info", "Update not available" );
});
autoUpdater.on("error", () => {
send( "info", "Error in auto-updater" );
});
autoUpdater.on("download-progress", () => {
send( "info", "Download in progress..." );
});
autoUpdater.on("update-downloaded", () => {
send( "info", "Update downloaded" );
send( "update-downloaded" );
});
ipcMain.on( "restart", () => {
autoUpdater.quitAndInstall();
});
Basically, we subscribe for the autoUpdater events and report them to the renderer script using the send function. When update-downloaded is fired, we send the update- downloaded event to the renderer. The renderer on this event supposedly reports to the user about a newly downloaded version and asks whether it would be convenient to restart the application. When confirmed, the renderer sends the restart event. From the main process, we subscribe to it using ipcMain ( http://bit.ly/2pChUNg ). So, when reset is fired, autoUpdater restarts the application.
Note that electron-debug won't be available after packaging, so we have to remove it from the main process:
// require( "electron-debug" )();
Now, we make a few changes to the renderer script--./app/index.html:
Chat
require( "./build/renderer.js" );
// Listen for messages
const { ipcRenderer } = require( "electron" ),
statusbar = document.getElementById( "statusbar" );
ipcRenderer.on( "info", ( ev, text ) => {
statusbar.innerHTML = text;
});
ipcRenderer.on( "update-downloaded", () => {
const ok = confirm('The application will automatically restart to finish installing the update');
ok && ipcRenderer.send( "restart" );
});
In HTML, we add the element with ID statusbar, which will print out reports from the main process. In JavaScript, we subscribe for main process events using ipcRenderer ( http://bit.ly/2p9xuwt ). On the info event, we change the content of the statusbar element with the event payload string. When update-downloaded occurs, we call confirm for the user opinion about a suggested restart. If the result is positive, we send the restart event to the main process.
Eventually, we edit CSS to stick our statusbar element in the left-bottom corner of the viewport--./app/assets/css/custom.css:
.statusbar {
position: absolute;
bottom: 1px;
left: 6px;
}
Everything is done; let's rock it! So, we first rebuild the project and release it:
npm run buildnpm run distWe make the release available through HTTP using http-server ( https://www.npmjs.com/package/http- server ):
http-server ./dist
We run the release to install the application. The application starts up as usual because no new releases are available yet, so we release a new version:
npm version patchnpm run buildnpm run distIn the footer component, we display the application name and version taken by the require function from the manifest. Webpack retrieves it at compilation time. So, if package.json is modified after the application is built, the changes do not reflect in the footer; we need to rebuild the project.
Alternatively, we can take the name and version dynamically from the app ( http://bit.ly/2qDmdXj ) object of Electron and forward it as an IPC event to the renderer.
Now, we will start our previously installed release and this time, we will observe the autoUpdater reports in statusbar. As the new release is downloaded, we will get the following confirmation window:
After pressing OK , the application closes and a new window showing the installation process pops up:
When it's done, start the updated application. Note that the footer now contains the latest released version: