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;
채널 생성 시간은 moment 라이브러리를 사용한다.
공식홈페이지 : https://momentjs.com/
설명 참조 : https://flamingotiger.github.io/javascript/momentjs/
설명 참조 : https://freestrokes.tistory.com/119
now(현재시간) 과 target(채널 생성 시간)에서 날짜가 같으면 시간/분 으로 다르다면 월/일 으로 표현한다.
// 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>
);
},
);
채팅은 아래 라이브러리를 사용한다.
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;