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


<<:  [Day 10] Reactive Programming - Reactor (generate & create)

>>:  用资料结构看 evernote - 修改前 - DAY 10

EP 21: Custom Launch Screen for iOS

Hello, 各位 iT邦帮忙 的粉丝们大家好~~~ 本篇是 Re: 从零开始用 Xamarin 技...

找LeetCode上简单的题目来撑过30天啦(DAY29)

题号:59 标题:Spiral Matrix II 难度:Medium Given a positi...

最简单的 Google Maps 嵌入方式 | 专案实作

工具有许多种,不过随着专案大小与需求不同,搭配适合的实作方式才能达到最佳效益,常见的 Google ...

关於封装

什麽是封装 In object-oriented programming (OOP), encaps...

day 24 - 失控的浮点数, decimal套件介绍

在写程序的过程, 多多少少会遇到需要复杂处理的状况, Go的优点是很多使用情境已经有前人帮忙整理成套...