Chat App - Part 2 (2)

Sang heon lee·2022년 7월 7일
0

1. 채널 목록 화면

  • firestore 의 onSnapshot을 활용하여 실시간으로 데이터를 감시하여 채널목록을 업데이트한다.
    https://firebase.google.com/docs/firestore/query-data/listen?authuser=0

  • useEffect 내에 함수를 해지해줘야 재호출되었을때 중복으로 데이터가 쌓이는 것을 방지할수 있다.
    return 함수를 해줘야 한다.

  • 채널목록을 클릭했을때 해당 채널로 이동하게끔 작성한다.

// src/screens/ChannelList.tsx

const ChannelList = ({ navigation }: Props) => {
  const [channels, setChannels] = useState([]);
  const db = getFirestore(app);

  useEffect(() => {
    const collectionQuery = query(
      collection(db, 'channels'),
      orderBy('createdAt', 'desc'),
    );

    const unsubscribe = onSnapshot(collectionQuery, snapshot => {
      const list = [];
      snapshot.forEach(doc => list.push(doc.data()));

      setChannels(list);
    });
    return () => unsubscribe();
  }, []);

  return (
    <Container>
      <StyledText>Channel List</StyledText>
      <FlatList
        data={channels}
        renderItem={({ item }) => (
          <Item
            key={item.id}
            item={item}
            onPress={({ id, title }: { id: string; title: string }) => {
              navigation.navigate('Channel', { id, title });
            }} 
          />
        )}
        // keyExtractor={item => item['id'].toString()}
        windowSize={5}
      />
    </Container>
  );
};

export default ChannelList;
// src/screens/ChannelList.tsx

import moment from 'moment';

const getDataOrTime = (ts: number) => {
  const now = moment().startOf('day');
  const target = moment(ts).startOf('day');
  return moment(ts).format(now.diff(target, 'day') > 0 ? 'MM/DD' : 'HH:mm');
};


const Item = React.memo(
  ({ item: { id, title, description, createdAt }, onPress }: itemProps) => {
    return (
      <ItemContainer onPress={() => onPress({ id, title })}>
        <ItemTextContainer>
          <ItemTitle>{title}</ItemTitle>
          <ItemDesc>{description}</ItemDesc>
        </ItemTextContainer>
        <ItemTime>{getDataOrTime(createdAt)}</ItemTime>
        <ItemIcon />
      </ItemContainer>
    );
  },
);

2. 채널 화면

  • 채팅은 아래 라이브러리를 사용한다.
    Gifted Chat : https://github.com/FaridSafi/react-native-gifted-chat

  • 메시지 하나하나를 fireStore에 저장한다.
    Gifted Chat 라이브러리에서 메시지 하나하나 입력시 저장되는 형태를 확인하자.

// src/firebase.tsx

export const createMessage = async ({ channelId, message }) => {
  const docRef = doc(db, `channels/${channelId}/messages`, message._id);
  await setDoc(docRef, { ...message, createdAt: Date.now() });
};
  • 메시지 전체를 해당 스크린 마운트시 불러온다.

  • send 버튼의 css는 좀더 공부해야 할듯...

// src/screens/Channel.tsx

const SendButton = (props: { text?: string }) => {
  return (
    <Send
      {...props}
      containerStyle={{
        width: 44,
        height: 44,
        justifyContent: 'center',
        alignItems: 'center',
        marginHorizontal: 4,
      }}
      disabled={!props.text}
    >
      <MaterialIcons name="send" size={24} />
    </Send>
  );
};

const Channel = ({ route }) => {
  const [messages, setMessages] = useState([]);
  const { uid, name, photo } = getCurrentUser();

  const _handleMessageSend = async (messageList: object[]) => {
    // console.log(messageList);
    const message = messageList[0];
    try {
      await createMessage({ channelId: route.params.id, message });
    } catch (e) {
      Alert.alert('Message Error', e.message);
    }
  };

  const db = getFirestore(app);

  useEffect(() => {
    const docRef = doc(db, 'channels', route.params.id);
    const collectionQuery = query(
      collection(db, `${docRef.path}/messages`),
      orderBy('createdAt', 'desc'),
    );
    const unsubscribe = onSnapshot(collectionQuery, snapshot => {
      const list = [];
      snapshot.forEach(doc => {
        list.push(doc.data());
      });
      setMessages(list);
    });
    return () => unsubscribe();
  }, []);

  return (
    <Container>
      <GiftedChat
        placeholder="Enter a message..."
        messages={messages}
        user={{
          _id: uid,
          name: name,
          avatar: photo,
        }}
        onSend={_handleMessageSend}
        scrollToBottom={true}
        renderUsernameOnMessage={true}
        alwaysShowSend={true}
        renderSend={props => SendButton(props)}
      />
    </Container>
  );
};

export default Channel;
profile
개초보

0개의 댓글