Mobile 개발/RN Project - Map Chat

RN - 채팅, GiftedChat with realtime database

히핑소 2021. 1. 1. 01:54
반응형

google firebase 의 realtime database 와

react-native-gitfted-chat library 를 활용하면

간단하게 채팅 화면을 구성할 수 있습니다.

 

왼쪽은 gifted-chat 가이드 화면, 우측은 제가 직접 적용한 화면입니다.

firebase 를 미리 구성했다는 전제하에,

gitfted chat 을 프로젝트에 install 합니다.

github.com/FaridSafi/react-native-gifted-chat

- Using npm: npm install react-native-gifted-chat --save
- Using Yarn: yarn add react-native-gifted-chat

 

아래는 전체 코드입니다.

- firebase-xxx/chat/ 폴더 구조로 구성

- firebase 에 ios/android app 을 등록해놓고 google-services.json 이나 googleservice-info.plist 만 잘 import 시켜놓으면, 아래 코드만 가지고 firebase 와 connect 됩니다.

import {GiftedChat, SystemMessage} from "react-native-gifted-chat";
import React, {Component} from "react";
import db from '@react-native-firebase/database';
import auth from "@react-native-firebase/auth";
import {setFilterText} from '../commonProperty/setFilterText';
import {View, TouchableOpacity, Text} from 'react-native';
import Toast from 'react-native-toast-message';
import {showToast} from '../commonProperty/showToast';
import {handleEmail} from '../commonProperty/handleEmail';
import RBSheet from "react-native-raw-bottom-sheet";
import { globalStyles } from '../commonProperty/globalStyles';
import Ionicons from 'react-native-vector-icons/Ionicons';
import MaterialIcons from "react-native-vector-icons/MaterialIcons";

const database = db().ref("chat");
export default class ChatScreen extends Component {
  state = {
    messages: //[],
    [{
      _id: 0,
      text: '부적절하거나 불쾌감을 줄 수 있는 대화는 삼가 부탁드립니다. 회원제재를 받을 수 있습니다.',
      createdAt: new Date().getTime(),
      system: true,
    }],
    uid: "",
    reportUser: null,
  };

  loadMessages(callback) {
    database.off(); //Detaches a callback previously attached with on()
    const onReceive = data => {
      const message = data.val();
      callback({
        _id: data.key,
        text: message.text,
        createdAt: message.createdAt,
        user: {
          _id: message.user._id,
          name: message.user.name,
        }
      });
    };
    // let d = this.getLimit();
    // console.log(d);
    database.orderByChild('locationInfo')
      .equalTo(this.props.route.params.city)
      .limitToLast(mChatLimit).on("child_added", onReceive);
  }
  sendMessage(message) {
    let today = new Date();
    let timestamp = today.toISOString();
    for (let i = 0; i < message.length; i++) {
      if (setFilterText(message[i].text)) {
        database.push({
          text: message[i].text,
          user: message[i].user,
          createdAt: timestamp,
          locationInfo: this.props.route.params.city,
          email: this.props.route.params.email,
        });
      } else {
        showToast('불쾌감을 줄 수 있는 내용은 삼가 부탁드립니다', '전송 실패', defaultDuration, 'top');
      }
    }
  }
  onRenderSystemMessage = (props) => (
    <SystemMessage
      {...props}
      containerStyle={{backgroundColor:'#7cc8c3'}}
      textStyle={{ color: "white", fontWeight:"500", fontSize: 17, textAlign:'center'}}
    />
  );
  closeChat() {
    if (database) {
      database.off();
    }
  }
  getLimit() {
    let today = new Date();
    today.setDate(today.getDate() - 31); // last 30 Days
    let changedISODate = new Date(today).toISOString();
    // console.log(changedISODate);
    return changedISODate;
  }
  componentDidMount() {
    auth().onAuthStateChanged(user => {
      if (user) {
        this.setState({uid:user.uid});
      }
    });
    this.loadMessages(message => {
      this.setState(previousState => {
        return {
          messages: GiftedChat.append(previousState.messages, message)
        };
      });
    });
  }
  onHandleEmail = () => {
    handleEmail(this.state.reportUser, "채팅 중 부적절한 메시지 사용 (스크린샷 첨부 권장)", this.props.route.params.city, null);
  }
  onPressAvatar = (user) => {
    this.setState({reportUser: user.name});
    this.Standard.open();
  }
  componentWillUnmount() {
    this.closeChat();
  }

  render() {
    return (
      <View style={{ flex: 1 }}>
       <GiftedChat
          messages={this.state.messages}
          onSend={message => {
            this.sendMessage(message);
          }}
          user={{
            _id: this.state.uid,
            name: this.props.route.params.nickname,
          }}
          renderSystemMessage={this.onRenderSystemMessage}
          placeholder="message 입력"
          onPressAvatar={this.onPressAvatar}
          // onPressActionButton={this.onPressActionButton}
          // renderUsernameOnMessage
        />

        <Toast ref={(ref) => Toast.setRef(ref)} />
        <RBSheet
          ref={ref => {this.Standard = ref;}}
          height={230}
          closeOnDragDown
          customStyles={{
            container: {alignItems: "center", backgroundColor: "#F5FCFF", 
              borderTopLeftRadius: 30, borderTopRightRadius: 30}}}>
          <View style={globalStyles.GlobalRawBottomView}>
            <TouchableOpacity style={globalStyles.GlobalRawBottomTouchable} onPress={this.onHandleEmail}>
              <MaterialIcons name={'report-problem'} style={globalStyles.GlobalRawBottomIcon} />
              <Text style={globalStyles.GlobalRawBottomLabelStart}>신고하기</Text>
              <Text style={globalStyles.GlobalRawBottomLabelEnd}/>
            </TouchableOpacity>
          </View>
          <View style={globalStyles.GlobalRawBottomView}>
            <View style={globalStyles.GlobalRawBottomTouchable}>
              <Ionicons name={'ios-person'} style={globalStyles.GlobalRawBottomIcon} />
              <Text style={globalStyles.GlobalRawBottomLabelStart}>유저정보</Text>
              <Text style={globalStyles.GlobalRawBottomLabelEnd}>{this.state.reportUser}</Text>
            </View>
          </View>
          <View style={globalStyles.GlobalRawBottomView}>
            <TouchableOpacity style={globalStyles.GlobalRawBottomTouchable}>
              <MaterialIcons name={'thumb-down-alt'} style={globalStyles.GlobalRawBottomGrayIcon} />
              <Text style={globalStyles.GlobalRawBottomLabelGray}>싫어요</Text>
              <Text style={globalStyles.GlobalRawBottomLabelEnd}/>
            </TouchableOpacity>
          </View>
        </RBSheet>
      </View>
    );
  }
}

firebase hierachy 는 아래와 같이 되어있습니다

마지막으로, 채팅 기능을 넣게되면 AppStore 심사가 까다로워지는데요

아래 심사 통과 방법도 공유 드립니다.

yannichoongs.tistory.com/195

 

RN - 채팅 기능 넣고 AppStore 심사 통과 하기

안녕하세요, 최근에 android/ios 동시 앱을 출시하면서 android 는 간단하게 앱 심사 통과시켜주는 반면에 ios는 꽤 까다롭다는것을 몸소 체험했습니다. 경험 상 android는 최소한의 휴먼 검수와 대부분

yannichoongs.tistory.com

 

반응형