今天我們要開始將 firestore 與聊天室畫面接上關係 ,
利用 IPC 處理 Main Process 與 BrowserWindow 處理資料交換
在 day-09 中我們知道 IPC 交換資訊需要
下方我們就用這 2 大重點 , 進行實作 ![]()
接續 [ Day 10 ] - firestore 介紹 我們整理一下監聽特定房間的行為
預計會有如下的行為
將初始化的過程包裝成一個 init.js
const firebase = require('firebase');
const firebaseConfig = {
    "apiKey": "### FIREBASE API KEY ###",
    "authDomain": "ezoom-test.firebaseapp.com",
    "databaseURL": "https://ezoom-test.firebaseio.com",
    "projectId": "ezoom-test",
    "storageBucket": "ezoom-test.appspot.com",
    "messagingSenderId": "653212361558",
    "appId": "### FIREBASE APP ID ###"
} ;
firebase.initializeApp(firebaseConfig);
const db = firebase.firestore();
module.exports = db;
進入特定的聊天室
const ObserveUtils = {
  /**
   * 回傳一個聊天室的 observer
   *
   * below are event on the observer , use .on( 'event' , data ) to handle it
   *
   * - new-message : trigger when an user send new message
   * - update-message : trigger when an user change a message , it have sent
   * - delete-message : trigger when an user delete a message , it have sent
   *
   * @param roomId
   * @returns {module:events.EventEmitter.EventEmitter}
   */
  observeRoom: roomId => {
      // ...    
  },
}
監聽聊天室的談話
利用 EventEmitter 做出一個回傳 observer 的 ObserveUtils.observeRoom 函式
// in ObserveUtils.observeRoom function 
const emitter = new EventEmitter();
// 監聽 chat 的變化 => /chatroom/${roomId}/message 中的聊天訊息串
const collect = db.collection('chatroom').doc(roomId).collection('message');
const observer = collect.onSnapshot(docSnapshot => {
    docSnapshot.docChanges().forEach(change => {
        const type = change.type;
        const data = change.doc.data();
        if (type === 'added') emitter.emit('new-message', data);
        if (type === 'modified') emitter.emit('update-message', data);
        if (type === 'removed') emitter.emit('delete-message', data);
    });
}, err => emitter.emit('error', err));
經過上方三步驟,我們可以包裝出一個 ObserveUtils 讓監聽某房間的訊息更加容易
監聽可以包裝成 ObserveUtils 那發送訊息也可以整理成 SenderUtils
// sender.js
const db = require('./init');
// _uuid() 的參考資料 : https://cythilya.github.io/2017/03/12/uuid/
function _uuid() {
    var d = Date.now();
    if (typeof performance !== 'undefined' && typeof performance.now === 'function') {
        d += performance.now(); //use high-precision timer if available
    }
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = (d + Math.random() * 16) % 16 | 0;
        d = Math.floor(d / 16);
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}
const SenderUtils = {
    addMessage: async (roomId, message = {}) => {
        const uuid = _uuid();
        const chatRef = db.collection('chatroom').doc(roomId).collection('message');
        return await chatRef.doc(uuid).set({...message, uuid, create_at: new Date()});
    },
    addMessages: async (roomId, messages = []) => {
        return await Promise.all(messages.map(msg => SenderUtils.addMessage(roomId, msg)));
    },
    updateMessage: async (roomId, message = {}) => {
        const uuid = message.uuid;
        const chatRef = db.collection('chatroom').doc(roomId).collection('message');
        return await chatRef.doc(uuid).update(message);
    },
    deleteMessage: async (roomId, uuid) => {
        const chatRef = db.collection('chatroom').doc(roomId).collection('message');
        return await chatRef.doc(uuid).delete();
    },
}
module.exports = SenderUtils;
我們用一個 firestoreUtils 整合一下 observe 跟 sendMessage 行為
// firestoreUtils
module.exports = {
    observer: require('./observer'),
    sender: require('./sender'),
};
完成 firestoreUtils 代表我們已經包裝了一個 firestore 的工具檔 , 方便之後做使用 , 下方說明在畫面中使用的方式
於畫面中使用
在 background.js 中我們需要加上 start-observe 與 add-message 2 個 ipcMain 的 channel
// in background.js
import firestoreUtils from './firestore/firestoreUtils';
// 開始監聽 chatroom 的訊息
ipcMain.on('start-observe', (event, roomId) => {
    firestoreUtils.observer.observeRoom(roomId)
        .on('new-message', msg => event.reply('new-message', msg))
        .on('update-message', msg => event.reply('update-message', msg))
        .on('delete-message', msg => event.reply('delete-message', msg));
});
// 新增訊息
ipcMain.on('add-message', (event, {roomId, message}) => {
    firestoreUtils.sender.addMessage(roomId, message)
        .catch(e => console.error(e));
});
然後 , 在 Chatroom.vue 的 mounted 中追加 window.ipcMain
// in Chatroom.vue
mounted() {
            window.ipcRenderer.on('new-message', (event, msg) => this.chats.push(msg));
            window.ipcRenderer.send('start-observe', 'init-room');
        },
當然 , 送出訊息時 , 要用 ipcRenderer 呼叫 add-message 這個 channel 來新增訊息
// in Chatroom.vue
submit() {
    if (this.text) {
        const msg = {
            name: '你',
            team: 'right',
            avatar: 'cat-3.png',
            msg: this.text
        }
        window.ipcRenderer.send('add-message', {roomId: 'init-room', message: msg});
        this.text = ''; // reset input to empty 
    }
}
然後我們就可以送訊息給聊天室與接收訊息了 !
今年小弟第一次參加 `鐵人賽` , 如文章有誤 , 請各位前輩提出指正 , 感謝  <(_ _)>