import { View, Text, Platform } from "react-native"; import React, { useState, useCallback, useEffect } from "react"; import { GiftedChat, Send, InputToolbar, Composer, Bubble, Day, LoadEarlier, } from "react-native-gifted-chat"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useTailwind } from "tailwind-rn"; import "dayjs/locale/zh-cn"; import dayjs from "dayjs"; import { Image } from "expo-image"; import { get } from "../../../utils/storeInfo"; import Toast from "react-native-toast-message"; import baseRequest from "../../../utils/baseRequest"; import { generateSignature } from "../../../utils/crypto"; const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk"; /* params格式: { mid: item.mid, } */ export default function MessageDetail({ navigation, route }) { const tailwind = useTailwind(); const insets = useSafeAreaInsets(); const params = route.params; const [messages, setMessages] = useState([]); //获取本地自身数据 const [selfData, setSelfData] = useState({}); useEffect(() => { async function getData() { try { const account = await get("account"); if (account) { setSelfData(account); } } catch (e) { console.error(e); } } getData(); }, []); //获取主播数据发送自动回复信息并设置页面标题 useEffect(() => { const getData = async () => { const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const signature = await generateSignature({ mid: params.mid, ...base, }); const detailResponse = await fetch( `${apiUrl}/api/streamer/list_ext_by_mid?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mid: params.mid, ...base, }), } ); const detailData = await detailResponse.json(); if (detailData.ret === -1) { Toast.show({ type: "error", text1: detailData.msg, topOffset: 60, }); return; } navigation.setOptions({ title: detailData.data.streamer_ext.name, }); await sendAutoMessages( detailData.data.streamer_ext?.name, detailData.data.streamer_ext?.avatar?.images[0]?.urls[0], detailData.data.streamer_ext?.auto_response_message ); } catch (error) { console.error(error); } }; if (params?.mid === 1) { navigation.setOptions({ title: "在线客服", }); } else { getData(); } }, []); //显示主播自动回复 const sendAutoMessages = async (name, avatar, content) => { const temMessages = [ { _id: 1, text: content, createdAt: new Date(), user: { _id: 1, name: name, avatar: avatar, }, }, ]; setMessages(temMessages); }; //读取本地缓存的聊天记录 // const loadMessages = async () => { // const accout = await get("account"); // const selfMid = accout.mid; // const messagesCache = await get(`${selfMid}_to_${params.mid}_messages`); // if (messagesCache) { // setMessages(messagesCache); // } // }; //查询session const [sessionId, setSessionId] = useState(); useEffect(() => { const getSession = async () => { const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const account = await get("account"); const signature = await generateSignature({ mid: account.mid, ...base, }); const detailResponse = await fetch( `${apiUrl}/api/contact_customer_service_session/list_by_mid?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ mid: account.mid, ...base, }), } ); const detailData = await detailResponse.json(); if (detailData.ret === -1) { Toast.show({ type: "error", text1: detailData.msg, topOffset: 60, }); return; } if (detailData.data.session) { setSessionId(detailData.data.session.id); return; } } catch (error) { console.error(error); } }; getSession(); }, []); //创建session const createSession = async () => { const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const account = await get("account"); const signature = await generateSignature({ sub_mid: account.mid, obj_mid: 0, ...base, }); const createResponse = await fetch( `${apiUrl}/api/contact_customer_service_session/create?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ sub_mid: account.mid, obj_mid: 0, ...base, }), } ); const createData = await createResponse.json(); if (createData.ret === -1) { Toast.show({ type: "error", text1: createData.msg, topOffset: 60, }); return; } setSessionId(createData.data.session_id); } catch (error) { console.error(error); } }; //请求历史记录 const [offset, setOffset] = useState(1); const [more, setMore] = useState(1); const loadEarlierHistory = async () => { if (params?.mid !== 1) return; if (!more) return; if (sessionId === undefined) return; const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const signature = await generateSignature({ session_id: sessionId, offset: offset, limit: 12, ...base, }); const response = await fetch( `${apiUrl}/api/contact_customer_service/list_by_session_id?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ session_id: sessionId, offset: offset, limit: 12, ...base, }), } ); const data = await response.json(); if (data.ret === -1) { Toast.show({ type: "error", text1: data.msg, topOffset: 60, }); return; } setOffset(data.data.offset); setMore(data.data.more); const account = await get("account"); const temMessages = data.data.list.map((item) => { if (item.predicate === 0) { return { _id: item.id, createdAt: new Date(item.ct * 1000).toISOString(), text: item.message, user: { _id: account?.mid, name: account?.name, avatar: account?.avatar?.images[0]?.urls[0], }, }; } else { return { _id: item.id, createdAt: new Date(item.ct * 1000).toISOString(), text: item.message, user: { _id: 0, name: "客服", avatar: require("../../../assets/icon.png"), }, }; } }); setMessages((prev) => [...prev, ...temMessages]); } catch (error) { console.error(error); } }; //请求最新1条历史记录,如果与之前不同则加在第1条 const updateLatestHistory = async () => { if (params?.mid !== 1) return; if (sessionId === undefined) return; const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const signature = await generateSignature({ session_id: sessionId, offset: 0, limit: 1, ...base, }); const response = await fetch( `${apiUrl}/api/contact_customer_service/list_by_session_id?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ session_id: sessionId, offset: 0, limit: 1, ...base, }), } ); const data = await response.json(); if (data.ret === -1) { Toast.show({ type: "error", text1: data.msg, topOffset: 60, }); return; } const account = await get("account"); const temMessages = data.data.list.map((item) => { if (item.predicate === 0) { return { _id: item.id, createdAt: new Date(item.ct * 1000).toISOString(), text: item.message, user: { _id: account.mid, name: account.name, avatar: account.avatar.images[0].urls[0], }, }; } else { return { _id: item.id, createdAt: new Date(item.ct * 1000).toISOString(), text: item.message, user: { _id: 1, name: "客服", avatar: require("../../../assets/icon.png"), }, }; } }); setMessages((prev) => { if (prev[0]?._id === temMessages[0]?._id) { return prev; } else { return [...temMessages, ...prev]; } }); } catch (error) { console.error(error); } }; // 轮询更新历史记录 useEffect(() => { loadEarlierHistory(); updateLatestHistory(); // 设置轮询请求,每隔一定时间执行一次 const intervalId = setInterval(() => { updateLatestHistory(); }, 3000); // 间隔时间为3秒 // 在组件卸载时清除定时器 return () => { clearInterval(intervalId); }; }, [sessionId]); //发送私信功能 const onSend = useCallback( async (messages = []) => { if (!messages[0].text) { Toast.show({ type: "error", text1: "不可发送空内容", topOffset: 60, }); return; } //如果是第一次发送,需要创建session if (!sessionId) await createSession(); //查询历史记录的时候后移一位,防止记录重复 setOffset((prev) => prev + 1); //请求接口发送私信 const apiUrl = process.env.EXPO_PUBLIC_API_URL; try { const base = await baseRequest(); const signature = await generateSignature({ session_id: sessionId, predicate: 0, message: messages[0].text, ...base, }); const response = await fetch( `${apiUrl}/api/contact_customer_service/create?signature=${signature}`, { method: "POST", headers: { "Content-Type": "application/json", }, body: JSON.stringify({ session_id: sessionId, predicate: 0, message: messages[0].text, ...base, }), } ); const data = await response.json(); if (data.ret === -1) { Toast.show({ type: "error", text1: data.msg, topOffset: 60, }); return; } updateLatestHistory(); } catch (error) { console.error(error); } // //每次发送都缓存信息到本地 // addArr(`${selfData.mid}_to_${params.mid}_messages`, messages); }, [selfData, sessionId] ); //发送按钮样式 const renderSend = useCallback((props) => { return ( 发送 ); }, []); //日期格式 const renderDay = useCallback((props) => { const now = dayjs(); const isToday = now.diff(props.currentMessage.createdAt, "day") === 0; const isYesterday = now.diff(props.currentMessage.createdAt, "day") === 1; if (isToday) { return ; } else if (isYesterday) { return ; } else { return ; } }, []); //时间格式 const renderTime = useCallback((props) => { return <>; }, []); //输入框样式 const renderComposer = useCallback((props) => { return ( ); }, []); //整个输入栏样式 const renderInputToolbar = useCallback((props) => { if (params?.mid === 1) { return ( renderComposer(props)} renderSend={() => renderSend(props)} containerStyle={tailwind("p-2 bg-[#13121F] border-[#FFFFFF26]")} primaryStyle={tailwind("items-center")} /> ); } else { return ; } }, []); //头像样式 const renderAvatar = useCallback((props) => { return ( ); }, []); //气泡样式 const renderBubble = useCallback((props) => { return ( ); }, []); //加载更早信息样式 const renderLoadEarlier = useCallback( (props) => { return ( ); }, [more] ); return ( loadEarlierHistory()} showUserAvatar showAvatarForEveryMessage renderAvatarOnTop messagesContainerStyle={tailwind("bg-[#13121F] pb-4")} renderAvatar={renderAvatar} renderDay={renderDay} renderInputToolbar={renderInputToolbar} renderBubble={renderBubble} loadEarlier renderLoadEarlier={renderLoadEarlier} renderTime={renderTime} messages={messages} onSend={(messages) => onSend(messages)} user={{ _id: selfData?.mid, name: selfData?.name, avatar: selfData?.avatar?.images[0]?.urls[0], }} /> ); }