533 lines
14 KiB
JavaScript
533 lines
14 KiB
JavaScript
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 (
|
||
<Send
|
||
{...props}
|
||
disabled={!props.text}
|
||
containerStyle={tailwind(
|
||
"px-4 bg-[#FF669E] justify-center items-center rounded-lg h-10 ml-2"
|
||
)}
|
||
>
|
||
<Text style={tailwind("text-white font-medium text-sm")}>发送</Text>
|
||
</Send>
|
||
);
|
||
}, []);
|
||
|
||
//日期格式
|
||
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 <Day {...props} dateFormat="HH:mm" />;
|
||
} else if (isYesterday) {
|
||
return <Day {...props} dateFormat="昨天 HH:mm" />;
|
||
} else {
|
||
return <Day {...props} dateFormat="YYYY/MM/DD HH:mm" />;
|
||
}
|
||
}, []);
|
||
|
||
//时间格式
|
||
const renderTime = useCallback((props) => {
|
||
return <></>;
|
||
}, []);
|
||
|
||
//输入框样式
|
||
const renderComposer = useCallback((props) => {
|
||
return (
|
||
<Composer
|
||
{...props}
|
||
textInputStyle={tailwind(
|
||
"bg-[#FFFFFF1A] text-white px-4 py-2 rounded-lg text-sm ml-0"
|
||
)}
|
||
textInputProps={{ maxLength: 140 }}
|
||
/>
|
||
);
|
||
}, []);
|
||
|
||
//整个输入栏样式
|
||
const renderInputToolbar = useCallback((props) => {
|
||
if (params?.mid === 1) {
|
||
return (
|
||
<InputToolbar
|
||
renderComposer={() => renderComposer(props)}
|
||
renderSend={() => renderSend(props)}
|
||
containerStyle={tailwind("p-2 bg-[#13121F] border-[#FFFFFF26]")}
|
||
primaryStyle={tailwind("items-center")}
|
||
/>
|
||
);
|
||
} else {
|
||
return <View style={tailwind("flex-1")}></View>;
|
||
}
|
||
}, []);
|
||
|
||
//头像样式
|
||
const renderAvatar = useCallback((props) => {
|
||
return (
|
||
<Image
|
||
style={tailwind("w-10 h-10 rounded-full")}
|
||
source={props.currentMessage.user.avatar}
|
||
placeholder={blurhash}
|
||
contentFit="cover"
|
||
transition={1000}
|
||
cachePolicy="disk"
|
||
/>
|
||
);
|
||
}, []);
|
||
|
||
//气泡样式
|
||
const renderBubble = useCallback(
|
||
(props) => {
|
||
return <OwnBubble {...props} />;
|
||
},
|
||
[currentHeight, sessionId]
|
||
);
|
||
|
||
//加载更早信息样式
|
||
const renderLoadEarlier = useCallback(
|
||
(props) => {
|
||
return (
|
||
<LoadEarlier
|
||
{...props}
|
||
label={more === 0 || params.mid !== 1 ? "无更早消息" : "查看更早"}
|
||
wrapperStyle={tailwind("bg-[#FFFFFF1A]")}
|
||
/>
|
||
);
|
||
},
|
||
[more]
|
||
);
|
||
|
||
return (
|
||
<View
|
||
style={{
|
||
paddingBottom: insets.bottom,
|
||
paddingLeft: insets.left,
|
||
paddingRight: insets.right,
|
||
...tailwind("flex-1 bg-[#13121F]"),
|
||
}}
|
||
>
|
||
<GestureHandlerRootView>
|
||
<View style={tailwind("flex-1")}>
|
||
<GiftedChat
|
||
placeholder={params?.mid === 1 ? "输入新消息" : "爱就大胆说出来!"}
|
||
alwaysShowSend
|
||
locale={"zh-cn"}
|
||
keyboardShouldPersistTaps="never"
|
||
alignTop={false}
|
||
listViewProps={{
|
||
contentContainerStyle: {
|
||
flexGrow: 1,
|
||
justifyContent: "flex-end",
|
||
},
|
||
}}
|
||
onLoadEarlier={() => 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],
|
||
}}
|
||
/>
|
||
</View>
|
||
</GestureHandlerRootView>
|
||
</View>
|
||
);
|
||
}
|