Skip to content

boboxiaodd/react-native-easy-chat-ui

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

主要改动

1、当 RN > 0.69 ViewPropTypes 不能用了,用 deprecated-react-native-prop-types 代替合并代码。

2、增加依赖:react-native-pager-view,修复bug DaiYz#98 (comment)

3、修复警告:

Warning: Failed prop type: ChatWindow: prop type `userProfile.avatar` is invalid; it must be a function, usually from the `prop-types` package, but received `undefined`.

4、改成微信发送风格,去掉发送按钮图标,表情图标始终保持不变

5、基本重写键盘弹出、emoji、panel弹出关闭相关的逻辑。使其基本符合微信操作习惯(emoji面板使用TouchableOpacity来处理emoji点击会带来性能问题,改成了 Pressable

6、去掉默认的emoji图标,改成自定义的表情图标,增加emojiList属性,结构如下:

{
  name: "文字名称",
  url : "emoji的静图url",
  gif_url: "emoji的动图url"
}

使用远程图标,减少包大小,建议使用FastImage组件缓存,

<ChatScreen ... CustomImageComponent={FastImage} />

每页24个,创建分页的例子

const [emojiList, setEmojiList] = useState([]);
useEffect(() => {
      let emojiGroupList = [];
      if(emojiList.length === 0) {
          let page = -1;
          for (let i = 0; i < userinfo.emoji_list.length; i++) {
              if (i % 24 === 0) {
                  page++;
                  emojiGroupList[page] = [];
              }
              emojiGroupList[page].push({
                        name: userinfo.emoji_list[i].name,
                        url: parseUrl(userinfo.emoji_list[i].emoji),
                        gif_url: parseUrl(userinfo.emoji_list[i].emoji_gif),
              });
          }
      }
      setEmojiList(emojiGroupList);
},[]);

关于 aac 转 mp3 (库比较大,建议使用服务器转格式)

使用ffmpeg-kit-react-native 和子库 audio-lts,命令文本:

-i xxxx.aac -c:v mpeg3 xxxx.mp3 

关于MQTT库

那些原生库都不太好用,还是纯js实现的基于websocket的比较好

yarn add react_native_mqtt

有个坑,库比较旧,依赖的 async-storage 不能用,更换为 @react-native-async-storage/async-storage

react-native-easy-chat-ui

中文文档

npm npm npm npm

ScreenShots

react-native-easy-chat-ui react-native-easy-chat-ui react-native-easy-chat-ui react-native-easy-chat-ui react-native-easy-chat-ui react-native-easy-chat-ui

Installation

  • npm: npm install https://github.com/boboxiaodd/react-native-easy-chat-ui --save
  • Yarn: yarn add https://github.com/boboxiaodd/react-native-easy-chat-ui

Dependency

  • Use version 0.2.x for RN >= 0.44.0
  • Use version 0.1.x for RN < 0.44.0

Example

import React, { Component } from 'react';
import {
  Platform,
  StyleSheet,
  Text,
  View,
  StatusBar,
  PermissionsAndroid
} from 'react-native';
import { Header, NavigationActions } from 'react-navigation'
import {AudioRecorder, AudioUtils} from 'react-native-audio'
import RNFS from 'react-native-fs'
import Sound from 'react-native-sound'
import { ChatScreen } from 'react-native-easy-chat-ui'


class Example extends React.Component {
  state = {
     messages: [
            {
              id: `1`,
              type: 'text',
              content: 'hello world',
              targetId: '12345678',
              chatInfo: {
                avatar: require('../../source/defaultAvatar.png'),
                id: '12345678',
                nickName: 'Test'
              },
              renderTime: true,
              sendStatus: 0,
              time: '1542006036549'
            },
            {
              id: `2`,
              type: 'text',
              content: 'hi/{se}',
              targetId: '12345678',
              chatInfo: {
                avatar: require('../../source/defaultAvatar.png'),
                id: '12345678',
                nickName: 'Test'
              },
              renderTime: true,
              sendStatus: 0,
              time: '1542106036549'
            },
            {
              id: `3`,
              type: 'image',
              content: {
                uri: 'https://upload-images.jianshu.io/upload_images/11942126-044bd33212dcbfb8.jpg?imageMogr2/auto-orient/strip|imageView2/1/w/300/h/240',
                width: 100,
                height: 80,
              } ,
              targetId: '12345678',
              chatInfo: {
                avatar: require('../../source/defaultAvatar.png'),
                id: '12345678',
                nickName: 'Test'
              },
              renderTime: false,
              sendStatus: 0,
              time: '1542106037000'
            },
            {
              id: `4`,
              type: 'text',
              content: '你好/{weixiao}',
              targetId: '88886666',
              chatInfo: {
                avatar: require('../../source/avatar.png'),
                id: '12345678'
              },
              renderTime: true,
              sendStatus: -2,
              time: '1542177036549'
            },
            {
              id: `5`,
              type: 'voice',
              content: {
                uri: 'http://m10.music.126.net/20190810141311/78bf2f6e1080052bc0259afa91cf030d/ymusic/d60e/d53a/a031/1578f4093912b3c1f41a0bfd6c10115d.mp3',
                length: 10
              },
              targetId: '12345678',
              chatInfo: {
                avatar: require('../../source/defaultAvatar.png'),
                id: '12345678',
                nickName: 'Test'
              },
              renderTime: true,
              sendStatus: 1,
              time: '1542260667161'
            },
            {
              id: `6`,
              type: 'voice',
              content: {
                uri: 'http://m10.music.126.net/20190810141311/78bf2f6e1080052bc0259afa91cf030d/ymusic/d60e/d53a/a031/1578f4093912b3c1f41a0bfd6c10115d.mp3',
                length: 30
              },
              targetId: '88886666',
              chatInfo: {
                avatar: require('../../source/avatar.png'),
                id: '12345678'
              },
              renderTime: true,
              sendStatus: 0,
              time: '1542264667161'
            },
          ],
          // chatBg: require('../../source/bg.jpg'),
          inverted: false,  // require
          voiceHandle: true,
          currentTime: 0,
          recording: false,
          paused: false,
          stoppedRecording: false,
          finished: false,
          audioPath: '',
          voicePlaying: false,
          voiceLoading: false
  }


  sendMessage = (type, content, isInverted) => {
      console.log(type, content, isInverted, 'msg')
    }

  render() {
    return (
      <ChatScreen
        ref={(e) => this.chat = e}
        messageList={this.state.messages}
        sendMessage={this.sendMessage}
      />
    )
  }
}

Advanced example (How to record voice)

cd Demo
yarn
react-native run-ios or react-native run-android

About Message

{
    messages: [
      {
        id: `${new Date().getTime()}`,
        type: 'text',
        content: 'hello world',
        targetId: '12345678',
        chatInfo: {
          avatar: require('./app/source/image/avatar.png'),
          id: '12345678',
          nickName: 'Test'   // not require
        },
        renderTime: true,
        sendStatus: 0,
        time: new Date().getTime()
      }
    ]
}
  • id: message id
  • about message type: 'text', 'image', 'voice', 'video', 'location', 'share', 'videoCall', 'voiceCall', 'redEnvelope', 'file', 'system'
  • targetId: The id of the person who sent the message
  • content: see example
  • chatInfo: The profile of the person you're chatting with
  • renderTime: Whether to render time above message
  • sendStatus: 0 ---> sending, 1 ---> sendSuccess, -1 ---> You are deleted or on the blacklist, -2 ---> error
  • time: moment,messageList sorted by time

Props

  • message
props default Info
messageList [] Messages to display
inverted false When messageList exceeds the screen height, set it to true otherwise false (You can change this value when componentWillUnmount or delete message)
isIPhoneX false Is it full screen
chatBackgroundImage null Custom BackgroundImage
onScroll () => {} ListView Props
onEndReachedThreshold 0.1 ListView Props
chatWindowStyle undefined Container style
sendMessage (type, content, isInverted) => {} Callback when sending a message
reSendMessage (message) => {} Callback when you want send again
delMessage (indexs, isInverted) => {} Callback when delete message
renderAvatar (message) => {} Custom avatar view
avatarStyle undefined Style of avatar
chatId '123455678' The id of the person you're chatting with
chatType 'friend' Your relationship with the person you're chatting with
onMessagePress (type, index, content) => {} Callback when press a message
onMessageLongPress (type, index, content) => {} Callback when longPress a message and usePopView is false
pressAvatar (isSelf, targetId) => {} Callback when press avatar
headerHeight 66 navigation bar height + statusBar height
userProfile {id: '88888888', avatar: 'default.png'} Your own profile
showUserName false Whether show userName
loadHistory () => {} Callback when loading earlier messages
renderMessageTime (time) => {} Custom time inside above message
renderChatBg (bg) => {} Custom chat background image
renderErrorMessage (messageStatus) => {} Custom a message when the friend relationship is abnormal
panelSource [] Custom panel source
renderPanelRow () => {} Custom a tab icon
allPanelHeight 200 emojiPanel and plusPanel height
messageErrorIcon icon element Custom a icon when message failed to be sent
leftMessageBackground '#fffff' Custom background color on left
rightMessageBackground '#a0e75a' Custom background color on right
leftMessageTextStyle undefined Custom text message style on left
rightMessageTextStyle undefined Custom text message style on right
  • inputBarProps
props default Info
emojiIcon icon element Custom emoticons
placeholder '请输入...' Placeholder when text is empty
keyboardIcon icon Custom keyboard icon
plusIcon icon element Custom plus icon
sendIcon icon element Custom send icon
  • popViewProps
props default Info
usePopView true Display a popView when longPress a message
popoverStyle {backgroundColor: '#333'} popView style
renderDelPanel undefined Custom any what you want, (isSelect)=> {}
changeHeaderLeft () => {} Custom headerLeft
setPopItems (type, index, text) => {let items = [{title: '删除',onPress: () => {that.props.delMessage([index])}},{title: '多选',onPress: () => {that.multipleSelect(index)}}]if (type === 'text') {items = [{title: '复制',onPress: () => Clipboard.setString(text)},{title: '删除',onPress: () => {that.props.delMessage([index])}},{title: '多选', onPress: () => {that.multipleSelect(index)}}]}return items} Custom PopView
messageDelIcon icon element Custom delete icon
messageSelectIcon icon element Custom selected icon
renderMessageCheck undefined Custom selected icon, (isSelect)=> {}
  • voiceProps
props default Info
useVoice true send voice message
pressInText '按住 说话' Custom pressIn text
pressOutText '松开 发送' Custom pressOut text
voiceIcon icon element Custom voice icon
voiceLeftIcon undefined Custom icon of the message on the left
voiceRightIcon undefined Custom icon of the message on the right
voiceErrorIcon icon element Custom icon when record error
voiceErrorText '说话时间太短' Custom text when record error
voiceCancelIcon icon element Custom icon when cancel record
voiceCancelText '松开手指取消发送' Custom text when cancel record
voiceNoteText '手指上划,取消发送' Custom text when pressIn record button
voiceSpeakIcon [] Custom icon when pressIn record button
audioPath '' File path to store voice
audioOnProgress () => {} Callback when recording
audioOnFinish () => {} Callback when finish record
audioInitPath () => {} Callback when init file path
audioRecord () => {} Callback when start record
audioStopRecord () => {} Callback when stop record
audioPauseRecord () => {} Callback when pause record
audioResumeRecord () => {} Callback when resume record
audioCurrentTime 0 audio length
audioHandle true Whether to get a recording handle
setAudioHandle (status) => {} Callback when get handle or not
audioHasPermission false Whether has permission
requestAndroidPermission () => {} Callback when check permission on android
checkPermission () => {} Callback whether has permission
voiceLoading false Loading voice or not
voicePlaying false Playing voice or not
voiceLeftLoadingColor '#cccccc' Custom background color on left when load voice
voiceVolume 0 Volume (0~10)
voiceRightLoadingColor '#628b42' Custom background color on right when load voice
  • bubbleProps
props default Info
renderTextMessage undefined Custom message text, (data) => {}
renderImageMessage undefined Custom message image, (data) => {}
renderVoiceMessage undefined Custom message voice, (data) => {}
renderVoiceView undefined Custom voice container, (data) => {}
renderVideoMessage undefined Custom message video, (data) => {}
renderLocationMessage undefined Custom message location, (data) => {}
renderShareMessage undefined Custom message share, (data) => {}
renderVideoCallMessage undefined Custom message video call, (data) => {}
renderVoiceCallMessage undefined Custom message voice call, (data) => {}
renderRedEnvelopeMessage undefined Custom message red-envelope, (data) => {}
renderFileMessage undefined Custom message file, (data) => {}
renderPatMessage undefined Custom message pat, (data) => {}
renderCustomMessage undefined Custom message custom, (data) => {}
renderSystemMessage undefined Custom message system, (data) => {}

All Props

  propTypes = {
      /* defaultProps */
      messageList: PropTypes.array.isRequired,
      inverted: PropTypes.bool,
      isIPhoneX: PropTypes.bool.isRequired,
      lastReadAt: PropTypes.object,
      chatBackgroundImage: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
      onScroll: PropTypes.func,
      onEndReachedThreshold: PropTypes.number,
      chatWindowStyle: ViewPropTypes.style,
      sendMessage: PropTypes.func,
      renderAvatar: PropTypes.func,
      avatarStyle: ViewPropTypes.style,
      allPanelAnimateDuration: PropTypes.number,
      chatType: PropTypes.oneOf(['friend', 'group']),
      onMessagePress: PropTypes.func,
      onMessageLongPress: PropTypes.func,
      renderMessageTime: PropTypes.func,
      pressAvatar: PropTypes.func,
      renderErrorMessage: PropTypes.func,
      renderChatBg: PropTypes.func,
      reSendMessage: PropTypes.func,
      headerHeight: PropTypes.number.isRequired,
      iphoneXBottomPadding: PropTypes.number,
      showUserName: PropTypes.bool,
      showIsRead: PropTypes.bool,
      showInput: PropTypes.bool,
      isReadStyle: PropTypes.object,
      userProfile: PropTypes.shape({
        id: PropTypes.string.isRequired,
        avatar: PropTypes.isRequired,
        nickName: PropTypes.string
      }),
      panelSource: PropTypes.array,
      renderPanelRow: PropTypes.func,
      panelContainerStyle: ViewPropTypes.style,
      itemContainerStyle: ViewPropTypes.style,
      allPanelHeight: PropTypes.number,
      messageErrorIcon: PropTypes.element,
      loadHistory: PropTypes.func,
      leftMessageBackground: PropTypes.string,
      rightMessageBackground: PropTypes.string,
      leftMessageTextStyle: PropTypes.object,
      rightMessageTextStyle: PropTypes.object,
      renderLoadEarlier: PropTypes.func,
      extraData: PropTypes.any,
      containerBackgroundColor: PropTypes.string,
      showsVerticalScrollIndicator: PropTypes.bool,
      userNameStyle: PropTypes.object,
      panelContainerBackgroundColor: PropTypes.string,
      /* popProps */
      usePopView: PropTypes.bool,
      popoverStyle: ViewPropTypes.style,
      renderDelPanel: PropTypes.func,
      changeHeaderLeft: PropTypes.func,
      setPopItems: PropTypes.func,
      messageDelIcon: PropTypes.element,
      messageSelectIcon: PropTypes.element,
      delMessage: PropTypes.func,
      renderMessageCheck: PropTypes.func,
    
      /* inputBarProps */
      emojiIcon: PropTypes.element,
      placeholder: PropTypes.string,
      keyboardIcon: PropTypes.element,
      plusIcon: PropTypes.element,
      sendIcon: PropTypes.element,
      sendUnableIcon: PropTypes.element,
      inputStyle: ViewPropTypes.style,
      inputOutContainerStyle: ViewPropTypes.style,
      inputContainerStyle: ViewPropTypes.style,
      inputHeightFix: PropTypes.number,
      useEmoji: PropTypes.bool,
      emojiList: PropTypes.array, // <<------- 新属性
      usePlus: PropTypes.bool,
      /* voiceProps */
      useVoice: PropTypes.bool,
      pressInText: PropTypes.string,
      pressOutText: PropTypes.string,
      voiceIcon: PropTypes.element,
      voiceLeftIcon: PropTypes.element,
      voiceRightIcon: PropTypes.element,
      voiceErrorIcon: PropTypes.element,
      voiceCancelIcon: PropTypes.element,
      voiceSpeakIcon: PropTypes.array,
      audioPath: PropTypes.string,
      audioOnProgress: PropTypes.func,
      audioOnFinish: PropTypes.func,
      audioInitPath: PropTypes.func,
      audioRecord: PropTypes.func,
      audioStopRecord: PropTypes.func,
      audioPauseRecord: PropTypes.func,
      audioResumeRecord: PropTypes.func,
      audioCurrentTime: PropTypes.number,
      audioHandle: PropTypes.bool,
      setAudioHandle: PropTypes.func,
      audioHasPermission: PropTypes.bool,
      checkPermission: PropTypes.func,
      requestAndroidPermission: PropTypes.func,
      voiceErrorText: PropTypes.string,
      voiceCancelText: PropTypes.string,
      voiceNoteText: PropTypes.string,
      voiceLoading: PropTypes.bool,
      voicePlaying: PropTypes.bool,
      voiceLeftLoadingColor: PropTypes.string,
      voiceVolume: PropTypes.number,
      voiceRightLoadingColor: PropTypes.string,
      /* bubbleProps */
      renderTextMessage: PropTypes.func,
      renderImageMessage: PropTypes.func,
      renderVoiceMessage: PropTypes.func,
      renderVoiceView: PropTypes.func,
      renderVideoMessage: PropTypes.func,
      renderLocationMessage: PropTypes.func,
      renderShareMessage: PropTypes.func,
      renderVideoCallMessage: PropTypes.func,
      renderVoiceCallMessage: PropTypes.func,
      renderRedEnvelopeMessage: PropTypes.func,
      renderFileMessage: PropTypes.func,
      renderSystemMessage: PropTypes.func,
      renderCustomMessage: PropTypes.func,
      renderPatMessage: PropTypes.func,
      /* delPanelProps */
      delPanelStyle: ViewPropTypes.style,
      delPanelButtonStyle: ViewPropTypes.style,
      flatListProps: PropTypes.object
  }

Notes for Android

  • Make sure you have android:windowSoftInputMode="adjustResize" in your AndroidManifest.xml:
android:windowSoftInputMode="adjustResize"

Donation

☕️☕️

react-native-easy-chat-ui react-native-easy-chat-ui

License

About

chat UI for React Native

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages

  • JavaScript 92.8%
  • Objective-C 2.7%
  • Java 1.7%
  • Ruby 1.6%
  • Starlark 1.2%