tiefen_space_app/components/Post/index.jsx

413 lines
13 KiB
React
Raw Normal View History

2023-12-29 00:27:44 +08:00
import {
View,
Text,
TouchableOpacity,
Modal,
TouchableWithoutFeedback,
ActivityIndicator,
Image as NativeImage,
2023-12-29 00:27:44 +08:00
} from "react-native";
import React, { useState } from "react";
import { useTailwind } from "tailwind-rn";
import { Image } from "expo-image";
import ImageViewer from "react-native-image-zoom-viewer";
import VideoModal from "../VideoModal";
import { useNavigation } from "@react-navigation/native";
import formatTimestamp from "../../utils/formatTimestamp";
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";
2023-12-29 00:27:44 +08:00
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
export default function Post({ blur, data }) {
2023-12-29 00:27:44 +08:00
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
);
2023-12-29 00:27:44 +08:00
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 [operationOpen, setOperationOpen] = useState(false);
// const Operation = () => {
// const handleFeedback = () => {
// navigation.navigate("MessageDetail", { mid: 1 });
// setOperationOpen(false);
// };
// const handleBlock = async () => {
// const account = await get("account");
// const subMid = account.mid;
// const objMid = data.mid;
// await block(subMid, objMid);
// setOperationOpen(false);
// };
// return (
// <View style={tailwind("absolute right-4 top-2")}>
// <Icon
// type="ionicon"
// name="ellipsis-vertical"
// size={32}
// color="white"
// onPress={() => setOperationOpen(true)}
// />
// {operationOpen && (
// <TouchableWithoutFeedback onPress={() => setOperationOpen(false)}>
// <View
// style={{
// ...tailwind("items-center bg-white rounded-lg p-2"),
// }}
// >
// <TouchableOpacity
// onPress={handleBlock}
// style={tailwind("flex-row items-center px-4")}
// >
// <Icon
// type="ionicon"
// name="remove-circle"
// color="#f87171"
// size={24}
// />
// <Text style={tailwind("text-base")}>拉黑</Text>
// </TouchableOpacity>
// <Divider style={tailwind("w-full my-2")} />
// <TouchableOpacity
// onPress={handleFeedback}
// style={tailwind("flex-row items-center px-4")}
// >
// <Icon type="ionicon" name="warning" color="#60a5fa" size={24} />
// <Text style={tailwind("text-base")}>举报</Text>
// </TouchableOpacity>
// </View>
// </TouchableWithoutFeedback>
// )}
// </View>
// );
// };
2023-12-29 00:27:44 +08:00
return (
<View style={tailwind("flex flex-col")}>
<View style={tailwind("flex pt-3.5 pb-1.5 pl-3.5")}>
2023-12-29 00:27:44 +08:00
<View style={tailwind("flex flex-row")}>
<TouchableWithoutFeedback
onPress={() =>
navigation.navigate("StreamerProfile", { mid: data?.mid })
}
2023-12-29 00:27:44 +08:00
>
<Image
style={tailwind("w-9 h-9 rounded-full")}
source={data.streamer_ext?.avatar?.images[0]?.urls[0]}
placeholder={blurhash}
contentFit="cover"
transition={1000}
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>
2023-12-29 00:27:44 +08:00
</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 ? (
<ImageDisplay blur={blur} media={data.media_component.images} />
) : (
<TouchableOpacity
activeOpacity={1}
onPress={
blur
? () =>
navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
})
: () => {
setShowVideo(true);
}
}
>
<View>
<PosterDisplay
blur={blur}
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")}
>
<Text style={tailwind("text-sm font-medium text-[#FFFFFF80]")}>
{formatTimestamp(data?.ct)}
</Text>
{blur ? (
<TouchableOpacity
activeOpacity={1}
onPress={() =>
navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
})
}
style={tailwind(
"flex flex-row px-1.5 justify-between items-center rounded-lg bg-[#301024]"
)}
>
<NativeImage
source={require("../../assets/icon/others/vipsmall.png")}
/>
<Text
style={tailwind("mx-1 text-sm font-medium text-[#FF669E]")}
>
开通会员永久看动态
</Text>
<NativeImage
source={require("../../assets/icon/others/pinklink.png")}
/>
</TouchableOpacity>
) : (
<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-sm"),
}}
>
{like
? data.thumbs_up_num + 1 - data.is_thumbed_up
: data.thumbs_up_num - data.is_thumbed_up === 0
? "点赞"
: data.thumbs_up_num - data.is_thumbed_up}
</Text>
</TouchableOpacity>
)}
</View>
</View>
2023-12-29 00:27:44 +08:00
</View>
</View>
<View style={tailwind("h-[3px] rounded-full mx-4 bg-[#FFFFFF26]")}></View>
2023-12-29 00:27:44 +08:00
</View>
);
}
//媒体为图片时展示内容的组件
function ImageDisplay({ blur, media }) {
const navigation = useNavigation();
2023-12-29 00:27:44 +08:00
const tailwind = useTailwind();
const [isModalVisible, setIsModalVisible] = useState(false);
const [imageIndex, setImageIndex] = useState();
const images = media.map((item) => ({ url: item.urls[0] }));
return (
<View style={tailwind("flex flex-row flex-wrap")}>
{media.length > 1 ? (
media.map((item, index) => (
<TouchableOpacity
activeOpacity={1}
key={index}
onPress={
blur
? () =>
navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
})
: () => {
setIsModalVisible(true);
setImageIndex(index);
}
}
style={{ aspectRatio: 1, ...tailwind("basis-1/3 p-0.5") }}
>
<Image
style={tailwind("w-full h-full rounded")}
blurRadius={blur ? 75 : 0}
source={item.urls[0]}
placeholder={blurhash}
contentFit="cover"
transition={1000}
cachePolicy="disk"
/>
</TouchableOpacity>
))
) : (
<TouchableOpacity
activeOpacity={1}
onPress={
blur
? () =>
navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
})
: () => {
2023-12-29 00:27:44 +08:00
setIsModalVisible(true);
setImageIndex(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,
}}
>
2023-12-29 00:27:44 +08:00
<Image
style={tailwind("w-full h-full rounded")}
blurRadius={blur ? 75 : 0}
source={media[0].urls[0]}
2023-12-29 00:27:44 +08:00
placeholder={blurhash}
contentFit="contain"
contentPosition="left top"
2023-12-29 00:27:44 +08:00
transition={1000}
cachePolicy="disk"
/>
</TouchableOpacity>
)}
<Modal visible={isModalVisible} statusBarTranslucent transparent={true}>
<TouchableWithoutFeedback onPress={() => setIsModalVisible(false)}>
<ImageViewer
imageUrls={images}
enableSwipeDown
onSwipeDown={() => setIsModalVisible(false)}
index={imageIndex}
loadingRender={() => <ActivityIndicator size="large" />}
/>
</TouchableWithoutFeedback>
</Modal>
</View>
);
2023-12-29 00:27:44 +08:00
}
//媒体为视频时展示封面的组件
function PosterDisplay({ blur, media }) {
2023-12-29 00:27:44 +08:00
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")}
blurRadius={blur ? 75 : 0}
source={media.cover_urls[0]}
placeholder={blurhash}
contentFit="cover"
transition={1000}
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")} />
2023-12-29 00:27:44 +08:00
</View>
</View>
);
2023-12-29 00:27:44 +08:00
}