Agora, quando a gente envia a primeira mensagem, a gente pode ver que ela está bugada aqui, no lado errado e sem uma key única, vamos em messageList
interface Props {
messages: Messages[];
}
const MessagesList = ({ messages }: Props) => {
const renderItem: ListRenderItem<Messages> = ({ item }) => (
<MessageCard item={item} />
);
return (
<FlatList
data={messages}
inverted
keyExtractor={(item, index) => item._id + index.toString()}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingTop: 20 }}
/>
);
Agora que estamos tipando certinho, vamos para o card e fazer a modificação lá, que estão incorretas
interface Props {
item: Messages;
}
const MessageCard = ({ item }: Props) => {
const [senderId, setSenderId] = useState("");
const handleGetUser = async () => {
const user = await AsyncStorage.getItem("user");
const { _id } = JSON.parse(user || "");
setSenderId(_id);
};
useEffect(() => {
handleGetUser();
}, []);
return (
<>
{item.sender === senderId ? (
<SenderMsgContainer>
<SenderMsg>{item.content}</SenderMsg>
</SenderMsgContainer>
) : (
<ReciverMsgContainer>
<ReciverMsg>{item.content}</ReciverMsg>
</ReciverMsgContainer>
)}
</>
);
};
Antes de irmos pegar todos os chats, vamos primeiro tipar tudo dentro de messages
import { Product, Seller } from "./Product";
export interface Messages {
_id?: string;
content: string;
receiver: string;
sender: string;
}
export interface Buyer {
_id: string;
email: string;
name: string;
phone: string;
}
export interface Chats {
_id: string;
buyer: Buyer;
messages: Messages[];
product: Product;
seller: Seller;
updatedAt: string;
}
Desse jeito agora temos tudo certo aqui nos chats, vamos então pegar todos os chats para exibir em allChats. Iremos para dentro de ChatList, que está dentro de components, com isso feito, nós vamos em ChatList, que é o componente que exibe para os usuários os chats que temos, e aqui vamos modificar algumas coisas
const ChatList = () => {
const [loading, setLoading] = useState(true);
const [chats, setChats] = useState<Chats[]>([]);
const renderItem: ListRenderItem<Chats> = ({ item }) => (
<ChatCard data={item} />
);
const handleGetChats = async () => {
const res = await chatService.getChats();
setChats(res.data);
setLoading(false);
};
useFocusEffect(
React.useCallback(() => {
handleGetChats();
}, [])
);
if (loading) {
return <Loader />;
}
return (
<>
{chats.length <= 0 ? (
<NoChat>Você não tem chats no momento</NoChat>
) : (
<FlatList
data={chats}
keyExtractor={(item) => item._id}
renderItem={renderItem}
showsVerticalScrollIndicator={false}
contentContainerStyle={{ paddingBottom: 80 }}
/>
)}
</>
);
};
E agora conhecendo o useFocus, vamos usar ele também na home, para que todas as vezes que a gente acesse ela, se tiver algum produto novo ele vai aparecer
useFocusEffect(
React.useCallback(() => {
handleGetProducts();
}, [])
);
Agora que a gente já tem a estrutura pronta, vamos para o card, que é o mais importante de fato
interface Props {
data: Chats;
}
const ChatCard = ({ data }: Props) => {
const navigation = useNavigation<PropsStack>();
return (
<Container
activeOpacity={0.85}
onPress={() => {
navigation.navigate("Chat", {
product: data.product,
sellerName: data.seller.name,
sellerId: data.seller._id,
messages: data.messages,
});
}}
>
<Image source={{ uri: data.product.images[0].url }} />
<InfoContainer>
<Price>R$ {data.product.price}</Price>
<Title numberOfLines={2}>{data.product.name}</Title>
<LikeContainer>
<SellerContainer>
<PublishedText>
Publicado em {getDate(data.product.createdAt)} por:
</PublishedText>
<SellerName>{data.seller.name}</SellerName>
</SellerContainer>
<TrashButton onPress={() => {}} activeOpacity={0.85}>
<TrashImage source={trashIcon} />
</TrashButton>
</LikeContainer>
</InfoContainer>
</Container>
);
};
Agora, já conseguimos navegar livremente para dentro do nosso chat
Vamos então buscar enviar mensagens aqui dentro do chat, isso lá em Chat. Vamos começar instalando as dependências do socketio, que será a lib que usaremos para começar isso
npm install socket.io-client
Agora, nós podemos fazer a conexão com o socket
let socket: Socket | null = null;
const connectSocket = async () => {
const token = await SecureStore.getItemAsync("onebitshop-token");
socket = io("ws://192.168.0.10:3000", {
auth: {
token: token,
},
});
};
connectSocket();
const Chat = ({ route }: Props) => {
Com ele conectado, podemos criar a estrutura que vai fazer a manipulação das mensagens
const Chat = ({ route }: Props) => {
const [content, setContent] = useState("");
const [receiver, setReceiver] = useState("");
const [sender, setSender] = useState("");
const [messageList, setMessageList] = useState<Messages[]>([]);
const handleGetUsers = async () => {
const user = await AsyncStorage.getItem("user");
const { _id } = JSON.parse(user || "");
if (route.params.buyerId === _id) {
setSender(route.params.buyerId);
setReceiver(route.params.sellerId);
} else {
setSender(route.params.sellerId);
setReceiver(route.params.buyerId);
}
};
const handleSendMessage = async () => {
const message = {
content,
receiver,
sender,
};
const conversationId = route.params._id;
socket?.emit("message_sent", {
conversationId,
message,
});
setContent("");
};
useEffect(() => {
setMessageList(route.params.messages);
handleGetUsers();
}, []);
useEffect(() => {
socket?.on("new_message", (newMessage: any) => {
console.log(newMessage.message);
setMessageList((mostRecentState) => [
newMessage.message,
...mostRecentState,
]);
});
return () => {
socket?.off("new_message");
};
}, [socket]);
E agora podemos usar tudo que a gente criou
return (
<Container>
<ChatHeader
sellerName={route.params.sellerName}
sellerId={route.params.sellerId}
product={route.params.product}
/>
<MessagesList messages={messageList} />
<InputContainer>
<Input
placeholder="Digite sua mensagem"
placeholderTextColor="white"
multiline
value={content}
onChangeText={(val) => {
setContent(val);
}}
/>
<SendButton onPress={handleSendMessage}>
<SendIcon source={chatImage} />
</SendButton>
</InputContainer>
<NavBar />
</Container>
);
E agora sempre que enviarmos uma mensagem, poderemos ver ela aqui atualizando no chat