preface

Recently received a demand is on the basis of the original project to develop an instant messaging features, mainly is the one-to-one chat, for instant communication between the customer and customer, main functions include chat list, send word, send expressions, sending pictures, video, voice, work out send custom information, and other functions. Because the project needs, so chose Tencent cloud IM to develop.

The development process

The first step is to register Tencent cloud and create the app, get the APPID and the secret key.

The document location: cloud.tencent.com/document/pr…

1. Install dependencies

// NPM install tim-wx-sdk --save // NPM install tim-upload-plugin --saveCopy the code

2. Introduce the module into the project script and initialize it.

After installing the dependencies, create the tencentIM directory under the utils directory of the project directory to store js files related to IM.

Encapsulate event. Js

Because the project needs some time to listen, you need to add an Event class to utils and bind it to the WX global object on app.js.

// app.js
import Event from './utils/tencentIM/event'
wx.event = new Event();

// event.js
class Event {
  version = "1.0.0";
  constructor() {
    this._events = {};
  }
  on(eventName, listener) {
    if(! eventName || ! listener)return;
    // Determine whether the listener for the callback is a function
    if (isValidListener(listener)) {
      throw new TypeError("listener must be a function");
    }
    let events = this._events;
    let listeners = (events[eventName] = events[eventName] || []);
    let listenerIsWrapped = typeof listener === "object";
    // Do not add events repeatedly, check whether there is the same
    if (indexOf(listeners, listener) === -1) {
      listeners.push(
        listenerIsWrapped
          ? listeners
          : {
              listener,
              once: false}); }return this;
  }
  once(eventName, listener) {
    // Call the on method directly, passing the once argument to true and waiting for the once processing
    this.on(eventName, {
      listener,
      once: true});return this;
  }
  emit(eventName, args) {
    // Get the corresponding custom event callback directly from the internal object
    let listeners = this._events[eventName];
    if(! listeners)return;
    // Multiple listeners need to be considered
    listeners.forEach((listener) = > {
      if (listener) {
        listener.listener.apply(this, args || []);
        if (listener.once) {
          this.off(eventName, listener.listener); }}});return this;
  }
  off(eventName, listener) {
    let listeners = this._events[eventName];
    if(! listener)return;
    listeners.some((item, index) = > {
      if (item && item.listener === listener) {
        listeners.splice(index, 1);
        return true;
      }
      return false;
    });
  }
  allOff(eventName) {
    if (eventName && this._events[eventName]) {
      this._events[eventName] = [];
    } else {
      this._events = {}; }}}// Check whether the listener is valid
function isValidListener(listener) {
  if (typeof listener === "function") {
    return true;
  } else if (listener && typeof listener === "object") {
    return isValidListener(listener.listener);
  } else {
    return false; }}// Check whether the new user-defined event exists
function indexOf(array, item) {
  var result = -1;
  item = typeof item === "object" ? item.listener : item;
  for (var i = 0, len = array.length; i < len; i++) {
    if (array[i].listener === item) {
      result = i;
      break; }}return result;
}

Copy the code

The server side can develop and generate userSig interface or download the files provided by Tencent Cloud to generate userSig

Related document: cloud.tencent.com/document/pr…

Client generated userSig file provided by Tencent Cloud: github.com/tencentyun/…

Initialize Tim and set up listening

import TIM from 'tim-wx-sdk';
import TIMUploadPlugin from 'tim-upload-plugin';
let options = {
 SDKAppID: 0 // Replace 0 with the SDKAppID of your IM application
};
// To create an SDK instance, the 'tim.create ()' method only returns the same instance for the same 'SDKAppID'
let tim = TIM.create(options); // SDK instances are usually represented by Tim
// Set the SDK log output level, Detailed classification see < a href = "https://imsdk-1252463788.file.myqcloud.com/IM_DOC/Web/SDK.html#setLogLevel" > setLogLevel interface description < / a >
tim.setLogLevel(0); // This parameter is a common level with a large amount of logs. Therefore, this parameter is recommended for access
// tim.setLogLevel(1); // Release level, SDK output key information, recommended for production environment
// Register Tencent cloud IM upload plug-in
tim.registerPlugin({'tim-upload-plugin': TIMUploadPlugin});
// Listen events, for example:
tim.on(TIM.EVENT.SDK_READY, function(event) {
// After receiving the notification that the offline message and session list are synchronized, the access side can invoke interfaces requiring authentication, such as sendMessage
// event.name - TIM.EVENT.SDK_READY
});
tim.on(TIM.EVENT.MESSAGE_RECEIVED, function(event) {
// After receiving the new message pushed by single chat, group chat, group prompt and group system notification, you can obtain the message list data by traversing event.data and render it to the page
// event.name - TIM.EVENT.MESSAGE_RECEIVED
// event.data - Array of Message objects - [Message]
});
tim.on(TIM.EVENT.MESSAGE_REVOKED, function(event) {
// Received a notification that the message was withdrawn
// event.name - TIM.EVENT.MESSAGE_REVOKED
// Event. data - Array of Message objects to store - [Message] - isRevoked property value of each Message object is true
});
tim.on(TIM.EVENT.MESSAGE_READ_BY_PEER, function(event) {
// The SDK receives a notification that the message has been read from the peer end. Before using the SDK, upgrade it to V2.7.0 or later. Only single-chat sessions are supported.
// event.name - TIM.EVENT.MESSAGE_READ_BY_PEER
// event.data - event.data - Array of Message objects - [Message] - isPeerRead property value of each Message object is true
});
tim.on(TIM.EVENT.CONVERSATION_LIST_UPDATED, function(event) {
// The session list update notification is received, and the session list data can be obtained by iterating through event.data and rendered to the page
// event.name - TIM.EVENT.CONVERSATION_LIST_UPDATED
// event.data - Store Conversation objects - [Conversation]
});
tim.on(TIM.EVENT.GROUP_LIST_UPDATED, function(event) {
// The group list update notification is received, and the group list data can be obtained by iterating through event.data and rendered to the page
// event.name - TIM.EVENT.GROUP_LIST_UPDATED
// event.data - Store an array of Group objects - [Group]
});
tim.on(TIM.EVENT.PROFILE_UPDATED, function(event) {
// Receive notification of your or your friend's profile change
// event.name - TIM.EVENT.PROFILE_UPDATED
// event.data - Array of Profile objects - [Profile]
});
tim.on(TIM.EVENT.BLACKLIST_UPDATED, function(event) {
// Received notification of blacklist update
// event.name - TIM.EVENT.BLACKLIST_UPDATED
// event.data - Array of userids - [userID]
});
tim.on(TIM.EVENT.ERROR, function(event) {
// Received SDK error notification, you can obtain the error code and error information
// event.name - TIM.EVENT.ERROR
// event.data.code - Error code
// event.data.message - Error message
});
tim.on(TIM.EVENT.SDK_NOT_READY, function(event) {
// Received a notification that the SDK entered the not Ready state, at which time the SDK cannot work properly
// event.name - TIM.EVENT.SDK_NOT_READY
});
tim.on(TIM.EVENT.KICKED_OUT, function(event) {
// Receive notification of being kicked offline
// event.name - TIM.EVENT.KICKED_OUT
// event.data.type - The reason for being kicked off, for example:
// -tim.types.KICKED_OUT_MULT_ACCOUNT Multi-instance login was kicked
// -tim.types.KICKED_OUT_MULT_DEVICE Multi-terminal login was kicked
// -tim.types.KICKED_OUT_USERSIG_EXPIRED signature kicked (supported from V2.4.0).
});
tim.on(TIM.EVENT.NET_STATE_CHANGE, function(event) { 
// Network status changes (supported since V2.5.0).
// event.name - TIM.EVENT.NET_STATE_CHANGE 
// event.data.state Indicates the current network status. Enumeration values are as follows:
// \ -tim.types.net _STATE_CONNECTED - The network has been connected
// \ -tim.types.net _STATE_CONNECTING Network jitter is likely encountered. SDK is retrying. The access side can display the Current network is unstable or Connecting based on the status.
// \ -tim.types.net _STATE_DISCONNECTED - No network access. The access side can display the current network is unavailable according to this status. The SDK will continue to retry, and if the user's network is restored, the SDK will automatically synchronize the message
});
// Start logging in
tim.login({userID: 'your userID'.userSig: 'your userSig'});
Copy the code

Chat development log point

(1) Expression development

Enter thewww.oicqzone.com/tool/emoji/Website, the last column of expression downloaded down of course can also be through the following way to copy down the expression, and then save to a JS command inside:Array.from(document.querySelectorAll('body > table > tbody > tr > td:nth-child(8)')).map(item=>item && item.innerText||'')

Then import the project can be used, click the emoticon will be directly placed after the input box text

  selectEmoji(e){
    const {text} = e.currentTarget.dataset
    this.setData({
      colSendMsg:this.data.colSendMsg + text,
    })
  }
Copy the code

(2) Voice sending function

Send voice call mainly wx. CreateInnerAudioContext () method Main details is recording authorization, long press the tape, the tape was shorter than must not be sent in time and prompt, can be sent via slide way to cancel.

The recording authorization
  // Click the record button
  record() {
    let that = this
    // The canRecord variable is used to save the status of whether the recording function is authorized
    if (that.canRecord) {
      // Start recording
      that.beforeStartRecord()
      return
    }
    wx.authorize({
      scope: 'scope.record'.success() {
        console.log("Recording authorization successful");
        that.canRecord = true
        // The user has agreed that the applet should use the recording function
      },
      fail() {
        console.log("The first recording authorization failed.");
        wx.showModal({
          title: 'tip'.content: 'You are not authorized to record, the function will not be used'.showCancel: true.confirmText: "Authorization".confirmColor: "#52a2d8".success: function (res) {
            if (res.confirm) {
              // Confirm to open the Settings page (key)
              wx.openSetting({
                success: (res) = > {
                  console.log(res.authSetting);
                  if(! res.authSetting['scope.record']) {
                    // Recording authorization is not configured
                    wx.showModal({
                      title: 'tip'.content: 'You are not authorized to record, the function will not be used'.showCancel: false.success: function (res) {},})}else {
                    // Only the second time
                    that.canRecord = true
                    console.log("Setting recording authorization succeeded"); }},fail: function () {
                  console.log("Failed to authorize recording Settings"); }})}else if (res.cancel) {
              console.log("cancel"); }},fail: function () {
            console.log("openfail"); }})}})},Copy the code

There are many blog posts related to the recording process behind, so I will not repeat it, but only provide you with a thought:

(3) Scroll to the bottom

A chat page is important for users to see the latest information, so to display the bottom of the page, I display the bottom of the page according to the following situations:

  1. The page is displayed for the first time
  2. Send any message
  3. Listen for the latest messages

The specific code is as follows:

// wxml
<scroll-view class="chat-area" refresher-enabled="{{true}}" refresher-triggered="{{triggered}}"
  bindrefresherrefresh="onRefresh" scroll-into-view="{{ viewIndex }}" scroll-y="true" enable-flex="{{true}}"
  bindtap="onHideSendMore" bindtap="hideBottom" style="padding-bottom:{{InputBottom}}px">
  <! -- <view wx:if="{{showLoading}}" class="cu-load loading"></view> -->
  <view class="cu-chat">
    <view wx:for="{{ arrMsg }}" wx:key="ID">// Details</view>
  </view>
</scroll-view>// js-pagesCrollTobottom // isBottom slides to the bottom // lastIndex slides to the specified position // arrMsg message list pageScrollToBottom(isBottom,lastIndex)  = 0) { let index = null if (isBottom) { index = 'msg-' + (this.data.arrMsg.length -1); } else if(lastIndex) { index = 'msg-' + lastIndex; }else{ index = 'msg-' + 0; } this.setData({ viewIndex: index }) },Copy the code

(4) Bottom spring height

When we are developing, we often need the bottom pop up, and the keyboard pop up operation, which also needs to optimize the user experience.

  1. Make the chat record push up when it bounces
  2. Keep the other departments bouncing at the same height as the keyboard (so there’s not too much jumping)

I’ll use the style=”padding-bottom:{{InputBottom}}px”, InputBottom is set to 200 by default, and then record the height of the keyboard when it comes up, and then save the height of the keyboard

// wxml
<input catchblur="InputBlur"></input>
// js
InputFocus(e) {
    let height = e.detail.height
    if(height > 0) this.InputBottom = height
    this.setData({
      InputBottom: height
    })
}
Copy the code

The end of the

This article is just a record of some important places in my development of this project, for everyone’s reference and personal review in the future, if there is insufficient place, please give more advice.