分享

Create A Cross-Platform Desktop Ripple XRP Wallet With Vue.js And Electron

 quasiceo 2018-08-16

Create A Cross-Platform Desktop Ripple XRP Wallet With Vue.js And Electron

As you are probably aware, I’ve been interested in cryptocurrency and the blockchain lately. I haven’t necessarily been buying anything, but I’ve been doing a lot of watching as it has all been very interesting so far.

I decided, a few weeks ago, to purchase some Ripple (XRP) because it seems like it could do great things for the banking industry. When it came to keeping track of my XRP, there wasn’t really anything available. There is Ripple Wallet , but after having used it for a little bit, it seemed buggy. That got me thinking. What if I were to build my own wallet?

Ripple actually released a Node.js library called ripple-lib on GitHub which makes it very easy to interact with XRP. What makes things even better is that the cross-platform desktop application framework, Electron , uses Node.js.

We’re going to see how to create a cross-platform desktop application using Node.js, Vue.js, and Electron to keep track of our Ripple and how much our wallet is worth.

Before we jump into actually building something, let’s stop and think about what we plan on building. Take a look at the application screenshot below:

Using the Vue.js JavaScript framework and the Bootstrap CSS framework, we plan to create an attractive frontend application. While we could, the frontend won’t be doing any heavy lifting with the Ripple network. Instead, we’ll be using Node.js inside of Electron to make requests against a service.

If at any time during this tutorial you feel like you’d like to donate some XRP to me, my public address is r3qpzJaUnhRqBgvXwC26Mk6jGpRi89Aoin . While not necessary, I’d be very grateful of anything that came my way.

Creating a Vue.js Web Application with the Vue CLI

To get things started, we’re going to create a fresh Vue.js project. The easiest way to do this is through the Vue CLI . With the Vue CLI installed, execute the following:

vue init simple xrp-wallet

Notice that we’re scaffolding a basic project, not one with Webpack or similar. There is a lot of preparation work necessary when trying to use Webpack with Electron. It is a project best saved for another day.

We’re not quite ready to start developing our desktop application, but we need to get Bootstrap set up. Download Bootstrap and copy the files into a directory within the project called static . It won’t already exist, but go ahead and create it. You’ll also want to create a static/css/app.css file for our custom styles.

At this point in time your project structure should look like the following:

static
    css
        bootstrap.min.css
        app.css
        // ...
    fonts
        // ...
    js
        // ...
index.html

There are plenty of files that come with Bootstrap, but we’re only concerned with knowing about the bootstrap.min.css file. It will use various fonts, but we won’t need to know about it.

Open the project’s index.html file and make it look like the following:

    
        
        
        
        
    
    
        

        
            // ...
        
    

What we’re really after in the above HTML markup is the imports of our CSS files. Basically ignore everything else that is going on for now. Just include the following lines:

Make sure you use relative paths when referencing files. Absolute paths will likely cause problems with how Electron renders things.

Integrating Electron in the Vue.js Web Application

At this point in time we should have a Vue.js application created that is configured to use Bootstrap as our CSS framework. Both Vue.js and Bootstrap cover half the technologies used. The other half will use Node.js.

To initialize our project to use Node.js, execute the following from the command line:

npm init -y

The next step is to get Electron into our new Node.js project. From the command line, execute the following command to install the necessary Electron package:

npm install electron --save-dev

Since we’ll be working with Electron as a local dependency rather than a global dependency, we should probably create a script for it.

Open the project’s package.json file and include the following:

"scripts": {
    "test": "echo "Error: no test specified" && exit 1",
    "electron": "./node_modules/.bin/electron ."
},

Our project is now suitable for Node.js, Vue.js, and Electron development, but we need to create a logic file for our project. At the root of our project, create a main.js file with the following JavaScript:

const {app, BrowserWindow } = require('electron')
const path = require('path')
const url = require('url')

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win

function createWindow () {
    // Create the browser window.
    win = new BrowserWindow({width: 800, height: 600})

    // and load the index.html of the app.
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
    }))

    // Emitted when the window is closed.
    win.on('closed', () => {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        win = null
    })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
        createWindow()
    }
})


// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

If the above JavaScript looks familiar, it is because it was taken directly from the Electron Quick Start . The main.js file will have all of our “backend” Node.js logic and the index.html file will have all of our “frontend” Vue.js HTML markup and JavaScript code.

For a sanity check, our project should have the following structure:

static
    css
        bootstrap.min.css
        app.css
        // ...
    fonts
        // ...
    js
        // ...
index.html
main.js
package.json

Now we can worry about adding our Node.js logic which will do the heavy lifting when it comes to Ripple.

Communicating with the CoinMarketCap API and the Ripple Servers

When it comes to our application, we’ll be using the CoinMarketCap API to track the price of XRP and other metrics about it. We’ll be using ripple-lib to track information about our wallet.

To make this possible, we need a few Node.js dependencies. From the command line, execute the following:

npm install ripple-lib request --save

The request package will allow us to make HTTP requests to the CoinMarketCap API from Node.js. More information on making HTTP requests in Node.js can be found in a previous tutorial I wrote on the topic titled, Consume Remote API Data from within a Node.js Application .

Open the project’s main.js file so we can import and configure the dependencies:

const RippleAPI = require("ripple-lib").RippleAPI;
const Request = require("request");

With dependencies imported, we need to initialize them. In reality, we just need to define the Ripple servers for later use. This can be done by doing the following:

const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const address = 'r3qpzJaUnhRqBgvXwC26Mk6jGpRi89Aoin';

The address variable is the public address of your XRP wallet. While the ripple-lib package could create an address for us, I already had one that I wanted to use.

At the bottom of your main.js file, we can create the functions that will get our wallet value and the market value of Ripple:

// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

exports.address = address;

exports.getRippleWalletValue = () => {
    return new Promise((resolve, reject) => {
        api.connect().then(() => {
            api.getBalances(address).then(balances => {
                resolve(balances);
            }, error => {
                reject(error);
            });
        }, error => {
            reject(error);
        });
    });
}

exports.getRippleMarketValue = function() {
    return new Promise((resolve, reject) => {
        Request.get("https://api./v1/ticker/ripple/", (error, response, body) => {
            if(error) {
                reject(error);
            }
            resolve(JSON.parse(body));
        });
    });
}

By exporting these functions we’ll be able to access them from our Vue.js code. Our Vue.js code will call these functions and render the result in the HTML.

The full main.js file should look like the following:

const {app, BrowserWindow } = require('electron')
const path = require('path')
const url = require('url')
const RippleAPI = require("ripple-lib").RippleAPI;
const Request = require("request");

const api = new RippleAPI({server: 'wss://s1.ripple.com:443'});
const address = 'r3qpzJaUnhRqBgvXwC26Mk6jGpRi89Aoin';

// Keep a global reference of the window object, if you don't, the window will
// be closed automatically when the JavaScript object is garbage collected.
let win

function createWindow () {
    // Create the browser window.
    win = new BrowserWindow({width: 800, height: 600})

    // and load the index.html of the app.
    win.loadURL(url.format({
        pathname: path.join(__dirname, 'index.html'),
        protocol: 'file:',
        slashes: true
    }))

    // Emitted when the window is closed.
    win.on('closed', () => {
        // Dereference the window object, usually you would store windows
        // in an array if your app supports multi windows, this is the time
        // when you should delete the corresponding element.
        win = null
    })
}

// This method will be called when Electron has finished
// initialization and is ready to create browser windows.
// Some APIs can only be used after this event occurs.
app.on('ready', createWindow)

// Quit when all windows are closed.
app.on('window-all-closed', () => {
    // On macOS it is common for applications and their menu bar
    // to stay active until the user quits explicitly with Cmd + Q
    if (process.platform !== 'darwin') {
        app.quit()
    }
})

app.on('activate', () => {
    // On macOS it's common to re-create a window in the app when the
    // dock icon is clicked and there are no other windows open.
    if (win === null) {
        createWindow()
    }
})


// In this file you can include the rest of your app's specific main process
// code. You can also put them in separate files and require them here.

exports.address = address;

exports.getRippleWalletValue = () => {
    return new Promise((resolve, reject) => {
        api.connect().then(() => {
            api.getBalances(address).then(balances => {
                resolve(balances);
            }, error => {
                reject(error);
            });
        }, error => {
            reject(error);
        });
    });
}

exports.getRippleMarketValue = function() {
    return new Promise((resolve, reject) => {
        Request.get("https://api./v1/ticker/ripple/", (error, response, body) => {
            if(error) {
                reject(error);
            }
            resolve(JSON.parse(body));
        });
    });
}

For this particular XRP wallet application, the Node.js logic is quite minimal. Getting the market value and wallet value will allow us to accomplish quite a bit.

Now we can start to string things together with Vue.js and HTML.

Developing the Vue.js Logic and HTML UI

The client facing part of our application can be broken into three parts. We’ll have an HTML layer, a JavaScript layer, and a custom CSS layer that goes beyond Bootstrap.

We’re going to start with the JavaScript logic. Open the project’s index.html file and include the following within the tags:

    const { remote } = require('electron');
    const mainProcess = remote.require('./main.js');

    var app = new Vue({
        el: '#app',
        data: {
            address: "",
            ripple_coin: {},
            ripple_wallet: {
                xrp: 0.00,
                usd: 0.00
            }
        },
        mounted() {
            this.address = mainProcess.address;
            mainProcess.getRippleMarketValue().then(result => {
                this.ripple_coin = result[0];
                mainProcess.getRippleWalletValue().then(result => {
                    this.ripple_wallet.xrp = result[0].value;
                    this.ripple_wallet.usd = (this.ripple_wallet.xrp * this.ripple_coin.price_usd).toFixed(2);
                });
            });
        },
        methods: { }
    });

The first thing we do in the above JavaScript is import a few things that will allow us to use the previously exported functions within the main.js file.

The data object is where we initialize the variables that we plan to use throughout the application. The address variable will map to the address that we had set in the main.js file. The ripple_coin will represent the market data for Ripple and the ripple_wallet variable will hold information obtained from the ripple-lib package.

When the mounted method is called when the application loads, a few things will happen. We’ll get the market value of XRP by calling the getRippleMarketValue function and when the promise resolves we’ll get the wallet value by calling the getRippleWalletValue function.

this.ripple_wallet.usd = (this.ripple_wallet.xrp * this.ripple_coin.price_usd).toFixed(2);

There is no way to directly determine the USD price of any number of XRP, so we manually calculate it. With all of our data loaded from the Node.js functions, we can worry about rendering it on the screen.

    本站是提供个人知识管理的网络存储空间,所有内容均由用户发布,不代表本站观点。请注意甄别内容中的联系方式、诱导购买等信息,谨防诈骗。如发现有害或侵权内容,请点击一键举报。
    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多