tiefen_space_app/screeens/Messages/MessageDetail/index.jsx

590 lines
16 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 (
<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 (
<Bubble
{...props}
wrapperStyle={{
left: tailwind("bg-white p-1"),
right: tailwind("p-1"),
}}
/>
);
}, []);
//加载更早信息样式
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]"),
}}
>
<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}
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>
</View>
);
}