Frameworks

Tutorial de Electron: Creación de la sala de chat

En esta segunda parte del Tutorial de Electron, veremos el concepto de eventos en profundidad y crearemos nuestras sala de chat de nuestra aplicación

Publicado el 19 de Enero de 2018
Compartir

En el artículo anterior aprendimos detalles básicos sobre Electron y comenzaron a preparar nuestro entorno para trabajar con el proyecto. En este artículo vamos a ver como implementar nuestra primera y fundamental parte de OpenChat la cual es la sala de chat. Para ello crearemos dos componentes que permitirán ver los mensajes de la base de datos y enviar mensajes a la base de datos de Firebase. Para ello recibiremos y enviaremos toda esta información al backend mediante eventos de Electron y este, se los enviará a Firebase. Puedes pensar que en este caso podríamos mandarlo directamente desde React y no estás equivocado pero así tendremos la oportunidad de aprender eventos y la comunicación entre los diferentes canales que Electron tiene.

Ten en cuenta que no siempre podrás escribir datos desde el Front como en Firebase, es más, en la mayoría de ocasiones, si trabajas con una base de datos directamente te tocará pasarlo por el backend pues es tanto una medida de seguridad como la manera correcta de trabajar con lectura y escritura de datos.

Concepto: Eventos

Todos lo relacionado con Eventos en Electron hereda de la clase EventEmitter. Como hijos de esta clase tenemos dos módulos llamados ipcMain y ipcRenderer.

Con ipcMain podremos, desde el back escuchar a eventos enviados por el ipcRenderer e incluso contestar a ellos o simplemente actuar cuando estos se produzcan.

Con ipcRenderer podremos, desde el front principalmente enviar eventos al ipcRenderer aunque también podremos escuchar eventos enviados por el ipcMain.

El módulo por defecto para enviar eventos es el ipcRenderer es decir desde el Front al Back, usando ipcRenderer.send('mi-evento', [...]). A pesar de ello dentro de un listener en ipcMain podremos enviar eventos para contestar a estos. Lo veremos a lo largo de el artículo.

Estos módulos permiten intercambiar cualquier tipo de comunicación entre los dos ambiantes. Por ejemplo, si quieremos acceder a un archivo que está en el ordenador del usuario no podremos hacerlo desde el Front pues necesitamos el módulo de fs. La solución como ya habrás deducido sería enviar un evento desde el ipcRenderer que el ipcMain recogerá, ejecutará lo que crea conveniente, en este caso las funciones del módulo fs y retornará el resultado, el cual el ipcRenderer estará escuchando.

En nuestro caso usamos Firebase por lo que todo esto dejaría de tener sentido, para ello lo que vamos a hacer es usar tanto la base de datos de Firebase como el servicio de Storage desde el back, ya que es posible y de esta manera podremos trabajar los eventos.


Veremos esto de una manera más práctica a lo largo de este artículo así que no será un problema para ti.

Paso 1: Crear el componente para mandar mensajes

En nuestra estructura del proyecto, añadiremos un componente llamado MessageBox que no será más que un input con un botón que al hacer click en este obtendremos el mensaje para luego poder enviarlo. A mi me quedó algo como lo siguiente:

import React from 'react'

class SendBox extends React.Component {
    constructor(props) {
        super(props)

        this.sendMessage = this.sendMessage.bind(this)
    }
    sendMessage() {
        const messageObject = {
            message: document.getElementById('send-message').value
        } // Preparamos el objeto Mensaje para luego mandarlo con eventos.
    }
    render() {
        return(
            <div className="grid-x grid-margin-x">
                <input type="text" id="send-message" class="large-11 cell" placeholder="Escribe tu mensaje aqui" />
                <a className="button success large-1 cell" onClick={this.sendMessage}>Enviar</a>
            </div>
        )
    }
}

export default SendBox

Si te fijas, hemos añadido diversas clases que nos ayudaran a darle un poco de estilo a la aplicación pues no son más que las de Foundation.

Paso 2: Crear el componente para recibir mensajes

En este caso, deberemos de empezar a jugar con el estado y mostrar diferentes elementos en el caso de que haya mensajes y no. Basándome en Slack se me ocurrió la idea de poner alguna frase ingeniosa cuando estos no estén o estén cargando. Al final me decanté por añadir un: “Que bien sabe un café mientras los mensajes cargan…”

import React from 'react'

class MessagesBox extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            messages: null
        }
    }
    render() {
        if(this.state.messages != null) {
            return(
                <div>
                    {
                        this.state.messages.map((message) => (
                            <Message message={message} />
                        ))
                    }
                </div>
            )
        } else {
            return(
                <h4 className="text-center subheader">Que bien sabe un café mientras los mensajes cargan...</h4>
            )
        }
    }
}

export default MessagesBox

Si te fijas, cuando hay mensajes, con el fin de modularizar el proyecto, usaremos otro componente llamado <Message> que simplemente muestra el mensaje. Algo como:

import React from 'react'

function Message(props) {
    return(
        <div className="Message row">
            <img src={props.message.userURL} className="large-2 columns"/>
            <div className="large-10 columns">
                <b>{props.message.userName}</b>
                <p>{props.message.message}</p>
            </div>
        </div>
    )
}

export default Message

Paso 3: Enviar y solicitar mensajes usando Eventos de Electron

A todos los componentes que acabamos de crear, vamos a aplicarles eventos, tanto para enviar un mensaje y grabarlo en la base de datos(newMessage) como para solicitar todos y cada uno de los mensajes que haya en la base de datos(requestMessages) y mostrarlos en las vista de nuestra aplicación(receiveMessages). Este último será la respuesta del ipcMain.

Imagen 0 en Tutorial de Electron: Creación de la sala de chat

El evento newMessage

Entramos en el componente el cual queremos accionar el envío de este evento, SendBox y vamos a enviar el evento después del click en el botón. Junto a el envío del evento irá el objeto del mensaje. Debería de quedarte algo como lo siguiente:

import React from 'react'
import { ipcRenderer } from 'electron' // Traemos el módulo para mandar el evento.

class SendBox extends React.Component {
    constructor(props) {
        super(props)

        this.sendMessage = this.sendMessage.bind(this)
    }
    sendMessage() {
        const messageObject = {
            message: document.getElementById('send-message').value,
            userURL: 'https://avatars.slack-edge.com/2017-11-27/279122939639_c199c00a34366734b118_72.jpg',
            userName: 'Miguh Ruiz'
        }

        ipcRenderer.send('newMessage', messageObject) // Enviamos el evento con el objeto de mensaje.
    }
    render() {
        return(
            <div className="grid-x grid-margin-x">
                <input type="text" id="send-message" className="large-11 cell" placeholder="Escribe tu mensaje aqui" />
                <a className="button success large-1 cell" onClick={this.sendMessage}>Enviar</a>
            </div>
        )
    }
}

export default SendBox

En el archivo principal de Electron, localizado en la raíz, index.js, añadiremos el siguiente listener del evento:

ipcMain.on('newMessage', (ev, objectMessage) => {
    // Enviar el mensaje a la base de datos de Firebase.
})

El evento requestMessages y receiveMessages

Tal como hemos hecho en el evento anterior, desde el componente, en esta ocasión MessagesBox vamos a enviar el evento requestMessages y a añadir un listener de receiveMessages. Quedaría algo como:

import React from 'react'
import { ipcRenderer } from 'electron'

import Message from '../Message/index.jsx'

class MessagesBox extends React.Component {
    constructor(props) {
        super(props)
        this.state = {
            messages: []
        }
    }
    componentDidMount() {
        ipcRenderer.send('requestMessages')

        ipcRenderer.on('receiveMessages', (ev, messages) => {
            this.setState({ messages }) // Los mensajes traidos desde ipcMain se ponen en el state para cambiar la vista.
        })
    }
    render() {
        if(this.state.messages.length > 0) {
            return(
                <div>
                    {
                        this.state.messages.map((message) => (
                            <Message message={message} />
                        ))
                    }
                </div>
            )
        } else {
            return(
                <h4 className="text-center subheader">Que bien sabe un café mientras los mensajes cargan...</h4>
            )
        }
    }
}

export default MessagesBox

En el archivo de Electron, tendríamos que añadir un listener de requestMessages y en este, como parte del callback mandar un evento, que se llamará receiveMessages, algo tal que:

ipcMain.on('requestMessages', (ev) => {
    // Pedir mensajes a la base de datos de Firebase.
    ev.sender.send('receiveMessages', [])
})

Paso 4: Acceder y escribir datos en Firebase

En el último paso vamos a escribir en los callbacks de nuestro archivo de Electron las funciones para escribir y leer la base de datos. Te recomiendo repasar Construir API con Firebase Cloud functions en la que vimos funciones de firebase en firebase-admin, el módulo para trabajar con Firebase desde Node.JS.

$ npm install firebase-admin

Una vez tengamos el módulo anterior instalamos lo inicializamos en el archivo raíz de Electron:

const admin = require('firebase-admin')
let serviceAccount = require('./firebase_key.json')// Se obtiene desde el panel de configuración, en formato JSON

admin.initializeApp({
    credential: admin.credential.cert(serviceAccount),
    databaseURL: 'https://openchat17.firebaseio.com'
})

Si te fijas, en el objeto de configuración de firebase(admin.initializeApp(firebaseConfObject)) tenemos una propiedad credential la cual certifica un archivo JSON con información de Firebase. Para obtener éste debemos ir a la configuración del proyecto en Firebase y en la pestaña Cuentas de servicio obtendremos el JSON haciendo click en el botón llamado Generar nueva clave privada.

Escribir datos en la base de datos

ipcMain.on('newMessage', (ev, objectMessage) => {
    const docRef = db.ref('messages')

    docRef.push(objectMessage)
})

Leer datos de la base de datos

ipcMain.on('requestMessages', (ev) => {
    const docRef = db.ref('messages')

    docRef.on('value', snapshot => {
        let messages = []
        snapshot.forEach(message => {
            messages.push(message.val())
        })
        ev.sender.send('receiveMessages', messages)
    })
})

Conclusión

Hemos conseguido añadir componentes a nuestra vista y cargar datos a la misma usando eventos de Electron y la base de datos de Firebase. Si te quedó alguna duda estamos pendiente de resolverlas en los comentarios.


Compartir este post

También te puede interesar...

Tecnología

Ventajas de usar Electron

28 Mayo 2018 Juan David Rodriguez
Electron

Electron: Desarrollo de Apps de Escritorio

5 horas y 16 minutos · Curso

El curso Electron te enseñará los elementos fundamentales del Framework Electrón para desarrollar aplicaciones de escritorio usando tus conocimientos de JavaScript, HTML y CSS usando …

  • Desarrollo Web
Artículos
Ver todos