524 lines
16 KiB
JavaScript
524 lines
16 KiB
JavaScript
import {
|
|
View,
|
|
Text,
|
|
TouchableOpacity,
|
|
Modal,
|
|
TouchableWithoutFeedback,
|
|
Platform,
|
|
Image as NativeImage,
|
|
} from "react-native";
|
|
import React, { useEffect, useState, useCallback } from "react";
|
|
import { useTailwind } from "tailwind-rn";
|
|
import { Image } from "expo-image";
|
|
import VideoModal from "../VideoModal";
|
|
import { useNavigation } from "@react-navigation/native";
|
|
import { follow, unfollow, block } from "../../utils/relation";
|
|
import baseRequest from "../../utils/baseRequest";
|
|
import { generateSignature } from "../../utils/crypto";
|
|
import Toast from "react-native-toast-message";
|
|
import { get } from "../../utils/storeInfo";
|
|
import { useImageViewer } from "../../context/ImageViewProvider";
|
|
|
|
export default function Post({ data }) {
|
|
const tailwind = useTailwind();
|
|
const navigation = useNavigation();
|
|
const [like, setLike] = useState(data?.is_thumbed_up === 1 ? true : false);
|
|
const [isFollowed, setIsFollowed] = useState(
|
|
data?.is_followed === 1 ? true : false
|
|
);
|
|
const [showVideo, setShowVideo] = useState(false);
|
|
|
|
//点赞和取消点赞功能
|
|
const thumbsUp = async (id, times = 1) => {
|
|
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
|
|
try {
|
|
const base = await baseRequest();
|
|
const signature = await generateSignature({
|
|
moment_id: id,
|
|
times: times,
|
|
...base,
|
|
});
|
|
const _response = await fetch(
|
|
`${apiUrl}/api/moment/thumbs_up?signature=${signature}`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
moment_id: id,
|
|
times: times,
|
|
...base,
|
|
}),
|
|
}
|
|
);
|
|
const _data = await _response.json();
|
|
if (_data.ret === -1) {
|
|
Toast.show({
|
|
type: "error",
|
|
text1: _data.msg,
|
|
topOffset: 60,
|
|
});
|
|
return;
|
|
}
|
|
} catch (error) {
|
|
console.error(error);
|
|
}
|
|
};
|
|
const handleLike = () => {
|
|
setLike(!like);
|
|
if (like) {
|
|
thumbsUp(data.id, -1);
|
|
return;
|
|
}
|
|
thumbsUp(data.id, 1);
|
|
};
|
|
|
|
//关注和取关功能
|
|
const handleFollow = async () => {
|
|
setIsFollowed(!isFollowed);
|
|
const account = await get("account");
|
|
if (isFollowed) {
|
|
unfollow(account.mid, data.mid);
|
|
return;
|
|
}
|
|
follow(account.mid, data.mid);
|
|
};
|
|
|
|
//帖子操作功能
|
|
const [operationModalVisible, setOperationModalVisible] = useState(false);
|
|
|
|
const formatZoneUpdateTime = useCallback((time) => {
|
|
if (time === 0) return "今日";
|
|
if (time === 1) return "昨日";
|
|
return `${time}日前`;
|
|
}, []);
|
|
|
|
return (
|
|
<View style={tailwind("flex flex-col")}>
|
|
<View style={tailwind("flex pt-3.5 pb-1.5 pl-3.5")}>
|
|
<View style={tailwind("flex flex-row")}>
|
|
<TouchableWithoutFeedback
|
|
onPress={() =>
|
|
navigation.navigate("StreamerProfile", { mid: data?.mid })
|
|
}
|
|
>
|
|
<Image
|
|
style={tailwind("w-9 h-9 rounded-full")}
|
|
source={data?.streamer_ext?.avatar?.images[0]?.urls[0]}
|
|
contentFit="cover"
|
|
transition={100}
|
|
cachePolicy="disk"
|
|
/>
|
|
</TouchableWithoutFeedback>
|
|
<View
|
|
style={tailwind("flex flex-col flex-1 justify-around ml-2 pr-4")}
|
|
>
|
|
<View
|
|
style={tailwind("flex flex-row items-center justify-between")}
|
|
>
|
|
<Text style={tailwind("text-base font-medium text-white")}>
|
|
{data?.streamer_ext?.name}
|
|
</Text>
|
|
<TouchableOpacity
|
|
onPress={handleFollow}
|
|
style={{
|
|
backgroundColor: isFollowed ? "#07050A" : "#FFFFFF1A",
|
|
borderColor: "#FFFFFF1A",
|
|
borderWidth: isFollowed ? 1 : 0,
|
|
...tailwind(
|
|
"ml-1 justify-center items-center rounded-full px-3 h-6"
|
|
),
|
|
}}
|
|
>
|
|
<Text style={tailwind("text-white text-xs")}>
|
|
{isFollowed ? "已关注" : "关注"}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</View>
|
|
<Text style={tailwind("text-base text-white font-medium my-2")}>
|
|
{data?.text}
|
|
</Text>
|
|
{/* 媒体展示 */}
|
|
<View style={tailwind("pr-10 mb-1")}>
|
|
{data.media_component.video_ids?.length === 0 ||
|
|
data.media_component.video_ids === null ? (
|
|
<ImageDisplay media={data.media_component?.images} />
|
|
) : (
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
onPress={() => {
|
|
setShowVideo(true);
|
|
}}
|
|
>
|
|
<View>
|
|
<PosterDisplay media={data.media_component?.videos[0]} />
|
|
<VideoModal
|
|
visible={showVideo}
|
|
setVisible={setShowVideo}
|
|
url={data.media_component.videos[0].urls[0]}
|
|
/>
|
|
</View>
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
<View
|
|
style={tailwind("flex flex-row items-center justify-between h-8")}
|
|
>
|
|
{data?.streamer_ext?.is_active_within_a_week === 1 ? (
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
navigation.navigate("SpaceIntroduce", { mid: data?.mid })
|
|
}
|
|
style={tailwind("flex flex-row items-center")}
|
|
>
|
|
<NativeImage
|
|
source={require("../../assets/icon/others/space_new_post.png")}
|
|
/>
|
|
<Text style={tailwind("text-[#FF669E] font-medium text-xs")}>
|
|
空间
|
|
{formatZoneUpdateTime(
|
|
data?.streamer_ext
|
|
?.days_elapsed_since_the_last_zones_update
|
|
)}
|
|
有更新
|
|
</Text>
|
|
<NativeImage
|
|
source={require("../../assets/icon/others/pinklink.png")}
|
|
/>
|
|
</TouchableOpacity>
|
|
) : (
|
|
data?.streamer_ext?.zones?.length !== 0 && (
|
|
<TouchableOpacity
|
|
onPress={() =>
|
|
navigation.navigate("SpaceIntroduce", { mid: data?.mid })
|
|
}
|
|
style={tailwind("flex flex-row items-center")}
|
|
>
|
|
<Text
|
|
style={tailwind("text-[#FFFFFFB2] font-medium text-xs")}
|
|
>
|
|
查看TA的空间
|
|
</Text>
|
|
<NativeImage
|
|
style={{ right: 10, transform: [{ scale: 0.75 }] }}
|
|
source={require("../../assets/icon/32DP/smalllink.png")}
|
|
/>
|
|
</TouchableOpacity>
|
|
)
|
|
)}
|
|
<View style={tailwind("flex flex-row items-center ml-auto")}>
|
|
<TouchableOpacity
|
|
style={tailwind("flex flex-row items-center")}
|
|
onPress={handleLike}
|
|
>
|
|
{like ? (
|
|
<NativeImage
|
|
source={require("../../assets/icon/others/thumbup.png")}
|
|
/>
|
|
) : (
|
|
<NativeImage
|
|
source={require("../../assets/icon/others/notthumbup.png")}
|
|
/>
|
|
)}
|
|
<Text
|
|
style={{
|
|
color: like ? "#FF669E" : "#FFFFFF80",
|
|
...tailwind("text-xs"),
|
|
}}
|
|
>
|
|
{like ? "已赞" : "点赞"}
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<TouchableOpacity
|
|
style={tailwind("ml-2")}
|
|
onPress={() =>
|
|
setOperationModalVisible(!operationModalVisible)
|
|
}
|
|
>
|
|
<NativeImage
|
|
source={require("../../assets/icon/others/gray....png")}
|
|
/>
|
|
<OperationModal
|
|
visible={operationModalVisible}
|
|
setVisible={setOperationModalVisible}
|
|
data={data}
|
|
/>
|
|
</TouchableOpacity>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
</View>
|
|
<View style={tailwind("h-[3px] rounded-full mx-4 bg-[#FFFFFF26]")}></View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
//媒体为图片时展示内容的组件
|
|
function ImageDisplay({ media }) {
|
|
const navigation = useNavigation();
|
|
const tailwind = useTailwind();
|
|
const images = media.map((item) => ({ url: item.urls[0] }));
|
|
|
|
const { showImageViewer } = useImageViewer();
|
|
|
|
if (images.length === 0) return null;
|
|
|
|
return (
|
|
<View style={tailwind("flex flex-row flex-wrap")}>
|
|
{media.length > 1 ? (
|
|
media.map((item, index) => (
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
key={index}
|
|
onPress={() => {
|
|
showImageViewer({
|
|
imageUrls: images,
|
|
index: index,
|
|
});
|
|
}}
|
|
style={{ aspectRatio: 1, ...tailwind("basis-1/3 p-0.5") }}
|
|
>
|
|
<Image
|
|
style={tailwind("w-full h-full rounded")}
|
|
source={item.urls[0]}
|
|
contentFit="cover"
|
|
transition={100}
|
|
cachePolicy="disk"
|
|
/>
|
|
</TouchableOpacity>
|
|
))
|
|
) : (
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
onPress={() => {
|
|
showImageViewer({
|
|
imageUrls: images,
|
|
index: 0,
|
|
});
|
|
}}
|
|
style={{
|
|
width:
|
|
media[0].w < media[0].h ? (media[0].w / media[0].h) * 200 : 250,
|
|
height:
|
|
media[0].w < media[0].h ? 200 : (media[0].h / media[0].w) * 250,
|
|
}}
|
|
>
|
|
<Image
|
|
style={tailwind("w-full h-full rounded")}
|
|
source={media[0].urls[0]}
|
|
contentFit="contain"
|
|
contentPosition="left top"
|
|
transition={100}
|
|
cachePolicy="disk"
|
|
/>
|
|
</TouchableOpacity>
|
|
)}
|
|
</View>
|
|
);
|
|
}
|
|
|
|
//媒体为视频时展示封面的组件
|
|
function PosterDisplay({ media }) {
|
|
const tailwind = useTailwind();
|
|
|
|
return (
|
|
<View
|
|
style={{
|
|
width:
|
|
media.cover_w < media.cover_h
|
|
? (media.cover_w / media.cover_h) * 200
|
|
: 250,
|
|
height:
|
|
media.cover_w < media.cover_h
|
|
? 200
|
|
: (media.cover_h / media.cover_w) * 250,
|
|
}}
|
|
>
|
|
<Image
|
|
style={tailwind("w-full h-full rounded")}
|
|
source={media.cover_urls[0]}
|
|
contentFit="cover"
|
|
transition={100}
|
|
cachePolicy="disk"
|
|
/>
|
|
<View
|
|
style={tailwind(
|
|
"flex absolute w-full h-full left-0 top-0 z-10 justify-center items-center"
|
|
)}
|
|
>
|
|
<NativeImage source={require("../../assets/icon/others/play.png")} />
|
|
</View>
|
|
</View>
|
|
);
|
|
}
|
|
|
|
//帖子操作组件
|
|
function OperationModal({ visible, setVisible, data }) {
|
|
const tailwind = useTailwind();
|
|
const navigation = useNavigation();
|
|
|
|
//拉黑
|
|
const handleBlock = async () => {
|
|
const account = await get("account");
|
|
const subMid = account.mid;
|
|
const objMid = data.mid;
|
|
await block(subMid, objMid);
|
|
setVisible(false);
|
|
};
|
|
|
|
//删除
|
|
const handleDelete = async () => {
|
|
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
|
|
try {
|
|
const base = await baseRequest();
|
|
const signature = await generateSignature({
|
|
id: data.id,
|
|
...base,
|
|
});
|
|
const _response = await fetch(
|
|
`${apiUrl}/api/moment/delete?signature=${signature}`,
|
|
{
|
|
method: "POST",
|
|
headers: {
|
|
"Content-Type": "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
id: data.id,
|
|
...base,
|
|
}),
|
|
}
|
|
);
|
|
const _data = await _response.json();
|
|
if (_data.ret === -1) {
|
|
Toast.show({
|
|
type: "error",
|
|
text1: _data.msg,
|
|
topOffset: 60,
|
|
});
|
|
return;
|
|
}
|
|
Toast.show({
|
|
type: "success",
|
|
text1: "删除成功,请刷新页面",
|
|
topOffset: 60,
|
|
});
|
|
return;
|
|
} catch (error) {
|
|
console.error(error);
|
|
} finally {
|
|
setVisible(false);
|
|
}
|
|
};
|
|
|
|
//判断是否展示删除、拉黑、举报功能
|
|
const [display, setDisPlay] = useState({
|
|
delete: false,
|
|
block: true,
|
|
feedback: true,
|
|
});
|
|
useEffect(() => {
|
|
const checkAuth = async () => {
|
|
const account = await get("account");
|
|
if (account.mid === data.mid || account.role === 1 || account.role === 2)
|
|
setDisPlay((prev) => {
|
|
return { ...prev, delete: true };
|
|
});
|
|
if (account.mid === data.mid)
|
|
setDisPlay((prev) => {
|
|
return { ...prev, block: false, feedback: false };
|
|
});
|
|
};
|
|
checkAuth();
|
|
}, []);
|
|
|
|
return (
|
|
<Modal
|
|
visible={visible}
|
|
statusBarTranslucent
|
|
transparent={true}
|
|
style={tailwind("flex-1")}
|
|
>
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
onPress={() => setVisible(false)}
|
|
style={{
|
|
backgroundColor: "#00000080",
|
|
...tailwind("flex flex-1 items-center justify-center"),
|
|
}}
|
|
>
|
|
<TouchableOpacity
|
|
activeOpacity={1}
|
|
style={tailwind(
|
|
"flex flex-col p-2 rounded-2xl bg-[#1E1C29] items-center justify-center w-3/4"
|
|
)}
|
|
>
|
|
{display.feedback && (
|
|
<View style={tailwind("flex flex-col w-full items-center")}>
|
|
<TouchableOpacity
|
|
onPress={() => {
|
|
navigation.navigate("MessageDetail", { mid: 1 });
|
|
setVisible(false);
|
|
}}
|
|
style={tailwind("flex flex-col w-full py-2 items-center")}
|
|
>
|
|
<Text style={tailwind("text-base text-white font-medium")}>
|
|
举报
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<View
|
|
style={tailwind(
|
|
"h-[1px] w-full rounded-full mx-4 bg-[#FFFFFF26]"
|
|
)}
|
|
></View>
|
|
</View>
|
|
)}
|
|
{display.block && (
|
|
<View style={tailwind("flex flex-col w-full items-center")}>
|
|
<TouchableOpacity
|
|
onPress={handleBlock}
|
|
style={tailwind("flex flex-col w-full py-2 items-center")}
|
|
>
|
|
<Text style={tailwind("text-base text-white font-medium")}>
|
|
拉黑
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<View
|
|
style={tailwind(
|
|
"h-[1px] w-full rounded-full mx-4 bg-[#FFFFFF26]"
|
|
)}
|
|
></View>
|
|
</View>
|
|
)}
|
|
{display.delete && (
|
|
<View style={tailwind("flex flex-col w-full items-center")}>
|
|
<TouchableOpacity
|
|
onPress={handleDelete}
|
|
style={tailwind("flex flex-col w-full py-2 items-center")}
|
|
>
|
|
<Text style={tailwind("text-base text-white font-medium")}>
|
|
删除
|
|
</Text>
|
|
</TouchableOpacity>
|
|
<View
|
|
style={tailwind(
|
|
"h-[1px] w-full rounded-full mx-4 bg-[#FFFFFF26]"
|
|
)}
|
|
></View>
|
|
</View>
|
|
)}
|
|
<TouchableOpacity
|
|
onPress={() => setVisible(false)}
|
|
style={tailwind("flex flex-col w-full py-2 items-center")}
|
|
>
|
|
<Text style={tailwind("text-base text-white font-medium")}>
|
|
关闭
|
|
</Text>
|
|
</TouchableOpacity>
|
|
</TouchableOpacity>
|
|
</TouchableOpacity>
|
|
</Modal>
|
|
);
|
|
}
|