JavaScript

Creating GUI for Duplicate File Finder app

Now we create the renderer.js file required by the index.html file as we mentioned in previous page. The renderer.js file is responsible for the getting input from the users.

The renderer.js file receives the input from the user, for example, when a user clicks on the Add folder button, in response the renderer.js shows the directory picker dialog box.

Lets start writing the renderer.js file. First we’ll import the Electron’s dialog api.

const {dialog} = require('electron').remote

let dir = dirObject
let dirs = []
let isStarted = false

let startBtn = document.querySelector('#startBtn')
startBtn.addEventListener('click', () => {
 if (dirs.length === 0) return
 isStarted ? pauseApp() : startApp()
})

let addBtn = document.querySelector('#addBtn')
addBtn.addEventListener('click', addFolder)

let pathsDiv = document.querySelectory('#paths')
let pathsDivHead = document.querySelector('#pathsHead')

const {dialog} = require('electron').remote

Load the Electron’s dialog api to display directory picker or browser.

let dir = dirObject

The dir object will hold the information of a folder added by the user i.e.  its type included or excluded, its path and so on. We’ll create this class later in this tutorial.

let dirs = []

The dirs array stores the dir objects. Whenever a folder added for scanning, the app create a new instance of dir object by calling it with folder path and then store that object in the dirs array.

let isStarted = false

The isStarted stores the app state – running or stopped. (See startBtn event function).

let startBtn = document.querySelector('startBtn')

Selects the starBtn from the index.html. We’ll use this button to start or pause the application.

startBtn.addEventListener('click', () => {

Add an event listener to the startBtn that fires when javascript detects a click on startBtn button.

if (dirs.length === 0) return

The callback function checks the dirs array, if array is empty then the further code will not execute due to not a single path available for scanning.

isStarted ? pauseApp() : startApp()

The ternary operator calls the pauseApp if isStarted value is true to pause the application or startApp if  the isStarted value is false to start the application.

let addBtn = document.querySelector('#addBtn')

Selects the addBtn from the index.html. We’ll use this button to browse our file system with the help of Electron’s dialog.

addBtn.addEventListener('click', addFolder)

Add an event listener to the addBtn that fires when javascript detects a click on addBtn button. The addFolder function will call in response.

let pathsDiv = document.querySelectory('#paths')

Select the div from the index.html to display the folders paths selected by the user using add folder button and also show some option such as delete path button and change the type of path to included or excluded button.

let pathsDivHead = document.querySelector('#pathsHead')

Select this div from the index.html. This div shows the headings content for the pathsDiv. By default it is hidden, we’ll unhide it when there is at-least one dir object exist in the dirs array. It means, at least one path exist for scanning to display the paths heading content.

Creating startApp function

function startApp(){
  startBtn.innerHTML = '<span class="red">&#9208;</span> Pause search'
  isStarted = true
}

Currently this function has only two lines of code, one for changing the text of startBtn to Pause search and the other line change the value of isStarted. The isStarted  hold the current state of application and its value must be change upon start or pause of the application.

Creating pauseApp function

function pauseApp() {
 startBtn.innerHTML = '<span class="green">&#9654;</span> Start search'
 isStarted = false
}

Similarly the pauseApp function also contains the two lines code to change the text of startBtn and value of isStarted.

The above two function are currently not doing a tough job but we’ll write more code to really start or pause the app later.

Creating the addFolder function

function addFolder(){
 if (isStarted) return
 let options = {
  properties: ['openDirectory', 'multiSelections']
 }
 dialog.showOpenDialog(options, (paths) => {
 if (!paths) return
 paths.forEach(path =>{
  let exist = dirs.some((dir) => {
   return dir.path == path
  })
  if (exist){
   alert(path + ' already added in the list') 
   return;
  }
  new dir(path);
  })
 })
}

The addFolder function displays the directory picker. It first checks the current state of application using if (isStarted) return code, and displays the dialog box (directory picker) if the application is not running.

The dialog box return an array of file paths chosen by the user,  we’ll retrieve each path with the help of forEach loop and test the each path if it already exist in dirs array, using javascript’s some() method. The some() method tests whether at least one element in the array passes the test implemented by the provided function.  See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some.

If path is not already exist in dirs array, we’ll create a new instance of new dir(path) object by calling it with path argument.

Creating the dirObject class

The dirObject object has two properties, path (the folder path) and isIncluded.  The isIncluded tells the app to scan this path if its value is true.

For example, we want to scan a folder d:\brainbell for duplicate file search but there is a folder d:\brainbell\backups which we want to exclude from the scanning.

To accomplish this, we must add  both folders and then change the type of d:\brainbell\backups folder to excluded, which will change the value of isIncluded to false,  now d:\brainbell\backups folder and its sub-folders will not scan.

The dir object has some additional code which will use to make some buttons, for example, the delete and type buttons. Each button has an event listener, which fires a callback function when clicked.

function dirObject(path){
 this.path = path
 this.isIncluded = true
 dirs.push (this)
 
 pathsDivHead.className = ''
 
 let textNode = document.createTextNode(path);
 
 let del = document.createElement('span')
 del.className = 'delDir';
 del.innerHTML = '&#128473;'
 
 let type = document.createElement('span');
 type.className = 'typeDir'
 type.innerHTML = 'included';
 
 let div = document.createElement('div')
 div.appendChild(del)
 div.appendChild(type)
 div.appendChild(textNode)
 pathsDiv.appendChild(div)
 
 del.addEventListener('click', () => {
  if (isStarted) return
  pathsDiv.removeChild(div)
  let index = dirs.indexOf(this)

  if (index !== -1)
   dirs.splice(index, 1)

  if (dirs.length === 0 )
   pathsDivHead.className = 'hide';
 })
 
 type.addEventListener('click', () => {
  if (isStarted) return
  this.isIncluded = !this.isIncluded
  if (this.isIncluded){
   type.className = 'typeDir'
   type.innerHTML = 'included'
  } else {
   type.className = 'typeDir typeDirDisable'
   type.innerHTML = 'excluded'
  }
 })
 }

Download project files:

* index.html
* main.js
* style.css
* renderer.js
* logo.png

Duplicate file finder demo


Mode Location