Название говорит само за себя!

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

Вот ссылка на мое демонстрационное репозиторий 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»: «», «регион»: «» «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 - это логическое значение, показывающее, когда сообщения были получены. Если задано значение false, на экране отображается круг загрузки.

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 и в базу данных.

Вот код для этого:

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 и базу данных.

messageIdGenerator: создает uuid для пути чата и аудиозаписи. Для этого можно использовать любой генератор uuid.