iT邦幫忙

2021 iThome 鐵人賽

DAY 9
0
Modern Web

摸索 WebSocket,遠望 WebRTC系列 第 9

Day08:Build Private Chat(建立私人頻道)

全文同步於個人 Docusaurus Blog

除了共用的頻道外,當不同使用者進入時,希望能夠出現一個很陽春的不同使用者的聊天頻道,並進行私人訊息。

而這一章首要解決的,便是不同使用者進入時,自動更新建立頻道。

Record User Name

第一步,記錄登入的使用者名稱到 server 端,所以需要先到中介的 handler.js 轉發訊息。透過 register-new-user 這組 socket 和 server-side 傳輸資料。

另外補充,當使用者登入啟動連線後,連線 socket 的 id 也會被保存 store.js 中,方便隨時可以使用。

// handler.js
import store from '../store.js';

const connectSocketIoServer = () => {
  // ...

  socket.on('connect', () => {
    store.setSocketId(socket.id);
    registerActiveSession();
  });
};

const registerActiveSession = () => {
  const userData = {
    username: store.getUserName(),
  };
  socket.emit('register-new-user', userData);
};

預先保留狀態使用的 function。

// store.js
let socketId = null;
let activeChatGroup = [];

const getSocketId = () => socketId;
const setSocketId = (id) => (socketId = id);
const getActiveChatGroup = () => activeChatGroup;
const setActiveChatGroup = (chatGroup) => (activeChatGroup = chatGroup);

export default {
  // ...
  getSocketId,
  setSocketId,
  getActiveChatGroup,
  setActiveChatGroup,
};

Server

當 server-side 透過監聽拿到資料後,可以組出一個物件,包含使用 socket.id 當作唯一值的 key,以及 username。每登入一個使用者,就建立一組物件,並將這些物件透過其餘的方式,push 到外層宣告的陣列 connectPeers,最後再經 boardcastConnectedPeers() 將陣列作為參數轉發回 handler.js。

// server.js
let connectPeers = [];

io.on('connection', (socket) => {
  // ...

  socket.on('register-new-user', (userData) => {
    const { username } = userData;

    const newPeer = {
      username,
      socketId: socket.id,
    };

    // use spread copy
    connectPeers = [...connectPeers, newPeer];
    boardcastConnectedPeers();
  });
});

const boardcastConnectedPeers = () => {
  const data = {
    connectPeers,
  };

  io.emit('active-peers', data);
};

handler.js 拿到資料後,會去 client-side 呼叫 updateActiveChatGroup(),並將資料傳輸過去,準備進行 render。

Client

client-side 拿到傳輸過來的資料後進行拆解,首先去 store 調出資料,和 server-side 傳輸過來的資料進行比對,剃除重複的使用者,同時呼叫 element.js 中,用於動態 render HTML 的 getChatList(),將資料作為參數傳入,方便 render 時作為變數使用。

最後將篩選過的物件,同樣透過其餘的方式,push 到 stroe 內保存的陣列,讓下一次使用者登入觸發時,可以再次使用。

const updateActiveChatGroup = (data) => {
  const { connectPeers } = data;
  const userSocketId = store.getSocketId();
  const activeChatGroups = store.getActiveChatGroup();

  connectPeers.forEach((peer) => {
    const isRepeat = activeChatGroups.find((node) => {
      return peer.socketId === node.socketId;
    });

    if (!isRepeat && peer.socketId !== userSocketId) {
      createNewUserChatGroup(peer);
    }
  });
};

const createNewUserChatGroup = (peer) => {
  const chatTitle = peer.username;
  const messageContainerID = `${peer.socketId}-message`;
  const messageInputID = `${peer.socketId}-input`;
  const chatContainerID = peer.socketId;

  const data = {
    chatTitle,
    messageContainerID,
    messageInputID,
    chatContainerID,
  };

  const chatGroup = element.getChatList(data);
  const chatList = document.querySelector('.chat-list');
  chatList.appendChild(chatGroup);

  // push new user to chat group
  const activeChatGroup = store.getActiveChatGroup();
  const newActiveChatGroup = [...activeChatGroup, peer];
  store.setActiveChatGroup(newActiveChatGroup);
};

export default {
  // ...
  updateActiveChatGroup,
};

Private Chat


上一篇
Day07:Boardcast Event(推播事件)
下一篇
Day09:Emit Direct Message I(發送個人訊息到 server 端)
系列文
摸索 WebSocket,遠望 WebRTC30

尚未有邦友留言

立即登入留言