Заглавието казва всичко!

Ето видео демонстрация:

Ето и връзка към моето демонстрационно репо в github: https://github.com/liplylie/ReactNativeChatImageAudio. Моля, дайте му звезда, ако сте харесали тази статия!

Технологичният стек ще бъде: React Native (дух), React-Native Gifted Chat, React-Native-Audio, React-Native-Sound, React-Native-Image-Picker, React-Native -aws3 и Firebase.

Исках да споделя кода си и да го обясня, за да помогна на други, които може да преминат през борбата с добавянето на функции за изображения и аудио записи към своя чат. Кодът отрязва някои чувствителни данни, така че ще трябва да го попълните със свои.

Освен това ще трябва да имате малко разбиране за AWS S3 и базата данни в реално време Firebase за това.

Моля, погледнете страницата ми в github и в директорията src създайте директория config и добаветеAWSconfig.js и FirebaseConfig.js. Ще ви е необходим и файл sensitive.json в основната директория с тази структура:

{ “BUCKET”: “”, “ACCESS_KEY”: “”, “SECRET_KEY”: “”, “APIKEY”: “”, “keyPrefix”: “”, “region”: “” “authDomain”: “”, “ databaseURL”: “”, “projectId”: “”, “storageBucket”: “”, “messagingSenderId”: “”}

Нека започнем, като разгледаме state и componentWillMount:

state = {
  messages: [],
  startAudio: false,
  hasPermission: false,
  audioPath: `${
    AudioUtils.DocumentDirectoryPath
    }/${this.messageIdGenerator()}test.aac`,
  playAudio: false,
  fetchChats: false,
  audioSettings: {
    SampleRate: 22050,
    Channels: 1,
    AudioQuality: "Low",
    AudioEncoding: "aac",
    MeteringEnabled: true,
    IncludeBase64: true,
    AudioEncodingBitRate: 32000
  }
};
componentWillMount() {
  this.chatsFromFB = firebaseDB.ref(
    `enter path to your group/one-one chat data`
  );
  this.chatsFromFB.on("value", snapshot => {
    if (!snapshot.val()) {
      this.setState({
        fetchChats: true
      });
     return;
    }
    let { messages } = snapshot.val();
     messages = messages.map(node => {
       const message = {}; 
       message._id = node._id;
       message.text = node.messageType === "message" ? node.text : "";
       message.createdAt = node.createdAt;
       message.user = {
         _id: node.user._id,
         name: node.user.name,
         avatar: node.user.avatar
       };
       message.image = node.messageType === "image" ? node.image : "";
       message.audio = node.messageType === "audio" ? node.audio : "";
       message.messageType = node.messageType;
       return message;
    });
      this.setState({
        messages: [...messages]
      });
    });
}

състояние: Messages е масив, съдържащ всички съобщения в чата. StartAudio е булево, което променя цвета на микрофона, когато се записва аудио. HasPermission проверява устройството на потребителя за разрешение за запис на аудио. AudioSetting е конфигурацията, необходима за React-Native-Audio. PlayAudio е булево, което променя цвета на символа за възпроизвеждане, когато се възпроизвежда аудиозапис. AudioPath е мястото в устройството, където се съхранява аудиозаписът. FetchChat е булева стойност, показваща кога съобщенията са били извлечени. Когато е невярно, на екрана се изобразява кръг за зареждане.

ComponentWillMount: Тук ще трябва да извлечете съобщенията от вашата база данни в реално време на firebase и да запазите препратка към тази база данни, за да можете да съхранявате нови съобщения.

Структурата на данните ми във firebase изглежда така:

- roomId
    - messages
        - 0
            - _id : "",
            - createdAt: "",
            - messageType: "", 
            - audio: "",
            - image: "",
            - text: "",
            - recipientId: "",
            - jobId: ""
            - user
                 - _id: "",
                 - avatar: "",
                 - name: ""

Типът съобщение е съобщение, изображение или аудио. Ако типът е съобщение, чатът ще бъде обикновен низ. Ако типът на съобщението е изображение, чатът ще визуализира изображение. URL адресът на изображението ще се съхранява в image. Ако типът на съобщението е аудио, URL адресът на звука ще се съхранява в audio.

Съответната информация за създателя на чата се съхранява в user.

RecipientId и jobId съответстват на идентификаторите на стаи. Идентификационният номер на получателя се използва за чатове един към един в този случай, а идентификаторът на работа се използва за групови чатове.

Използвайте this.setState(), за да зададете съобщенията на състоянието на компонента.

Ако трябва да форматирате данните си, така че да работят със структурата на GiftedChat, можете да създадете обект за чат по следния начин:

const message = {};
message._id = "";
message.text = "";
message.createdAt = "";
message.user = {
_id: "",
name: "",
avatar: ""
};
message.image = "";
message.audio = "";
message.messageType = "";

След това погледнете функцията за рендиране:

render() {
  const { user } = data; // wherever you user info is
  return (
    <View style={{ flex: 1 }}>
      <secretNavBar
        title = "your app name"
        onLeftButtonPress={() => `some function to go back to  previous screen`}
        leftButtonTitle={"Back"} 
        rightButtonIcon={"camera"} 
        onRightButtonPress={() => this.handleAddPicture()}    
      />
      {this.renderLoading()}
      {this.renderAndroidMicrophone()}
      <GiftedChat
        messages={this.state.messages} 
        onSend={messages => this.onSend(messages)}
        alwaysShowSend
        showUserAvatar
        isAnimated
        showAvatarForEveryMessage
        renderBubble={this.renderBubble}
        messageIdGenerator={this.messageIdGenerator} 
        onPressAvatar={this.handleAvatarPress}
        renderActions={() => {
          if (Platform.OS === "ios") {
            return (
              <Ionicons
                name="ios-mic"
                size={35}
                hitSlop={{ top: 20, bottom: 20, left: 50, right: 50 }}
                color={this.state.startAudio ? "red" : "black"}
                style={{
                  bottom: 50,
                  right: Dimensions.get("window").width / 2,
                  position: "absolute", 
                  shadowColor: "#000",
                  shadowOffset: { width: 0, height: 0 },
                  shadowOpacity: 0.5,
                  zIndex: 2,
                  backgroundColor: "transparent"
                }}
                onPress={this.handleAudio}
              />);
            }
          }}
          user={{
            _id: user.userId,
            name: `${user.firstName} ${user.lastName}`,
            avatar: `Your photo`
          }}
        />
      <KeyboardAvoidingView />
    </View>
  );
}

SecretNavBar: това е само компонент за лентата за навигация. Нарочно не показах кода за навигационната си лента, така че ще трябва да направите свой собствен. В моя случай моят изглежда така:

Не искам да отделям много време за този компонент, но важната част от това е иконата на камерата вдясно. Натискането върху него ще задейства this.handleAddPicture().

handleAddPicture: Използва RN-Image-Picker, за да изберете или направите снимка и да я изпратите до firebase и db.

Ето кода за това:

handleAddPicture = () => {
  const { user } = data; // wherever you user data is stored;
  const options = {
    title: "Select Profile Pic",
    mediaType: "photo",
    takePhotoButtonTitle: "Take a Photo",
    maxWidth: 256,
    maxHeight: 256,
    allowsEditing: true,
    noData: true
  };
  ImagePicker.showImagePicker(options, response => {
    console.log("Response = ", response);
    if (response.didCancel) {
      // do nothing
    } else if (response.error) {
      // alert error
    } else {
      const { uri } = response;
      const extensionIndex = uri.lastIndexOf(".");
      const extension = uri.slice(extensionIndex + 1);
      const allowedExtensions = ["jpg", "jpeg", "png"];
      const correspondingMime = ["image/jpeg", "image/jpeg", "image/png"];
      const options = {
        keyPrefix: "****",
        bucket: "****",
        region: "****",
        accessKey: "****",
        secretKey: "****"
      };
      const file = {
        uri,
        name: `${this.messageIdGenerator()}.${extension}`,
        type: correspondingMime[allowedExtensions.indexOf(extension)]
      };
      RNS3.put(file, options)
     .progress(event => {
       console.log(`percent: ${event.percent}`);
     })
     .then(response => {
       console.log(response, "response from rns3");
       if (response.status !== 201) {
         alert(
         "Something went wrong, and the profile pic was not uploaded."
         );
         console.error(response.body);
         return;
       }
       const message = {};
       message._id = this.messageIdGenerator();
       message.createdAt = Date.now();
       message.user = {
         _id: user.userId,
         name: `${user.firstName} ${user.lastName}`,
         avatar: "user avatar here"
       };
       message.image = response.headers.Location;
       message.messageType = "image";
       this.chatsFromFB.update({
         messages: [message, ...this.state.messages]
       });
     });
     if (!allowedExtensions.includes(extension)) {
       return alert("That file type is not allowed.");
     }
   }
});
};

renderLoading: зареждане на кръгови рендове, докато чатовете се зареждат.

renderAndroidMicrophone: Това беше трудно. Имах нужда от микрофон Ionicon, който да натисна, за да започна аудиозаписа. Поставянето на микрофона в renderActions на RN-Gifted-Chat обаче предизвика странен проблем, при който микрофонът не се показваше правилно. Изобразяването на микрофон над компонента GiftedChat работи.

renderBubble: Този метод е необходим за RN-Gifted-Chat, за да изобрази символа за възпроизвеждане за аудио записи и да изобрази имената на създателя на чата.

renderBubble = props => {
  return (
    <View>
      {this.renderName(props)}
      {this.renderAudio(props)}
      <Bubble {...props} />
    </View>
  );
};

renderName: Показва името на създателя на чат до написаните от него чатове. Извиква се в метода renderBubble.

renderAudio: изобразява иконата за възпроизвеждане на записано аудио и възпроизвежда записа при натискане.

OnAvatarPress: Когато се щракне върху аватара, отидете до потребителския профил.

render: просто исках да отбележа keyboardAvoidingView, поставено след компонента GiftedChat. Това е необходимо, за да може въвеждането от клавиатурата да се показва правилно на устройства с Android.

renderActions: изобразява микрофона за iOS устройства.

ComponentDidMount: Състоянието на съобщението е зададено на „прочетено“ за всички съобщения. Разписките за съобщения излизат извън обхвата на тази статия, така че не се тревожете твърде много за това. Проверява за разрешения за активиране на аудиозапис. Тук също се записват регистрационни файлове за напредъка на аудиозаписа.

OnSend: тази функция се задейства при всяко изпращане на чат съобщение (низ, не изображение или аудио). Изпраща съобщението до firebase и db (ако е необходимо).

renderAudio: изобразява иконата за възпроизвеждане на записано аудио и възпроизвежда записа при натискане.

handleAudio: записва аудиото на потребителя, изпраща аудиото до AWS S3, извлича URL адреса на S3, изпраща аудиозаписа до firebase и db.

messageIdGenerator: Генерира uuid за пътя на чата и аудиозаписа. Може да използва всеки uuid генератор за това.