tiefen_space_h5/app/messageDetail/page.js

487 lines
14 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.

"use client";
import React, { useState, useRef, useEffect, useCallback } from "react";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import { faAngleLeft } from "@fortawesome/free-solid-svg-icons";
import { Input, Button, Toast, Avatar, DotLoading } from "antd-mobile";
import { useRouter, useSearchParams } from "next/navigation";
import { get } from "@/utils/storeInfo";
import requireAPI from "@/utils/requireAPI";
import { formatDeadline } from "@/utils/tools";
import { getStreamerDetailInfo } from "@/api/space";
/*
params格式
{
mid: item.mid,
}
*/
export default function MessageDetail({}) {
// const [hasMore, setHasMore] = useState(true);
const router = useRouter();
const searchParams = useSearchParams();
const [oldMessages, setOldMessages] = useState([]);
const [messages, setMessages] = useState([]);
const [handledmessages, setHandledmessages] = useState([]);
const [sessionId, setSessionId] = useState();
const [userInfo, setUserInfo] = useState(null);
const [newMessage, setNewMessage] = useState("");
const [loading, setLoading] = useState(false);
const [offset, setOffset] = useState(0);
const [more, setMore] = useState(1);
const scrollBox = useRef();
const toScrollBottom = useRef(0);
useEffect(() => {
const userData = get("account");
const mid = searchParams.get("mid");
if (mid) {
getStreamerDetailInfo(Number(mid)).then((data) => {
setMessages([
[
{
predicate: 1,
_id: 1,
createdAt: new Date() / 1000,
text: data?.streamer_ext.auto_response_message,
user: {
_id: 0,
name: data?.streamer_ext.name,
avatar: data?.streamer_ext?.avatar?.images[0]?.urls[0],
},
},
],
]);
});
} else {
setUserInfo(userData);
getSession(userData.mid);
}
}, []);
useEffect(() => {
const intervalId = setInterval(() => {
// console.log("oldMessages[0]", oldMessages[0]);
if (oldMessages[0]) {
updateMessages(oldMessages[0]?.id, 0, oldMessages);
// toScrollBottom.current = 1;
}
}, 3000); // 间隔时间为3秒
// 在组件卸载时清除定时器
return () => {
clearInterval(intervalId);
};
}, [oldMessages]);
useEffect(() => {
if (toScrollBottom.current) {
scrollBox.current?.scrollTo(0, scrollBox.current.scrollHeight + 50);
toScrollBottom.current = 0;
}
}, [messages]);
useEffect(() => {
if (sessionId && userInfo.mid) {
loadEarlierHistory().then((res) => {
setMessages((old) => {
toScrollBottom.current = 1;
return res;
});
});
}
}, [sessionId]);
//查询session
const getSession = async (mid) => {
setLoading(true);
try {
const data = await requireAPI(
"POST",
"/api/contact_customer_service_session/list_by_mid",
{
body: {
mid: Number(mid),
},
}
);
if (data.ret === -1) {
Toast.show({
icon: "fail",
content: data.msg,
position: "top",
});
}
setLoading(false);
if (data.data.session) {
setSessionId(data.data.session.id);
} else {
//如果是第一次发送需要创建session
createSession(mid);
}
} catch (error) {
// console.error(error);
}
};
//创建session
const createSession = async (mid) => {
setLoading(true);
try {
const data = await requireAPI(
"POST",
"/api/contact_customer_service_session/create",
{
body: {
sub_mid: Number(mid),
obj_mid: 0,
},
}
);
if (data.ret === -1) {
Toast.show({
icon: "fail",
content: data.msg,
position: "top",
});
}
setSessionId(data.data.session_id);
setLoading(false);
} catch (error) {
// console.error(error);
}
};
//请求历史记录
const loadEarlierHistory = async () => {
if (!more) return;
try {
setLoading(true);
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({
icon: "fail",
content: data.msg,
position: "top",
});
return;
}
setOffset(data.data.offset);
setMore(data.data.more);
let mathNewMessages = handleData([...oldMessages, ...data.data.list]);
// setMessages((prev) => [...prev, ...temMessages]);
setLoading(false);
return mathNewMessages;
} catch (error) {
// console.error(error);
}
};
//发送私信功能
const onSend = useCallback(
async (message, lastId, oldArr) => {
if (message == "") {
// Toast.show({
// icon: "error",
// content: "不可发送空内容",
// position: "top",
// });
return;
}
//查询历史记录的时候后移一位,防止记录重复
setOffset((prev) => prev + 1);
//请求接口发送私信
try {
const data = await requireAPI(
"POST",
"/api/contact_customer_service/create",
{
body: {
session_id: sessionId,
predicate: 0,
message,
},
}
);
if (data.ret === -1) {
Toast.show({
icon: "error",
content: data.msg,
position: "top",
});
return;
}
// updateLatestHistory();
// console.log("oldArr", oldArr);
updateMessages(lastId, 0, oldArr).then((res) => {
setNewMessage("");
toScrollBottom.current = 1;
});
} catch (error) {
// console.error(error);
}
// //每次发送都缓存信息到本地
// addArr(`${selfData.mid}_to_${params.mid}_messages`, messages);
},
[userInfo, sessionId]
);
// async function doRefresh() {
// await sleep(1000);
// Toast.show({
// icon: "fail",
// content: "刷新失败",
// });
// throw new Error("刷新失败");
// }
async function loadMore() {
if (sessionId && userInfo.mid && offset && more) {
const append = await loadEarlierHistory();
if (append) {
// setMessages((val) => [...val, ...append]);
setMessages(append);
// setHasMore(append.length > 0);
}
}
}
const handleData = (list) => {
// console.log("list", list);
const account = get("account");
const temMessages = list.map((item) => {
if (item.predicate === 0) {
return {
predicate: item.predicate,
_id: item.id,
// createdAt: new Date(item.ct * 1000).toISOString(),
createdAt: item.ct,
text: item.message,
user: {
_id: account?.mid,
name: account?.name,
avatar: account?.avatar?.images[0]?.urls[0],
},
};
} else {
return {
predicate: item.predicate,
_id: item.id,
createdAt: item.ct,
text: item.message,
user: {
_id: 0,
name: "客服",
avatar: process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/images/icon.png",
},
};
}
});
// console.log("handledmessages......", handledmessages);
// console.log("[...messages, ...temMessages]", temMessages);
setHandledmessages(temMessages);
setOldMessages(list);
let newMessages = temMessages.reverse();
let mathNewMessages = newMessages.reduce(
(accumulator, currentValue, index, sourceArray) => {
// console.log(
// accumulator.time,
// "----",
// currentValue.createdAt,
// "---",
// index
// );
if (accumulator.time > currentValue.createdAt) {
let newData = {
...accumulator,
time: accumulator.time,
messages: [...accumulator.messages, currentValue],
totalArr:
index == sourceArray.length - 1
? [
...accumulator.totalArr,
[...accumulator.messages, currentValue],
]
: [...accumulator.totalArr],
};
return newData;
} else {
let newData = {
...accumulator,
time: currentValue.createdAt + 60 * 3,
messages: [currentValue],
totalArr:
index == sourceArray.length - 1
? [
...accumulator.totalArr,
accumulator.messages,
[currentValue],
]
: [...accumulator.totalArr, accumulator.messages],
};
return newData;
}
},
{ time: newMessages[0]?.createdAt + 60 * 3, messages: [], totalArr: [] }
);
return mathNewMessages.totalArr;
};
const updateMessages = async (lastId, currentOffset, oldArr) => {
// console.log("lastId", lastId);
try {
const data = await requireAPI(
"POST",
"/api/contact_customer_service/list_by_session_id",
{
body: {
session_id: sessionId,
offset: currentOffset,
limit: 12,
},
}
);
if (data.ret === -1) {
Toast.show({
icon: "fail",
content: data.msg,
position: "top",
});
return;
}
let newData = data.data.list.filter((element) => {
return element.id > lastId;
});
// console.log("[...messages,...newData]", [...newData]);
let mathNewMessages = handleData([...newData, ...oldArr]);
setMessages((old) => {
toScrollBottom.current = 1;
return mathNewMessages;
});
return;
} catch (error) {
console.error(error);
}
};
return (
<div className="bg-[#13121F] h-screen overflow-y-auto" ref={scrollBox}>
<div className="p-4 fixed top-0 z-10 w-full bg-deepBg">
<div className="flex items-center justify-center absolute">
<FontAwesomeIcon
icon={faAngleLeft}
style={{ maxWidth: "12px" }}
size="xl"
onClick={() => {
router.back();
}}
/>
</div>
<p className="text-base text-center">
{!searchParams.get("mid") ? "在线客服" : messages?.[0]?.[0].user.name}
</p>
</div>
<div>
<div className="my-[57px]">
{!searchParams.get("mid") && (
<div className="flex justify-center py-2">
<div className="px-3 py-2 rounded-full bg-[#FFFFFF1A]">
{loading ? (
<DotLoading />
) : more ? (
<span onClick={loadMore}>查看更早</span>
) : (
<span>无更早消息</span>
)}
</div>
</div>
)}
<ul className="py-2">
{messages?.map((item, index) => (
<li key={index}>
<p className="my-2 text-center">
{formatDeadline(item[0].createdAt)}
</p>
<ul className="px-4 overflow-y-auto scrollbarBox_hidden">
{item.map((it) => (
<li key={it?._id} className="py-3 rounded-lg ">
<div className="flex w-full">
{it?.predicate == 1 ? (
<div className="flex justify-start w-full">
<Avatar
className="mr-2 w-[32px] h-[32px]"
style={{ "--border-radius": "50px" }}
width={32}
height={32}
src={it?.user.avatar}
/>
<div
className="rounded-lg py-2 px-3 bg-blue-500 break-words"
style={{
borderTopLeftRadius: 0,
maxWidth: "calc(100% - 32px - 0.75rem)",
}}
>
{it?.text}
</div>
</div>
) : (
<div className="flex justify-end w-full">
<div
className="max-w-full rounded-lg py-2 px-3 bg-blue-500 break-words"
style={{
borderTopRightRadius: 0,
maxWidth: "calc(100% - 32px - 0.75rem)",
}}
>
{it?.text}
</div>
<Avatar
className="ml-2 w-[32px] h-[32px]"
style={{ "--border-radius": "50px" }}
width={32}
height={32}
src={it?.user?.avatar}
/>
</div>
)}
</div>
</li>
))}
</ul>
</li>
))}
{}
{/* <InfiniteScroll loadMore={loadMore} hasMore={more} /> */}
</ul>
</div>
{!searchParams.get("mid") && (
<div className="w-full h-16 fixed bottom-0 grid grid-cols-[1fr_68px] bg-black items-center p-2 border-t-2 border-[#ffffff2a]">
<div className="rounded bg-[#222036] px-4 py-2 mr-2">
<Input
placeholder="输入新消息"
className=""
value={newMessage}
onChange={setNewMessage}
style={{ "--font-size": "16px" }}
/>
</div>
<Button
size="middle"
block
onClick={() =>
onSend(newMessage, oldMessages[0]?.id || -1, oldMessages)
}
style={{ "--background-color": "#FF669E", color: "#FFFFFF" }}
>
发送
</Button>
</div>
)}
</div>
</div>
);
}