457 lines
16 KiB
JavaScript
457 lines
16 KiB
JavaScript
"use client";
|
||
|
||
import React, { useEffect, useState, useMemo, useCallback } from "react";
|
||
import Photos from "../Photos";
|
||
import { useRouter } from "next/navigation";
|
||
import PaySpacePost from "../PaySpacePost";
|
||
import { Image, Popover, Divider, Toast } from "antd-mobile";
|
||
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
|
||
import { faAngleRight } from "@fortawesome/free-solid-svg-icons";
|
||
import { handleFollow, thumbsUp, handleBlock } from "@/api/public";
|
||
import { get } from "@/utils/storeInfo";
|
||
import { Inter } from "next/font/google";
|
||
import requireAPI from "@/utils/requireAPI";
|
||
const inter = Inter({ subsets: ["latin"] });
|
||
export default function PostItem({
|
||
type,
|
||
follow,
|
||
date = new Date(),
|
||
data = {},
|
||
}) {
|
||
const router = useRouter();
|
||
const [isOpenText, setIsOpenText] = useState(false);
|
||
const [popoverVisible, setPopoverVisible] = useState(false);
|
||
const [isFollow, setIsFollow] = useState(data.is_followed);
|
||
const [isThumbsUp, setIsThumbsUp] = useState(
|
||
data?.is_thumbed_up || data?.is_zone_moment_thumbed_up
|
||
);
|
||
const [account, setAccount] = useState(
|
||
data?.is_thumbed_up || data?.is_zone_moment_thumbed_up
|
||
);
|
||
//判断是否是发帖人
|
||
const [isCreator, setIsCreator] = useState(false);
|
||
useEffect(() => {
|
||
const currentAccount = get("account");
|
||
setAccount(currentAccount);
|
||
if (currentAccount?.mid === data.mid) setIsCreator(true);
|
||
return () => {
|
||
router.prefetch("/profile/" + data.mid);
|
||
};
|
||
}, []);
|
||
const getDays = useMemo(() => {
|
||
const today = new Date();
|
||
const days = Math.floor((today - data.ct) / (1000 * 60 * 60 * 24));
|
||
return days;
|
||
}, []);
|
||
function findLinksInText(text) {
|
||
// 定义一个正则表达式来匹配URL
|
||
// 注意:这个正则表达式可能无法匹配所有可能的URL,但可以匹配大部分常见的格式
|
||
var urlPattern = /https?:\/\/[^\s\/$.?#].[^\s]*/g;
|
||
|
||
// 使用正则表达式的exec或match方法查找匹配项
|
||
// exec在全局搜索中需要循环调用,而match在全局模式下直接返回所有匹配项
|
||
var matches = text?.match(urlPattern);
|
||
|
||
// // 如果没有找到匹配项,返回空数组
|
||
// if (!matches) {
|
||
// return text;
|
||
// }
|
||
|
||
if (matches) {
|
||
matches?.forEach((el) => {
|
||
// console.log("index", text.indexOf(el));
|
||
text = text.replace(
|
||
el,
|
||
`<a className="text-btn" href="${el}" target="_blank">#网页链接</a>`
|
||
);
|
||
});
|
||
// console.log("matches", matches);
|
||
// console.log("text", text);
|
||
}
|
||
|
||
// 返回找到的链接数组
|
||
return text;
|
||
}
|
||
const formatZoneUpdateTime = useCallback((time) => {
|
||
if (time === 0) return "今日";
|
||
if (time === 1) return "昨日";
|
||
return `${time}日前`;
|
||
}, []);
|
||
const handleDelete = async () => {
|
||
try {
|
||
const _data = await requireAPI("POST", "/api/zone_moment/delete", {
|
||
body: {
|
||
id: data?.id,
|
||
},
|
||
});
|
||
if (_data.ret === -1) {
|
||
Toast.show({
|
||
icon: "fail",
|
||
content: _data.msg,
|
||
position: "top",
|
||
});
|
||
return;
|
||
}
|
||
Toast.show({
|
||
icon: "success",
|
||
content: "删除成功,请刷新页面",
|
||
position: "top",
|
||
});
|
||
return;
|
||
} catch (error) {
|
||
console.error(error);
|
||
} finally {
|
||
setPopoverVisible(false);
|
||
}
|
||
};
|
||
//置顶
|
||
const handleTop = async () => {
|
||
try {
|
||
const body = {
|
||
zone_moment_ids: [data.id],
|
||
op_type: data.is_headed ? 0 : 1,
|
||
};
|
||
const _data = await requireAPI("POST", "/api/zone_moment/head", {
|
||
body,
|
||
});
|
||
if (_data.ret === -1) {
|
||
Toast.show({
|
||
icon: "fail",
|
||
content: _data.msg,
|
||
position: "top",
|
||
});
|
||
return;
|
||
}
|
||
Toast.show({
|
||
icon: "success",
|
||
content: "操作成功,请刷新页面查看",
|
||
position: "top",
|
||
});
|
||
return;
|
||
} catch (error) {
|
||
console.error(error);
|
||
} finally {
|
||
setPopoverVisible(false);
|
||
}
|
||
};
|
||
return (
|
||
<div>
|
||
{type == "space" && data?.is_headed === 1 && (
|
||
<Image
|
||
src={process.env.NEXT_PUBLIC_WEB_ASSETS_URL + "/images/top_post.png"}
|
||
width={76}
|
||
className="mb-2"
|
||
/>
|
||
)}
|
||
<div className="flex">
|
||
<div
|
||
className="h-max"
|
||
onClick={() => {
|
||
router.push("/profile/" + data.mid);
|
||
}}
|
||
>
|
||
<Image
|
||
className="flex-none w-8 h-8 rounded-full mr-2"
|
||
fit="cover"
|
||
width={"2rem"}
|
||
height={"2rem"}
|
||
src={data.streamer_ext?.avatar.images[0].urls[0]}
|
||
alt=""
|
||
/>
|
||
</div>
|
||
|
||
<div className="flex-1">
|
||
<div className="flex justify-between items-center ">
|
||
<span className="text-md">{data.streamer_ext?.name}</span>
|
||
{type == "post" && (
|
||
<span
|
||
onClick={() => handleFollow(isFollow, data?.mid, setIsFollow)}
|
||
className="rounded-full bg-[#FFFFFF1A] px-2 py-1 text-xs text-white font-medium"
|
||
>
|
||
{isFollow ? "已关注" : "关注"}
|
||
</span>
|
||
)}
|
||
</div>
|
||
{(data?.status === 0 || data?.status === 1) && (
|
||
<div className="flex flex-col items-start mt-2">
|
||
<div className="py-1 px-2 rounded bg-[#3B69B8]">
|
||
<span className="text-sm">审核中</span>
|
||
</div>
|
||
</div>
|
||
)}
|
||
{data?.status === 3 &&
|
||
(type == "space" ? (
|
||
<p
|
||
className="py-1 px-2 inline-block bg-[#F53030] rounded"
|
||
onClick={() => {
|
||
router.push(
|
||
`/space/editSpacePost?data=${encodeURIComponent(
|
||
JSON.stringify(data)
|
||
)}`
|
||
);
|
||
}}
|
||
>
|
||
<span className="text-sm">
|
||
审核未通过,请<span className="underline">重新编辑</span>。
|
||
</span>
|
||
</p>
|
||
) : (
|
||
<p className="py-1 px-2 inline-block bg-[#F53030] rounded">
|
||
<span className="text-sm">
|
||
审核未通过,请删除后重新提交。{type}
|
||
</span>
|
||
</p>
|
||
))}
|
||
<div>
|
||
{!data?.is_zone_moment_unlocked ? (
|
||
<span
|
||
dangerouslySetInnerHTML={{
|
||
__html: findLinksInText(data.text),
|
||
}}
|
||
className={`mb-2 mt-2 whitespace-pre-wrap ${
|
||
!isOpenText ? "text-ellipsis-7" : ""
|
||
} ${inter.className}`}
|
||
// style={{
|
||
// WebkitLineClamp: data?.text_visible_range < 999?data?.text_visible_range:7,
|
||
// }}
|
||
></span>
|
||
) : (
|
||
<div>
|
||
<span
|
||
className={`mb-2 mt-2 whitespace-pre-wrap ${
|
||
!isOpenText ? "text-ellipsis-7" : ""
|
||
} ${inter.className}`}
|
||
dangerouslySetInnerHTML={{
|
||
__html: findLinksInText(data.text),
|
||
}}
|
||
style={{
|
||
WebkitLineClamp:
|
||
data?.text_visible_range < 999
|
||
? data?.text_visible_range
|
||
: 7,
|
||
}}
|
||
></span>
|
||
</div>
|
||
)}
|
||
{!(
|
||
!data?.is_zone_moment_unlocked && data?.text_visible_range < 999
|
||
) &&
|
||
data.text?.length > 140 && (
|
||
<div
|
||
className="font-bold text-btn my-4 text-base"
|
||
onClick={() => setIsOpenText(!isOpenText)}
|
||
>
|
||
{isOpenText ? "收起" : "全文"}
|
||
</div>
|
||
)}
|
||
</div>
|
||
<div className="mr-8 rounded-md">
|
||
{data.media_component && (
|
||
<Photos
|
||
data={data}
|
||
media={data.media_component}
|
||
isUnlocked={data.is_zone_moment_unlocked}
|
||
mediaAmount={data.media_amount}
|
||
mediaVisibleRange={data.media_visible_range}
|
||
type={type}
|
||
/>
|
||
)}
|
||
{type == "space" && !!data.c_type && (
|
||
<PaySpacePost
|
||
type={data.is_ironfan_visible ? "ironFan" : "superFan"}
|
||
price={data.price / 100}
|
||
status={data.is_ironfanship_unlocked}
|
||
is_ironfan_visible={data.is_ironfan_visible == 1}
|
||
ironfanship_price={data.ironfanship_price / 100}
|
||
is_zone_moment_unlocked={data.is_zone_moment_unlocked}
|
||
data={data}
|
||
isCreator={isCreator}
|
||
/>
|
||
)}
|
||
</div>
|
||
<div className="flex justify-between items-center mt-2">
|
||
{type == "post" ? (
|
||
<div
|
||
className="flex items-center"
|
||
onClick={() =>
|
||
router.push("/space/person_space_introduce/" + data.mid)
|
||
}
|
||
>
|
||
{data.streamer_ext.is_active_within_a_week ? (
|
||
<>
|
||
<Image
|
||
src={
|
||
process.env.NEXT_PUBLIC_WEB_ASSETS_URL +
|
||
"/icons/space_new_post.png"
|
||
}
|
||
width={18}
|
||
className="w-4 h-full mr-1"
|
||
placeholder=""
|
||
/>
|
||
<span className="mr-1 text-primary text-xs">
|
||
{/* {data.streamer_ext.days_elapsed_since_the_last_zones_update < 7 &&
|
||
`空间${
|
||
data.streamer_ext.days_elapsed_since_the_last_zones_update === 0
|
||
? "今日"
|
||
: "new" === 1
|
||
? "昨日"
|
||
: "new" === 2
|
||
? "前天"
|
||
: data.streamer_ext.days_elapsed_since_the_last_zones_update +
|
||
"天前"
|
||
}有更新`}
|
||
空间 */}
|
||
空间
|
||
{formatZoneUpdateTime(
|
||
data?.streamer_ext
|
||
?.days_elapsed_since_the_last_zones_update
|
||
)}
|
||
有更新
|
||
</span>
|
||
<FontAwesomeIcon
|
||
icon={faAngleRight}
|
||
style={{ maxWidth: "12px" }}
|
||
color="#FF669E"
|
||
size="sm"
|
||
/>
|
||
</>
|
||
) : (
|
||
data?.streamer_ext?.zones?.length !== 0 && (
|
||
<div className="text-[#FFFFFFB2] font-medium text-xs flex items-center">
|
||
<span className="mr-1">查看TA的空间</span>
|
||
<FontAwesomeIcon
|
||
icon={faAngleRight}
|
||
size="sm"
|
||
style={{ maxWidth: "12px" }}
|
||
/>
|
||
</div>
|
||
)
|
||
)}
|
||
</div>
|
||
) : (
|
||
<div className="text-[#ffffff88] text-xs">
|
||
<span className="mr-2">
|
||
{getDays < 3
|
||
? `${
|
||
getDays === 0 ? "今日" : "new" === 1 ? "昨日" : "前天"
|
||
}`
|
||
: date.getMonth() + 1 + "-" + date.getDate()}
|
||
</span>
|
||
<span>
|
||
{(date.getHours() > 9
|
||
? date.getHours()
|
||
: "0" + date.getHours()) +
|
||
":" +
|
||
(date.getMinutes() > 9
|
||
? date.getMinutes()
|
||
: "0" + date.getMinutes())}
|
||
</span>
|
||
</div>
|
||
)}
|
||
<div className="flex items-center">
|
||
<div
|
||
className="flex items-center mr-4 h-[32px]"
|
||
onClick={() =>
|
||
thumbsUp(data.id, isThumbsUp, setIsThumbsUp, type == "space")
|
||
}
|
||
>
|
||
<Image
|
||
src={
|
||
process.env.NEXT_PUBLIC_WEB_ASSETS_URL +
|
||
(isThumbsUp == 1
|
||
? "/icons/thumbup.png"
|
||
: "/icons/notthumbup.png")
|
||
}
|
||
width={32}
|
||
className="w-4 h-full"
|
||
placeholder=""
|
||
/>
|
||
<span
|
||
className={`text-xs ${
|
||
isThumbsUp == 1 ? "text-[#FF669E]" : "text-[#FFFFFF80]"
|
||
}`}
|
||
>
|
||
{isThumbsUp == 1 ? "已赞" : "点赞"}
|
||
</span>
|
||
</div>
|
||
<Popover
|
||
visible={popoverVisible}
|
||
// stopPropagation={["click", "touchstart"]}
|
||
destroyOnHide={true}
|
||
style={{ "--background": "#1E1C29" }}
|
||
content={
|
||
<ul>
|
||
{type == "space" && account?.mid == data?.mid ? (
|
||
<>
|
||
<li className="py-1 px-4" onClick={handleTop}>
|
||
{data.is_headed ? "取消置顶" : "置顶"}
|
||
</li>
|
||
<hr className="border-[#ffffff2b] my-1" />
|
||
<li
|
||
className="py-1 px-4"
|
||
onClick={() => {
|
||
router.push(
|
||
`/space/editSpacePost?data=${encodeURIComponent(
|
||
JSON.stringify(data)
|
||
)}`
|
||
);
|
||
}}
|
||
>
|
||
编辑
|
||
</li>
|
||
<hr className="border-[#ffffff2b] my-1" />
|
||
<li className="py-1 px-4" onClick={handleDelete}>
|
||
删除
|
||
</li>
|
||
</>
|
||
) : type == "post" && account?.mid == data?.mid ? (
|
||
<>
|
||
<li className="py-1 px-4" onClick={handleDelete}>
|
||
删除
|
||
</li>
|
||
</>
|
||
) : (
|
||
<>
|
||
<li
|
||
className="py-1 px-4"
|
||
onClick={() => {
|
||
router.push("/messageDetail");
|
||
}}
|
||
>
|
||
举报
|
||
</li>
|
||
<hr className="border-[#ffffff2b] my-1" />
|
||
<li
|
||
className="py-1 px-4"
|
||
onClick={async () => {
|
||
const subMid = account.mid;
|
||
const objMid = data.mid;
|
||
await handleBlock(subMid, objMid);
|
||
setPopoverVisible(false);
|
||
}}
|
||
>
|
||
拉黑
|
||
</li>
|
||
</>
|
||
)}
|
||
</ul>
|
||
}
|
||
trigger="click"
|
||
placement="left"
|
||
>
|
||
<span
|
||
className="mr-2"
|
||
onClick={() => setPopoverVisible(!popoverVisible)}
|
||
>
|
||
···
|
||
</span>
|
||
</Popover>
|
||
</div>
|
||
</div>
|
||
{/* <div className="rounded-full h-px bg-gray-200 mt-2"></div> */}
|
||
</div>
|
||
</div>
|
||
</div>
|
||
);
|
||
}
|