import { View, Text } from "react-native"; import React, { useState, useCallback, useEffect } from "react"; import { GiftedChat, Send, InputToolbar, Composer, 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 requireAPI from "../../../utils/requireAPI"; import { GestureHandlerRootView } from "react-native-gesture-handler"; import OwnBubble from "./components/OwnBubble"; 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 [currentHeight, setCurrentHeight] = useState(null); //获取本地自身数据 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 () => { try { const _data = await requireAPI( "POST", "/api/streamer/list_ext_by_mid", { body: { mid: params.mid }, } ); if (_data.ret === -1) { Toast.show({ type: "error", text1: _data.msg, topOffset: 60, }); return; } navigation.setOptions({ title: _data.data.streamer_ext.name, }); await sendAutoMessages( _data.data.streamer_ext?.name, _data.data.streamer_ext?.avatar?.images[0]?.urls[0], _data.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 () => { try { const account = await get("account"); const detailData = await requireAPI( "POST", "/api/contact_customer_service_session/list_by_mid", { body: { mid: account.mid }, } ); 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 () => { try { const account = await get("account"); const createData = await requireAPI( "POST", "/api/contact_customer_service_session/create", { body: { sub_mid: account.mid, obj_mid: 0 }, } ); if (createData.ret === -1) { Toast.show({ type: "error", text1: createData.msg, topOffset: 60, }); return; } setSessionId(createData.data.session_id); return 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; try { const data = await requireAPI( "POST", "/api/contact_customer_service/list_by_session_id", { body: { session_id: sessionId, offset: offset, limit: 12 }, } ); 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, mType: item.m_type, 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, mType: item.m_type, 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; try { const data = await requireAPI( "POST", "/api/contact_customer_service/list_by_session_id", { body: { session_id: sessionId, offset: 0, limit: 1 }, } ); 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, mType: item.m_type, 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, mType: item.m_type, user: { _id: 1, name: "客服", avatar: require("../../../assets/icon.png"), }, }; } }); setMessages((prev) => { if (prev[0]?._id === temMessages[0]?._id) { return prev; } else { // console.log("temMessages", temMessages, "prev", prev); const newMessages = [...temMessages, ...prev].filter( (item, index, self) => index === self.findIndex((t) => t.id === item.id) ); return [...temMessages, ...prev]; } }); if (temMessages.length > 0) { const data2 = await requireAPI( "POST", "/api/contact_customer_service/read_all", { body: { session_id: sessionId, }, } ); if (data2.ret === -1) { Toast.show({ icon: "fail", content: data2.msg, position: "top", }); return; } } } 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 let _sessionId; if (!sessionId) _sessionId = await createSession(); //查询历史记录的时候后移一位,防止记录重复 setOffset((prev) => prev + 1); //请求接口发送私信 try { const data = await requireAPI( "POST", "/api/contact_customer_service/create", { body: { session_id: sessionId ? sessionId : _sessionId, predicate: 0, message: messages[0].text, }, } ); 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 ; }, [currentHeight, sessionId] ); //加载更早信息样式 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} // renderMessageText={renderMessageText} 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], }} /> ); }