review

React Builds the chat interface

SpringBoot chat message persistence

Netty – socketio SpringBoot integration

Websocket

WebSocket protocol was born in 2008 and became an international standard in 2011. All browsers already support it. Its biggest characteristic is that the server can take the initiative to push information to the client, the client can also take the initiative to send information to the server, is a real two-way equal dialogue, belongs to a server push technology.

  • Reference: WebSocket tutorial www.ruanyifeng.com/blog/2017/0…

Implementation effect

Train of thought

  • Use the socket to connect to the server
  • The client sends a message to the server
  • The client listens for socket events
  • The server pushes the message to the client

The chat list is displayed

The service definition

  • Call/API/v1 / message/record/list
export async function getMessageRecordList(params) {
  return request("/api/v1/message/record/list", {
    method: "POST".requestType: "form".data: params,
  });
}
Copy the code

Chat model definition


effects: {*getMessageRecordList({ payload }, { call, put }) {
      const response = yield call(getMessageRecordList);
      yield put({
        type: 'updateMessageRecordList'.payload: {
          messageRecordList: response.data,
        },
      });
    },

Copy the code

Establish a connection

  • usesocket.io-clientThe module connects to the socket

Install the module

NPM install [email protected] - saveCopy the code
"socket.io-client": "^ 1.7.4." "
Copy the code

TODO: With 1.7.4, there may be problems with 2.x

Use in ChatLayout

Encapsulation method

// After successful connection, get chat list of current logged-in user
const getMessageRecordList = () = > {
  dispatch({ type: "chat/getMessageRecordList" });
};

const connectImServer = () = > {
  try {
    // Adjust the login interface without token
    let accessToken =
      localStorage.getItem(MOOSE_REACT_LEARN_ACCESS_TOKEN) || "";
    if(! accessToken) { redirectLogin();return;
    }

    const socket = require("socket.io-client") (`http://localhost:9000`, {
      transports: ["websocket"].query: { userId, access_token: accessToken },
    });

    // Establish a connection
    socket.on("connect".() = > {
      console.log("connect:: ");

      // Connect successfully, call back to chat list
      getMessageRecordList();
    });

    // Error sending
    socket.on("error".(error) = > {
      console.log("error:: ", error);
    });

    // Connection error
    socket.on("connect_error".(error) = > {
      console.log("connect_error:: ", error);
    });

    // Disconnect the connection
    socket.on("disconnect".(reason) = > {
      console.log("disconnect:: ", reason);
    });

    // Listen to receive server push messages
    socket.on("SINGLE_CHAT".(message) = > {
      console.log("The client received a message:", message);
      // Trigger dVA data saving
      dispatch({ type: "chat/receiverMessage".payload: { message } });

      // Scroll to the bottom
      scrollToBottom("bottomElement"."chatItems");
    });

    // Save connection socket information
    dispatch({ type: "chat/saveServerInfo".payload: { socket } });
  } catch (error) {
    console.log(error); }};Copy the code

call

useEffect(() = > {
  connectImServer();
  return () = >{}; } []);Copy the code

Use DVA to save the connection state

  • models/chat.js
    saveServerInfo(state, { payload: { socket } }) {
      return { ...state, socket: socket };
    },

    /** * Update chat list */
    updateMessageRecordList(state, { payload: { messageRecordList } }) {
      return { ...state, messageRecordList };
    },

    /** * Updates the current chat list */
    updateMessageChatList(state, { payload: { messageChatList } }) {
      return { ...state, messageChatList };
    },
Copy the code

Click to select chat partner

// Click to switch the current chat object
const onChangeChatCurrentUser = (item) = > {
  // Determine the chat object according to the current sendId and receiveId
  let chatId = getDiffChatId(item, userId);
  // Switch the current chat object
  dispatch({ type: "chat/chatCurrentUser".payload: { chatUserInfo: item } });
  // Get the chat records of the current chat object
  dispatch({ type: "chat/getMessageChatList".payload: { chatId } });
};
Copy the code

Get chat details

The service definition

export async function getMessageChatList(params) {
  return request("/api/v1/message/chat/list", {
    method: "POST".requestType: "form".data: params,
  });
}
Copy the code

Chat model definition


/** * get chat request */
*getMessageChatList({ payload }, { call, put }) {
  const { chatId } = payload;
  const response = yield call(getMessageChatList, { chatId });
  console.log(response);
  if (response.code === 200) {
    yield put({
      type: 'updateMessageChatList'.payload: {
        messageChatList: response.data, }, }); }},Copy the code

Distinguish the user Id of the selected chat object, sender, and receiver

/** * Distinguish between chat object and current logged-in user *@param {*} ChatItem Chat records *@param {*} UserId Id of the current login user *@returns* /
export const getDiffChatId = (chatItem, userId) = > {
  const { sendId, receiveId } = chatItem;
  if (userId === sendId) {
    return receiveId;
  }
  if (userId === receiveId) {
    return sendId;
  }
  return "";
};
Copy the code

Send a message

  • Click the Send button to send
  • Assemble the message template
  • The socket send

Click button logic

const onSendMessage = () = > {
  if(! receiveId) { message.error("Please choose who to talk to.");
    return;
  }

  if(! content) {return;
  }

  let messageTemplate = {
    type: "MS:TEXT".chatType: "CT:SINGLE",
    content,
    sendId: userId,
    receiveId: receiveId,
  };

  // Chat Model sends messages
  dispatch({ type: "chat/sendMessage".payload: { messageTemplate } });

  // The message scrolls to the bottom
  onMessageScroll();
};
Copy the code

chat model effects sendMessage

*sendMessage({ payload }, { call, put, select }) {
  const { messageTemplate } = payload;

  // the dVA select API gets the corresponding state according to different namespaces
  const chatState = yield select((state) = > state['chat']);

  // Retrieve the socket object from the state --> Save the socket object when establishing the connection
  const { messageChatList = [], socket } = chatState;

  // Assemble the message
  const temp = messageChatList.concat();
  temp.push(messageTemplate);

  // Refresh the chat list
  yield put({ type: 'refreshChatList'.payload: { messageChatList: temp } });

  // Situation input box
  yield put({ type: 'chatInputMessageChange'.payload: { content: null}});if(! socket)console.warn('Socket does not exist, need to log in again, please check the socket connection. ');

  // Send messages (this event is pre-agreed with the server)
  if (socket) socket.emit('SINGLE_CHAT', messageTemplate);
  console.log(chatState);
},

Copy the code

Receives the message

  • After you select a chat partner, listen for chat messages
  • The message is parsed and a messageChatList is placed to refresh the status data

chat model effects receiverMessage


*receiverMessage({ payload }, { call, put, select }) {
  const { message } = payload;
  const chatState = yield select((state) = > state['chat']);
  const { messageChatList = [] } = chatState;
  const temp = messageChatList.concat();

  // The server pushes the message in JSON format and parses the message
  temp.push(JSON.parse(message));

  // Refresh the chat details list
  yield put({ type: 'refreshChatList'.payload: { messageChatList: temp } });
},
Copy the code

chat model reducers refreshChatList

// Refresh the chat list
refreshChatList(state, { payload: { messageChatList } }) {
  return { ...state, messageChatList: messageChatList };
},
Copy the code

The server receives the message and the database stores it

TODO:

  • Sending and receiving messages
    • The chat history list is not updated
  • Click back to the chat history of the chat object to obtain only 10 data at a time, you need to slide to obtain historical chat history data
  • The chat list avatar is still fetched from Mock data
  • Unread messages
  • .

Pay attention to the public account full stack technology department, continue to learn more interesting technical knowledge.