React native build App chat bằng Pubnub

Pubnub là framework cung cấp Javascript SKD và sử dụng real-time khi chat không thua kém gì firebase. Pubnub tạo ra các channel để nhiều user tham gia.

  1. Cấu hình

Ở bài này mình sẽ không nói chi tiết phần bắt đầu tạo app như nào. để bắt đầu 1 dự án React native, bạn có thể tham khảo tại đây.

Đầu tiên, sau khi tạo được dự án thì ta thêm các plugin dưới đây:

npm install pubnub pubnub-react  @react-navigation/native @react-navigation/stack react-native-emoji-selector

2. Cài đặt app

Bây giờ mình khởi tạo các App components chứa provider và view. Mình sẽ tạo Pubnub để có thể sử dụng được PubNubProvider lần sau. Để đảm bảo rằng tính năng được khởi chạy thì phải có được publish và subscribe keys từ tài khoản Pubnub của bạn.
Cuối cùng là cho publish và subscribe keys vào trong app của mình ở ngoài dùng PubNubProvider.

import "react-native-gesture-handler";
import React from "react";
import { NavigationContainer } from "@react-navigation/native";
import { createStackNavigator } from "@react-navigation/stack";
import PubNub from "pubnub";
import { PubNubProvider } from "pubnub-react";
import { EmojiPickerView } from "./views/EmojiPicker";
import { ChatView } from "./views/Chat";
const pubnub = new PubNub({
subscribeKey: "",
publishKey: ""
});
console.disableYellowBox = true;
const Stack = createStackNavigator();
export default function App() {
return (


<Stack.Navigator headerMode="none">
<Stack.Screen name="EmojiPicker" component={EmojiPickerView} />
<Stack.Screen name="Chat" component={ChatView} />
</Stack.Navigator>


);
}

Chọn Emoji

Ở màn này ta sẽ cho emoji để thay cho UUID và avatar cho từng user sử dụng pubnub.setUUID().

import React, { useState } from "react";
import { StyleSheet, Text, SafeAreaView, Button, View } from "react-native";
import EmojiSelector from "react-native-emoji-selector";
export const EmojiPickerView = ({ navigation }) => {
// In here we are soring our currently picked emoji.
const [chosenEmoji, setEmoji] = useState(null);
// This method will be called when our user selects an emoji
const handleEmojiSelected = emoji => {
setEmoji(emoji);
};
// This method will be called when our user wants to continue with
// currently selected emoji - this method will do nothing if user
// didn't pick an emoji.
const handleContinueButton = () => {
if (chosenEmoji !== null) {
navigation.replace("Chat", { emoji: chosenEmoji });
}
};
return (



Pick an emoji that will represent you in the chat!

<View
style={{
...styles.emojiContainer,
...(chosenEmoji === null ? styles.empty : {})
}}
>
{chosenEmoji || ""}

<Button
// If user haven't chosen an emoji, we disable the continue button
disabled={chosenEmoji === null}
style={styles.continueButton}
title="Continue"
onPress={handleContinueButton}
/>

<View style={{ height: "50%" }}>



);
};
const styles = StyleSheet.create({
container: {
flexDirection: "column",
alignItems: "center",
width: "100%",
height: "100%"
},
topContainer: {
flexDirection: "column",
alignItems: "center",
justifyContent: "center",
width: "100%",
height: "50%"
},
hint: {
fontSize: 16,
textAlign: "center",
marginTop: 32
},
continueButton: {
marginVertical: 64,
width: 300
},
emojiContainer: {
width: 64,
height: 64,
marginVertical: 32
},
emoji: {
width: "100%",
height: "100%",
textAlign: "center",
textAlignVertical: "center",
fontSize: 60
},
empty: {
borderWidth: 5,
borderStyle: "dashed",
borderColor: "rgba(0, 0, 0, 0.2)"
}
});

Subscribe channel

Mỗi channel được tạo ra tương ứng với id của phòng chat. và user sẽ subscribe channel để tham gia phòng chat.

import React, { useEffect, useState } from "react";
import {
StyleSheet,
Text,
View,
Button,
SafeAreaView,
TextInput,
KeyboardAvoidingView,
Platform,
Keyboard
} from "react-native";
import { usePubNub } from "pubnub-react";
export const ChatView = ({ route }) => {
// The route prop will be bassed to us thanks to React Navigation.
// It will contain our emoji in route.params.emoji.
const userEmoji = route.params.emoji;
// Here we obtain our PubNub instance thanks to using the provider
const pubnub = usePubNub();
// In next two statements we define the state needed for our chat
const [input, setInput] = useState("");
const [messages, setMessages] = useState([]);
// First we need to set our PubNub UUID and subscribe to chat channel.
// We will use useEffect hook for that.
useEffect(() => {
// We need to make sure that PubNub is defined
if (pubnub) {
// Set the UUID of our user to their chosen emoji
pubnub.setUUID(userEmoji);
// Create a listener that will push new messages to our messages variable
// using the setMessages function.
const listener = {
message: envelope => {
setMessages(msgs => [
...msgs,
{
id: envelope.message.id,
author: envelope.publisher,
content: envelope.message.content,
timetoken: envelope.timetoken
}
]);
}
};
// Add the listener to pubnub instance and subscribe to chat channel.
pubnub.addListener(listener);
pubnub.subscribe({ channels: ["chat"] });
// We need to return a function that will handle unsubscription on unmount
return () => {
pubnub.removeListener(listener);
pubnub.unsubscribeAll();
};
}
}, [pubnub]);

Publish message

Trong màn Chat ta có func handleSubmit để publish 1 message thông qua pubnub.publish(). Ở đây mình ví dụ trước gửi tin nhắn, mình có thể đính kèm thêm tệp, ảnh hoặc ghi âm.

 const handleSubmit = () => {
   setInput("");
   // Tạo message random `id`.
   const message = {
     content: input,
     id: Math.random()
       .toString(16)
       .substr(2)
   };
   // Publish message tới channel `chat`
   pubnub.publish({ channel: "chat", message });
 };

Ở func useEffect mình đã lắng nghe sự kiện listener khi có message thay đổi. để cập nhật lại danh sách tin nhắn và update lại View.

Tổng kết

Và tổng kết lại cũng chỉ đơn giản vậy thôi. còn đi sâu vào nữa sẽ còn phần thông báo, nhận sự kiện typing, seen,... mình sẽ cập nhật vào các bài viết tiếp theo.