0% found this document useful (0 votes)
13 views43 pages

Jafar Shora

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
13 views43 pages

Jafar Shora

Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 43

Shora Jafar

Desktop Application with Electron


and React: Darkglass Admin
Application

Metropolia University of Applied Sciences


Bachelor of Engineering
Information Technology
Bachelor’s Thesis
18.10.2021
Abstract

Author: Shora Jafar


Title: Desktop Application with Electron and React: Darkglass
Admin Application
Number of Pages: 36 pages + 2 appendices
Date: 18 October 2021

Degree: Bachelor of Engineering


Degree Programme: Information Technology
Professional Major: Software Engineering
Supervisors: Janne Salonen, Head of School (ICT)

Developing desktop applications with web technologies has gained popularity in


recent years. Modern frameworks and libraries such as React, Electron and Express
have facilitated the development of sophisticated, highly performant applications.
Therefore, the main objective of this thesis was to integrate React and Electron in the
context of developing an Admin application for Darkglass Electronics. Express was
used as well to make a server for the application user administration.

In this thesis, the theoretical background of the chosen technology stack was
provided, and the application specifications were identified. Furthermore, the
environment setup and the project implementation were documented, and the
resulting application was analysed. The thesis concluded with the writer’s evaluation
of the requirements fulfilment and gathered experience along the development
process.

Keywords: React, Electron, Redux, Node, Express


Contents

List of Abbreviations

1 Introduction 1

2 Background 2

2.1 Web Development Technologies 2


2.1.1 HTML 2
2.1.2 CSS 2
2.1.3 JavaScript 3
2.2 Full-stack Web Development 4
2.2.1 Single-Page Applications 5
2.2.2 Node.js 5
2.3 Frameworks and Libraries 6
2.3.1 React 6
2.3.2 Redux 8
2.3.3 Electron 8
2.3.4 Material UI 10
2.3.5 Express 10
2.4 Database 11
2.4.1 MongoDB 11
2.4.2 Mongoose 11

3 Development Requirments and Environment Setup 12

3.1 Application Specifications 12


3.2 Tools and Environment Setup 13

4 Project Implementation 13

4.1 Backend Implementation 14


4.1.1 Server Initialization 14
4.1.2 Folder Structure 15
4.1.3 Controllers, Models and Server Application 16
4.1.4 User Administration with JSON Web Token 19
4.2 Electron Application and Frontend Implementation 20
4.2.1 Electron Application 21
4.2.2 React Application 25
4.3 Application Analysis 31

5 Conclusion 33

References 34

Appendices

Appendix 1: Rankcharts Component Code Listing


A
List of Abbreviations

API: Application Programming Interface

CLI: Command-line Interface

CSV: Comma-separated values

GUI: Graphical User Interface

HTTP: Hypertext Transfer Protocol

I/O: Input/output

IP: Internet Protocol

JSON: JavaScript Object Notation

XML: Extensible Markup Language


1

1 Introduction

Cross-Platform application development has become widespread among the


solution providers since the revolution in internet and mobile devices. The
benefits are mainly considered as saving costs, reducing development time,
and utilizing the already popular and well-established technologies.

Electron, as an open-source software platform, allows using web technologies


such as HTML, CSS, and JavaScript as well as its widely used libraries, namely
React, Vue, etc., to be employed in order to facilitate the development of
operating system specific software from the same base code. The aim of
Electron is to make an entry point for web developers with less experience in
the field to start writing desktop applications.

Darkglass Electronics, a Helsinki based bass guitar equipment company,


needed a desktop admin application to be used by its in-house research and
development team as well as developers and managers to track the products
rankings. To this end, an Electron application, integrated with React, was
proposed for the application frontend and an Express Server and a MongoDB
database to serve as the authentication backend.

Thus, the objectives of the project were the development of:

1) a frontend with login page and navigation bar to route between different
views.

2) a main view allowing the user to upload the products ranking JSON file,
filter the desired products, draw line charts from the filtered data, and
export the selected data on the charts as a .csv file.

3) an Express backend to handle the authentication requests.


2

Following sections start with exploring the related literature to provide an


overview of desktop software development with web technologies. The
specifications, technologies employed and concepts pertaining to the topic will
be described. In addition, the section on implementation process will discuss
the application development and logic. In the end, results of the implementation
process and the conclusion sections will be provided.

2 Background

2.1 Web Development Technologies

During the past decade, substantial advancements in web technologies, have


turned the browser into a medium to run many applications. The fast pace of the
growth in JavaScript libraries in addition to the well-known HTML, CSS stack
has enabled web developers to write large scale applications. (Pernice et al.,
2019)

2.1.1 HTML

HTML is a markup language that is responsible for defining the structure of the
content of a web application. It encloses parts of the content in elements or tags
and dictates the way they appear or act. (MDN Web Docs, no date)

Throughout its history, HTML has been moving towards providing more
semantic markup, division of design and content, and accessibility as well as
the support for rich media experience. (HTML.COM, no date)

2.1.2 CSS

Cascading Style Sheets is a coding language responsible for designing web


pages. It addresses the html elements using selectors and identifies different
attributes for them pertaining to way they look and display. (tutorialspoint, no
date)
3

Although, to enhance their products, web developers nowadays use CSS


frameworks such as Bootstrap, Material UI, etc. these frameworks have
become an inseparable part of modern web ecosystem, as they aid developers
to avoid wasting time on the basics, increase their productivity and unify the
look of their application. (Goyal, 2021)

A typical HTML element and a CSS selector as its attribute is shown in figure 1.

Figure 1. An HTML markup with CSS attribute

2.1.3 JavaScript

JavaScript is a high-level, single threaded, interpreted, object-oriented,


lightweight programming language, which is mostly regarded as a scripting
language to develop web pages. Its dynamic and prototypical construct as well
as multi-paradigmatic programming style have made it widely used in
programming both client (frontend) and server (backend) sides web
development. JavaScript is a relatively easy to learn language and have huge
resource base and community. (MDN Web Docs, no date)

When used in the browser, JavaScript uses a program called the engine that is
specific to each browser and can interpret JavaScript code. Through the
browser API, JavaScript can access and manipulate the Document Object
Model (DOM), which is a tree-structure hierarchy of HTML elements. (Sheikh,
2021)
4

Since its advent in 1995, when it was deployed to add interactivity to the
webpages, JavaScript has flourished into a highly functional language for
developing web, desktop, and mobile as well as building server-side
applications. Moreover, its open-source nature has allowed the emergence of
numerous frameworks including React, Angular, Vue, etc. for client-side, React
Native for Mobile, and namely Node.js for server-side development. (Michael,
2019)

Figure 2 contains a schematic representation of four trending JavaScript


frameworks in the past four year.

Figure 2. Comparative view of four popular JavaScript frameworks (npm trends, no


date)

2.2 Full-stack Web Development

A web application is a software that runs inside a web browser and is


dependent on the network. In the most basic form, it can be just a static web
page. Traditionally, web applications are stored on the server and can be
accessed through unique IP addresses by the browser. However, Due to the
evolution in web technologies, browsers have now become more capable of
deciphering the logic and the application functionalities have moved to the
client-side. (Java T Point, no date)
5

2.2.1 Single-Page Applications

According to Gordon (2018), a single-page application (SPA) is placed in


contrast with a server-rendered application. The later deals with every user’s
action by sending a request to the serve, whereas SPA loads the content in the
browser and every interaction remains inside the browser. This in turn results in
fast performance and pitfalls in search engine optimization (SEO).

An SPA loads a web document once and consequently updates just the DOM
elements through API calls when there are to be changes in the content. This
means that JavaScript changes parts of one HTML file dynamically. To navigate
between different pages, HTML History API, provides the navigation between
different views. (Johansson, 2020)

Figure 3. SPA schematic diagram (Beginning Elm, no date)

2.2.2 Node.js

As a runtime environment, Node enables JavaScript to be run outside of the


browser. It uses Chrome’s V8 engine and allows JavaScript to be employed in
server-side development. Being single-threaded, JavaScript allows for having
asynchronous, non-blocking I/O operations which in turn permits having
numerous concurrent connections with one server. Thanks to the event loop
6

construct that is inherent to JavaScript, Node waits for the response and
resumes just after receiving it. (nodejs, no date)

Node also comes with Node Package Manager (npm), which is a software
registry. npm is used to share third-party, open-source dependencies that are
basically packages of pre-written code. These packages provide access to
libraries that facilitate the application development. Most Web applications
contain a package.json file that hold the name of the dependencies every
project needs to run. As npm comes with a CLI tool, running npm install
command in its CLI, downloads and installs the specified dependencies in a
directory called node_modules. (Johansson, 2020)

For example, a React application usually depends heavily on npm and


node_modules. This will be discussed more in the next section.

2.3 Frameworks and Libraries

2.3.1 React

React is a free and open-source frontend JavaScript library for building user
interfaces (React, no date). It can isolate different parts of a whole web
application into separate building blocks called components. React is mainly
focused on creating Views and does not favor any specific architectural pattern
when it comes to data management. (Johansson, 2020)

React component is basically a JavaScript function or a class that renders JSX


(JavaScript XML) on the DOM. JSX produces React element and allows the
division of concerns and comes with all JavaScript capabilities. Being
fundamentally JavaScript functions, components can receive arguments or
props to dynamically change the Views. They also can keep track of the state of
themselves through state object or React Hooks. (React, no date)

Added to React since the release of version 16.8., Hooks are functions that
enable functionalities, that had been only possible in class components, to be
7

accessible in function components. They were proposed as replacements to


React lifecycle methods. Hooks allow the breaking down of one component into
smaller functions, based on their relations to each other. (React, no date)

There are three phases in the life of a component. In the beginning it is


mounted onto the DOM, this phase followed with possible updates in their state
and eventually they are removed or unmounted from the DOM. These three
phases are React component lifecycle. Figure 4 shows the React component
lifecycle. It should be noted that a component may or may not go through all
these stages. (React, no date)

Figure 4. A diagram of the React component lifecycle

As it was mentioned earlier, React Hooks allow function components to become


more advanced. A hook, called useState, enables the function component to
preserve the state throughout its existence. The useEffect hook can replace
lifecycle methods by executing a customizable function for every render, re-
render, or when the component is unmounted from the DOM. (Johansson,
2020)

The virtual DOM is a programming concept, through which React keeps a


virtual copy of a UI in the memory. This copy is synchronized with the real DOM
by ReactDOM library. This notion allows React to change just the node in the
DOM tree that has been changed and to avoid manipulating those that stayed
unchanged. (React, no date)
8

2.3.2 Redux

Based on the Flux architectural pattern, which was invented by Facebook,


Redux library allows more control over the flow of data in an application. The
underlying MVC (Model-view-controller) that separates data, its representation,
and the way they interact to each other, needed a unidirectional data flow.
(Johansson, 2020)

Redux keeps the state data in its single store that serves as a “Single source of
truth” for the entire application. Views can be subscribed to this store and can
be changed according to state changes in the store. To decide over the
changes, Redux relies on pure functions, called Reducers, that evaluate
changes that are dispatched through Action objects. Every Action has a type
and may or may not have a payload. Upon receiving an Action, the reducer
changes the store according to the type of the Action. This consequently
changes the Views that are subscribed to the store. (Redux, no date)

Redux is a common companion to React as a data management solution


through a npm library called react-redux. This library comes with some custom
hook APIs methods that can be imported into a component and wire up the
Redux store with it. A hook named useSelector allows the component to extract
data from the store. A useDispatch hook returns a reference to the store
dispatch function that allows sending changes to the store. (React Redux, no
date)

2.3.3 Electron

With the influx of libraries and frameworks in the last decade, JavaScript usage
has drastically grown. This growth and the help from Node.js have pushed
JavaScript beyond the realm of the browser. Electron is another framework that,
since its launch in 2013, has become one of the largely used frameworks for
desktop-application development. Some of the well-known desktop applications
9

made by Electron are Visual Studio Code, Slack, Skype, Discord, etc. (Perkaz,
2021)

Electron uses Chromium, that is Google’s codebase widely used in browsers,


alongside Node.js in its binary, allowing writing desktop software without using
native languages. The knowledge of JavaScript can be transferred to Electron
development, thus getting started with it is made easy. However, having
Chromium and Node environments, makes Electron apps to be more resource
hungry than apps made with native counterparts. Moreover, as an Electron app
grows in complexity, challenges related to underlying construct will surface.
(Perkaz, 2021)

According to Kinney (2018), Electron applications contain two processes: a


main process as well as zero or more renderer processes. It is only the main
process that has access to the operating system’s API. It can create and
destroy renderer processes, responds to application lifecycle events, namely
starting up, quitting, going to background or foreground and so on. Whereas
renderer processes load web pages that show a GUI and can run on their
separate threads. These pages in turn can run JavaScript code. Although, they
differ with normal web pages in that they can access all Node APIs in renderer
processes. Figure 5 shows Electron’s multi-process architecture.
10

Figure 5: Multiprocess architecture of Electron.js (Kinney, 2018)

Perkaz (2021) classifies three different types of Electron applications based on


their complexity. Low complexity applications are those web applications
packaged as desktop applications. A good example is Telegram chat app, for
which Electron act as a wrapper for the web application and no changes are
made to the backend. Whereas medium and high complexity applications
demand more custom code for the desktop version, the ability to cache and
work offline.

As it was mentioned above, React is a hugely popular library with a large


community of developers. Having access to numerous component libraries,
reusable code, and resources, make it a good choice to be integrated with
Electron to make highly performant UI in a time saving manner.

2.3.4 Material UI

Material UI is a React component library, that follows Google’s material design


principles, to make high-quality components, with uniform styling throughout the
application. Material UI reduces the amount of work it takes to build reusable,
11

user-friendly and aesthetically appealing user interfaces. Being well-


documented with many working examples available, make it an ideal and to
some extent only choice to style React applications. (react.school, no date)

2.3.5 Express

Express is a Node.js framework that is built based on Node’s Connect


middleware and http module. Express facilitates writing servers by abstracting
Node’s core functionalities. It supports reusing code and provides an MVC-like
structure for web apps (Mardan, 2014). The model part can be supplied with
database libraries such as Mongoose that will be discussed in the coming
section.

2.4 Database

2.4.1 MongoDB

MongoDB is a document-based, non-relational database to store structured as


well as unstructured data. Its structure makes it easy to operate and understand
and is designed to enable both educational small-scale and real-world big
applications. With MongoDB, making data Schema is flexible and
straightforward. It can be installed on various operation systems and be
managed through CLI. However, depending on the project requirements, the
Database as a Service (DBaaS) cloud version of it, called MongoDB (Atlas) is
available as well. MongoDB Atlas provides a secured environments with high
availability and lifts the weight of maintenance requirements off the developers’
shoulders. (Phaltankar, 2020)

2.4.2 Mongoose

Mongoose is an Object Data Model (ODM) library for modelling data for
MongoDB. As MongoDB is schema-less NoSQL database, it stores a JSON-like
format document, which brings about the advantage of speeding up the
12

development. Mongoose can manage data relationships, provide data schema


and its validation, and map objects in the code with their representations in
MongoDB (Karnik, 2018). Figure 6 depicts object mapping in Mongoose.

Figure 6: Object mapping in Mongoose (Karnik, 2018)

3 Development Requirements and Environment Setup

This chapter provides an overview of the application that was developed as the
basis for this thesis.

3.1 Application Specifications

As a result of scaling up their App Suite, Darkglass Electronics, a bass guitar


equipment manufacturer, needed a graphical UI to handle the data. The
specifications proposed for the current project were to develop:

• a frontend with a login page and a navigation bar to route between different
views.

• a view allowing the user to upload the products rankings JSON file, filter
the desired products, draw line charts from the filtered data, and exporting
the selected data on the charts as a .csv file.
13

• an Express backend to handle the authentication requests.

The data for the charts was provided via a JSON file and an open-source
Electron-React boilerplate was suggested by the project manager.

3.2 Tools and Environment Setup

Of all popular IDEs (Integrated development environment), Visual Studio Code


was chosen for this project. It contains built-in JavaScript IntelliSense, formatting
and refactoring and is proven to be an ideal choice for JavaScript programming.

As for version control systems, Git was the obvious choice, and two separate
repositories were instantiated on GitHub to host both the backend and the
frontend code. The frontend repository was populated by cloning the git repository
of the suggested boilerplate.

Considering the size and the scope of the project, GitHub Flow approach taken;
that is to have a lean, simple branching policy. A “develop” branch was made and
every feature branch was immediately merged into it.

In the end, Node.js, and alongside it npm, were installed globally. This allowed
the installation of dependencies on the Electron-React boilerplate through the
“npm install” command.

4 Project Implementation

The implementation process started with designing a backend with Nodejs and
MongoDB Atlas to handle the authentication for the application. The endpoints
on the server were tested using Postman application, that is used for API testing
when there is no frontend available yet.
When the server was developed and running, based on the boilerplate that was
suggested for the project, the development of Electron app integrated with the
React frontend started.
14

4.1 Backend Implementation

4.1.1 Server Initialization

Nodejs, Express and MongoDB Atlas were used to implement the backend for
the application. As it was discussed in the previous chapter, Node comes with
npm package manager, and using "npm init -y" command initializes a Node
project with a package.json file. This is the file that includes the dependencies
that a project need. Moreover, new dependencies can be installed and the
metadata about them can be recorded in the same file. Figure 7 shows the inside
of the package.json file for the server side of the application, the installed
dependencies, and the scripts that can be used in npm CLI to run tasks related
to the application.

As it can be noted, running the dev script via “npm run dev” command starts the
server, and it utilizes nodemon package that automatically restarts the server
after each change in the directory. Also, the list of dependencies contains all the
dependencies or npm packages that were installed by running the command
“npm install <packageName>” at the start of the project.
15

Figure 7: Server application package.json file content

4.1.2 Folder Structure

The structure for the app was based on a common, straightforward approach
that divides the routes, models, and the controllers into different directories.
Figure 8 shows the structure of the server application.
16

Figure 8: Server application folder structure

4.1.3 Controllers, Models and Server Application

Controllers’ folder contains “users.js” that holds two asynchronous functions.


These functions receive the sign-in and sign-up requests that are coming from
the frontend, process the data that are asked for and return the response or an
error message depending on the result of the process. The authentication logic,
that will be discussed shortly, takes place in “users.js”. Listing1 shows the
“signin” controller.
17

const bcrypt = require('bcrypt');


const jwt = require('jsonwebtoken');

const User = require('../models/user');

const signin = async (req, res) => {


const { email, password } = req.body;

try {
const existingUser = await User.findOne({ email });

if (!existingUser)
return res.status(404).json({ message: 'User does not exist' });

const isPasswordCorrect = await bcrypt.compare(


password,
existingUser.password
);

if (!isPasswordCorrect)
return res.status(400).json({ message: 'Invalid Credentials' });

const token = jwt.sign(


{ email: existingUser.email, id: existingUser.id },
'test',
{ expiresIn: '1h' }
);

res.status(200).json({ result: existingUser, token });


} catch (error) {
res.status(500).json({ message: 'Something went wrong' });
}
};
Listing1. “Signin” controller

Controller functions are imported into routes files to be used as call back
functions when the endpoints are requested from the frontend. Listing 2 shows
the routes that serve the end points for sign-in and sign-up requests.

const usersRouter = require('express').Router();

const { signin, signup } = require('../controllers/users');

usersRouter.post('/signin', signin);
usersRouter.post('/signup', signup);

module.exports = usersRouter;
Listing 2. User routers in /routes/user.js
18

Express Router module is used to make a new router object and subsequently
two routes are assigned to it for different routes in the application. Now these
routes can be used as middleware by the web server.

Here, model refers to a Mongoose model that is used to save the data about a
user as a MongoDB document in MongoDB Atlas cloud database. The
Mongoose model for this purpose contains a Mongoose Schema, called
“userSchema”, that maps to the users collection in the database and tells it in
what shape to save the data. In the case of this application, four pieces of data
are needed to be saved in the database. As it is depicted in Listing 3,
userSchema defines the shape of the data in MongoDB and Mongoose “set”
module takes care of adding the id assigned by MongoDB to and deleting the
security-related data from the response user object

const mongoose = require('mongoose');


const uniqueValidator = require('mongoose-unique-validator');

const userSchema = new mongoose.Schema({


name: { type: String, required: true },
email: { type: String, required: true },
password: { type: String, required: true },
id: String,
});

userSchema.plugin(uniqueValidator);

userSchema.set('toJSON', {
transform: (document, returnedObject) => {
returnedObject.id = returnedObject._id.toString();
delete returnedObject._id;
delete returnedObject.__v;
delete returnedObject.passwordHash;
delete returnedObject.refreshtoken;
},
});
Listing 3. The code snippet of Mongoose model
module.exports = mongoose.model('User', userSchema);

The Express server resides in “index.js” and is assigned to the variable “app”. It
is responsible to connect Mongoose to the database and binds the application
route middleware to an instance of the server. This can be seen in Listing 4.
19

const mongoose = require('mongoose');


const express = require('express');

const usersRouter = require('./routes/user');

const app = express();

app.use('/api/users', usersRouter);

const MONGODB_URI = config.MONGODB_URI;

mongoose
.connect(MONGODB_URI, {})
.then(() =>
app.listen(config.PORT, () =>
logger.info(`Server Running on Port: http://localhost:${config.PORT}`),
),
)
.catch((error) => {
logger.error('error connecting to MongoDB:', error.message);
});

Listing 4. The code snippet of the server

4.1.4 User Administration with JSON Web Token

As it was shown in Listing 1, JSON Web Token (JWT) is imported in the


controllers file to securely share information between server and client
applications. JWT digitally signs a token for the user, who enters the credential
that is already saved in the database. The controller sends the token along with
the user data to the browser window. Later this token will persist in the window
unless the user is signed out of the application.

Alongside JWT, bcrypt package is used to hash the password when the user
signs up to the application and to compare the password entered by the user at
the time, they are signing in to verify the existence of the credentials in the
database. The signup code can be seen in Listing 5.
20

const signup = async (req, res) => {


const { email, password, firstName, lastName, confirmPassword } = req.body;

try {
const existingUser = await User.findOne({ email });

if (existingUser) return res.status(400).json({ message: 'User exists!' });


if (password !== confirmPassword)
return res.status(400).json({ message: 'Passwords do not match!' });

const hashedPassword = await bcrypt.hash(password, 12);

const result = await User.create({


email,
password: hashedPassword,
name: `${firstName} ${lastName}`,
});

console.log(result);
const token = jwt.sign({ email: result.email, id: result._id }, 'test', {
expiresIn: '1h',
});

res.status(200).json({ result, token });


} catch (error) {
res.status(500).json({ message: 'something went wrong!' });
}
};
Listing 5. signup controller code snippet

4.2 Electron Application and Frontend Implementation

As stated in chapter 2 the Electron application consists of a main process and


zero to many renderer processes. In the case of this project, a main process
named “main.dev.js”, is placed in React “src” folder and is responsible to run the
React frontend. The structure of the React “src” is shown in Figure 9.

As the application was made on the boilerplate, the Electron was installed as
development dependency in the package.json with the following command:
21

This installation makes Electron available to our Node.js runtime.

Figure 9. Structure of src folder in React application

4.2.1 Electron Application

The main process contains the Electron application. It imports functionalities


from Electron API and making a new BrowserWindow to host the React
application. It also contains two custom functions that handle the importing and
exporting files from filesystem.

By Importing the app module from electron, the event-driven nature of Electron
allows assigning event listeners to the app that runs desired call back function
upon the fulfilment of the event. A snippet of the “main.dev.js” can be seen in
22

Listing 6. The app module runs the createWindow function as soon as


“app.whenReady” returns a fulfilled promise and as it is shown the createWindow
loads “index.html” that contains the DOM node which mounts the React
application.

import path from 'path';


import { app, BrowserWindow, shell, ipcMain, dialog } from 'electron';
import fs from 'fs';
import MenuBuilder from './menu';

let mainWindow: BrowserWindow | null = null;

const createWindow = async () => {


mainWindow = new BrowserWindow({
show: false,
width: 1024,
height: 728,

webPreferences: {
enableRemoteModule: true,
nodeIntegration: true,
},
});

mainWindow.loadURL(`file://${__dirname}/index.html`);

app
.whenReady()
.then(createWindow)

.catch(console.log);

app.on('activate', () => {
if (mainWindow === null) createWindow();
});

Listing 6. main.dev.js code snippet

As it was mentioned earlier, one expected functionality of the application was the
possibility to upload a JSON file and exporting a CSV file after data manipulation.
Two custom functions that were included inside the main process can be seen in
Listing 7. As it is depicted in the listing, “dialog” module from Electron is used to
trigger a native window. “uploadFile” function is exported from the main process.
23

Electron “remote” function allows the rendered to have run “uploadFile” from the
frontend.

“uploadFile” uses the helper function “openFile” to read the filesystem with fs
module from Node and responds with the “webContents” EventEmmiter. In the
case of opening a file from the filesystem, it sends an arbitrary named event,
along with the result, back to the renderer or the frontend component. The
renderer uses Inter-process Communication (ipc) method to send messages to
the main process and listen to the messages that are sent through.

// Import file to the app


const uploadFile = async () => {
const files = await dialog.showOpenDialog({
properties: ['openFile'],
filters: [{ name: 'JSON Files', extensions: ['json'] }],
});

if (!files || files.filePaths[0] === 'undefined') {


return;
}
const file = files.filePaths[0];

openFile(file);
};

// Open file helper function


const openFile = (file) => {
const content = fs.readFileSync(file).toString();
const parsedData = JSON.parse(content);
const fileName = path.basename(file);
mainWindow?.webContents.send('file-opened', fileName, parsedData);
};

// Export file from the app


const saveCSV = async (targetWindow, content) => {
const file = await dialog.showSaveDialog(targetWindow, {
title: 'Save CSV',
defaultPath: app.getPath('documents'),
nameFieldLabel: '.csv',
filters: [{ name: 'CSV Files', extensions: ['csv'] }],
});
if (!file.filePath) return;

console.log(file);

Listing 7. Functions to import


fs.writeFileSync(file.filePath, and export files to filesystem
content);
};
24

As it can be seen in Listing 8, the React component imports both “remote” and
“ipcRenderer” from Electron. It uses “remote” to gain access to the main
process function and uses “ipcRenderer” to listen for the event “file-opened”.
After the promise has been fulfilled the callback function stores the content of
the data in application state.

// import Electron modules


const { remote, ipcRenderer } = require('electron');

// Using remote to have access to main process functionalities


const mainProcess = remote.require('./main.dev.ts');
const currentWindow = remote.getCurrentWindow();

// Function Component
const HandleFiles = () => {

const [selectedFile, setSelectedFile] = useState(undefined);


const chosenData = useSelector((state) => state.ranks.selectChartData);

// Function for open Import button


const openFile = () => {
mainProcess.uploadFile();
console.log('you clicked the openfile button');
ipcRenderer.on('file-opened', (event, fileName, content) => {
setSelectedFile(fileName);
dispatch(initializeRankData(content));
});
};

// Function for export .csv button


const saveFile = () => {
(async () => {
const csv = new ObjectsToCsv(chosenData);
const csvString = await csv.toString();
mainProcess.saveCSV(currentWindow, csvString);
})();
};

Listing 8. Code snippet from HandleFiles component

Exporting the file follows the same event-based system of Electron. In this case
data from the frontend is being sent back to the main process to be saved on
25

the filesystem. Selected data from the state is passed to the


“mainProcess.saveCSV()” function that is available via remote module.

The last code snippet shows Electron’s ability in providing Node modules in the
client-side BrowserWindow. The aspect that is not available in web browsers.
As stated above, the mainWindow in the main process is an instance of the
BrowserWindow, which in the development environment opens up by running the
command “npm start” that is wired up in the boilerplate “package.json” file. The
React application loads up in the mainWindow.

4.2.2 React Application

The views that are included in the application can be divided as follows:

• Login page that also serves as the landing page of the application

• Dashboard page that will be shown after successful login and acts as the
homepage

• Rank chart page to draw charts from the data

Material UI

To have uniform design and save time, material UI was installed through the
following npm commands:

Login Page

Login page prompts the user to input their email and password. As it was
described in section 4.1.4, When the user presses the login button, and the
request goes to the backend, a JWT token will be generated and sent back to
26

the frontend. This token then persists in BrowserWindow “localStorage”

This process can be seen in Listing 9. A promise based Axios call, named
“apiClient”, is made via the create method and it is intercepted to check if the
user is authenticated, in that case, the Authorization header of the
BrowserWindow will be populated by the token.

This “apiClient” is used in Redux action creators through authentication services


to fetch the authentication data and save it in Redux Store.

import axios from 'axios';

let BASE_URL;
if (process.env.NODE_ENV === 'development') {
BASE_URL = 'http://localhost:3001/api';
} else if (process.env.NODE_ENV === 'production') {
BASE_URL = '/api';
}

export const apiClient = axios.create({


baseURL: BASE_URL,
responseType: 'json',
headers: {
'Content-Type': 'application/json',
},
});

apiClient.interceptors.request.use((config) => {
const user = JSON.parse(localStorage.getItem('loggedUser'));
const token = user && user.token;
config.headers.Authorization = `Bearer ${token}`;
return config;
});
}
Listing 9. Axios call to backend API with interceptors

The App component is where the routing between the different views takes
place. As it can be seen in Listing 10, components from “react-router-dom”
package have been imported and made the SPA able to switch between
different routes.
27

import './App.global.css';
import dgTheme from './configs/dgThemes';
import PrivateRoute from './components/PrivateRoute';
import { AuthRoutes, NonAuthRoutes } from './routes/paths';
import Auth from './layout/Auth';

const loading = <CircularProgress />;


const DefaultLayout = React.lazy(() => import('./layout/DefaultLayout'));

export default function App() {


return (
<ThemeProvider theme={dgTheme}>
<React.Suspense fallback={loading}>
<Switch>
<Route exact path={NonAuthRoutes.login} component={Auth} />
<PrivateRoute path={AuthRoutes.admin} component={DefaultLayout} />
</Switch>
</React.Suspense>
</ThemeProvider>
);
}
Listing 10. App.jsx code snippet

For fulfilling the requirements for good practice, all the routes after authentication
are put in the “AppContent” component where they are mapped through in a
reusable fashion. Routes are stored in a “routes” file and imported to
“AppContent”. This simplifies the process when it comes to adding new routes to
the application. This can be seen in Listing 11.
28

const AppContent = () => {


return (
<Suspense fallback={loading}>
<Switch>
{routes.map((route) => {
return (
route.component && (
<Route
key={route.name}
path={route.path}
exact={route.exact}
name={route.name}
render={(props) => (
<>
<route.component {...props} />
</>
)}
/>
)
);
})}
</Switch>
</Suspense>
);
};
Listing 11. Mapping through different routes in AppContent

Redux Integration

Redux was used to store the state of the app. Redux store keeps the data for
the authenticated user as well as RankCharts component. Two reducers were
used for authentication and rank data and as it is depicted in Listing 12, they
were combined in to make a root reducer.

Redux logger middleware was installed as a npm package to log the state of the
store on the console, as well as Redux Thunk to make Redux capable of returning
functions from action creators. In the end Redux store is exported to be used in
the “index.jsx”, where it wraps the App component with the help of Provider
component from “react-redux” package
29

import { createStore, combineReducers, applyMiddleware, compose } from 'redux';


import thunk from 'redux-thunk';
import logger from 'redux-logger';

import rankReducer from '../reducers/rankReducer';


import { authReducer } from '../reducers/authReducer';

const rootReducer = combineReducers({


ranks: rankReducer,
auth: authReducer,
});

const middlewares = [thunk, logger];

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ||


compose;
const store = createStore(
rootReducer,
composeEnhancers(applyMiddleware(...middlewares))
);

export default store;

Listing 12. Redux store

RankCharts

“RankCharts” component is the main part of the application and is in charge of


letting the user import the “Rank.json” file which contains all the data about the
company’s products in terms of their ranking over the definite time period. The
user can filter through the products based on three categories of pedals,
amplifiers, and cabinets, draw line charts of any number of the products and
export the product data that is shown on the chart as “.csv” format.

“RankCharts” contains two child components. “TypeSelect”, that provide user


with the user interface to import the file as well as to filter and choose products.
Also, “RanksLineChart” that draws the line charts. “TypeSelect” is shown in
Appendix 1.
30

For drawing the charts, React Recharts library was installed with the command
“npm install recharts” and deployed with React component. Listing 13 shows a
code snippet of “RanksLineChart”.

const RanksLineChart = ({ data }) => {


return (
<>
<ResponsiveContainer width="99%" aspect={2}>
<LineChart
data={data}
width="99%"
height={800}
margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<XAxis
type="number"
dataKey="time"
domain={['auto', 'auto']}
tick={{ fill: 'black' }}
tickFormatter={(unixTime) => dayjs(unixTime).format('MMM YYYY')} />
<YAxis
type="number"
domain={[0, 30000]}
label={{
value: 'Rank',
angle: -90,
position: 'insideLeft',
dy: 30,
}} />
<CartesianGrid horizontal="true" vertical="" stroke="#ccc" />
<Tooltip
contentStyle={{ backgroundColor: '#8884d8', color: '#fff' }}
itemStyle={{ color: '#fff' }}
cursor={false}
labelFormatter={(unixTime) => dayjs(unixTime).format('MMM DD')} />
<Legend
payload={data.map((p, index) => ({
id: p.name,
type: 'square',
value: p.name,
color: randomColors[index % 20],
}))} />
{data.map((p, index) => {
return (
<Line
key={p.name}
type="monotone"
data={p.rank}
dataKey="rank"
stroke={randomColors[index % 20]}
strokeWidth="2"
dot={{
fill: '#2e4355',
stroke: '#8884d8',
strokeWidth: 1,
r: 1, }}
activeDot={{
fill: 'white',
stroke: '#8884d8',
strokeWidth: 5,
Listing r:13. 5,}}Code
/>);})} snippet from RanksLineChart
</LineChart>
</ResponsiveContainer>
</>
);
};
31

4.3 Application Analysis

In development environment, running “npm start” in the root of the application will
launch the path “/” which is assigned to the sign in page. Figure 10 shows the
sign in page.

Figure 10: Application login page

After the successful authentication, the “DefaultLayout” component will load,


which contains all the routes to different views in the application. Dashboard
component will be shown, and the user can navigate between different pages
via the Drawer on the left side. A screenshot of the dashboard is shown in
Figure 11.

By navigating to sub-menus of Utilities on the drawer, users can reach the


RankCharts view, where they can import and export Rank.json files and draw
the charts. This view can be seen in Figure 12.
32

Figure 11: A view of Dashboard component

Figure 12: A view of RankCharts component


33

5 Conclusion

This thesis was based on the writer’s interest in web development technologies
and Darkglass’ need of an Electron application. The idea to employ the
knowledge of JavaScript and discover new and related topics was the main
source of motivation for the writer. The requirements of the proposed
application by the company were fulfilled to a reasonable extent. The user can
import JSON files of Rank data, filter the data to see a depiction of the products
ranks over time as line charts and export them as .csv file to be used in other
applications. Moreover, the working on new technologies and discussing issues
with the team provided a learning environment, that can be beneficial in further
developments.

However, during the process, challenges of Electron application development


became clearer. The assumption before conducting this research and
developing the application was that the learning curve of Electron js and its
integration with React applications should not be too steep. Although, it was
proven that the severity of challenges to be proportional to the size and
complexity of the project.

It can be suggested that this project can be enhanced by adding testing,


packaging for production, and migrating from JavaScript to TypeScript. These
can be considered as next stages of this application development.

Figure 6. Creating the bookmarks using headings.


34

References

Beginning Elm (no date) What is a Single-Page App? Available at:


https://elmprogramming.com/what-is-a-single-page-app.html (Accessed 16
September 2021)

Johansson, D. (2020) Building maintainable web applications using React: An


evaluation of architectural patterns conducted on Canvas LMS [online].
Available at: https://www.diva-
portal.org/smash/get/diva2:1415320/FULLTEXT01.pdf (Accessed 16
September 2021)

Gordon, E. (2018) Isomorphic Web Applications: Universal Development with


React [online]. Available at: https://learning.oreilly.com/library/view/isomorphic-
web-applications/9781617294396/kindle_split_011.html (Accessed 16
September 2021)

Goyal, Antariksh (2021) Best CSS Frameworks to Look Forward In 2021.


Available at: https://www.lambdatest.com/blog/best-css-frameworks-2021/
(Accessed 10 September 2021)

HTML.COM (no date) HTML5 Basics for Everyone Tired Of Reading About
Deprecated Code. Available at: https://html.com/html5/#What_is_HTML5
(Accessed 10 September 2021)

Java T Point (no date) What is a Web Application? Available at:


https://www.javatpoint.com/web-application (Accessed 14 September 2021)

Karnik N. (2018) Introduction to Mongoose for MongoDB. Available at:


https://www.freecodecamp.org/news/introduction-to-mongoose-for-mongodb-
d2a7aa593c57/ (Accessed 20 September 2021)
35

Kinney, S. (2018) Electron in Action [online]. Available at:


https://learning.oreilly.com/library/view/electron-in-
action/9781617294143/kindle_split_010.html (Accessed 17 September 2021)

Mardan, A. (2014) Pro Express.js [online]. Available at:


https://learning.oreilly.com/library/view/pro-express-js/9781484200377/
(Accessed 17 September 2021)

MDN Web Docs (no date) Element. Available at:


https://developer.mozilla.org/en-US/docs/Glossary/Element (Accessed 10
September 2021)

MDN Web Docs (no date) HTML basics. Available at:


https://developer.mozilla.org/en-
US/docs/Learn/Getting_started_with_the_web/HTML_basics (Accessed 10
September 2021)

Michael, John (2019) Evolution of JavaScript. Available at:


https://medium.com/@uzendujmike/evolution-of-javascript-d1fe967bcadb
(Accessed 13 September 2021)

nodejs (no date), Introduction to Node.js. Available at: https://nodejs.dev/learn


(Accessed 17 September 2021)

npm trends (no date) angular vs jquery vs react vs vue. Available at:
https://www.npmtrends.com/angular-vs-jquery-vs-react-vs-vue (Accessed 13
September 2021)

Perkaz A. (2021) Advanced Electron.js architecture. Available at:


https://blog.logrocket.com/advanced-electron-js-architecture/ (Accessed 16
September 2021)

Pernice E, Albiston C, Beeler R, Chou E, Fry C, Shor M, Spears J, Speck D,


Thakur A, West S. Application Development in the Face of Evolving Web
36

Technologies at the National Ignition Facility. In17th International Conference


on Accelerator and Large Experimental Physics Control Systems
(ICALEPCS'19), New York, NY, USA, 05-11 October 2019 Aug 1 (pp. 1052-
1056). JACOW Publishing, Geneva, Switzerland.

Phaltankar A, Ahsan J, Harrison M, Nedov L (2020) MongoDB Fundamentals


[online]. Available at: https://learning.oreilly.com/library/view/mongodb-
fundamentals/9781839210648/ (Accessed 21 September 2021)

React (no date) Introducing JSX. Available at:


https://reactjs.org/docs/introducing-jsx.html (Accessed 18 September 2021)

React Redux (no date) Hooks. Available at: https://react-redux.js.org/api/hooks


(Accessed 20 September 2021)

react.school (no date) Material UI. Available at: https://react.school/material-ui


(Accessed: 20 September 2021)

Redux (no date) Redux Essentials, Part 1: Redux Overview and Concepts.
Available at: https://redux.js.org/tutorials/essentials/part-1-overview-concepts
(Accessed 20 September 2021)

Sheikh, Sameer (2021) JS Engine, Window and DOM. Available at:


https://medium.com/@samfed/js-engine-window-and-dom-611bbc21ac75
(Accessed 13 September 2021)

Tutorialspoint (no date) What is CSS? Available at:


https://www.tutorialspoint.com/css/what_is_css.htm (Accessed 10 September
2021)
Appendix 1
1 (2)

RankCharts Component Code Listing

import HandleFiles from './HandleFiles';

import {deleteChip, selectChartData, clearChartData,} from '../../../../actions/ranks';

const TypeSelect = ({ data }) => {


const [category, setCategory] = useState('');
const [products, setProducts] = useState([]);

const classes = useStyles();


const theme = useTheme();

const dispatch = useDispatch();

let filteredData = [];

if (category === 'Pedal') {


filteredData = data.products.data.filter((p) => p.type === 'PEDAL');
} else if (category === 'Amplifier') {
filteredData = data.products.data.filter((p) => p.type === 'HEAD');
} else if (category === 'Cabinet') {
filteredData = data.products.data.filter((p) => p.type === 'CAB');
}

const handleChangeCategory = (event) => {


const category = event.target.value;
setCategory(category);
setProducts([]);
dispatch(clearChartData());
};

const handleChangeProduct = (event) => {


const {
target: { value },
} = event;

const productNames = typeof value === 'string' ? value.split(',') : value;

dispatch(
selectChartData(
data.products.data.filter((p) => productNames.includes(p.name))
)
);
setProducts(productNames);
};

const handleDeleteChip = (event) => {


const {
target: { innerText },
} = event;

dispatch(deleteChip(innerText));
setProducts(products.filter((name) => name !== innerText));
};
Appendix 1
2 (2)

import HandleFiles from './HandleFiles';

import {deleteChip, selectChartData, clearChartData,} from '../../../../actions/ranks';

const TypeSelect = ({ data }) => {


const [category, setCategory] = useState('');
const [products, setProducts] = useState([]);

const classes = useStyles();


const theme = useTheme();

const dispatch = useDispatch();

let filteredData = [];

if (category === 'Pedal') {


filteredData = data.products.data.filter((p) => p.type === 'PEDAL');
} else if (category === 'Amplifier') {
filteredData = data.products.data.filter((p) => p.type === 'HEAD');
} else if (category === 'Cabinet') {
filteredData = data.products.data.filter((p) => p.type === 'CAB');
}

const handleChangeCategory = (event) => {


const category = event.target.value;
setCategory(category);
setProducts([]);
dispatch(clearChartData());
};

const handleChangeProduct = (event) => {


const {
target: { value },
} = event;

const productNames = typeof value === 'string' ? value.split(',') : value;

dispatch(
selectChartData(
data.products.data.filter((p) => productNames.includes(p.name))
)
);
setProducts(productNames);
};

const handleDeleteChip = (event) => {


const {
target: { innerText },
} = event;

dispatch(deleteChip(innerText));
setProducts(products.filter((name) => name !== innerText));
};

You might also like

pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy