tiefen_space_app/components/Post/index.jsx

543 lines
17 KiB
JavaScript

import {
View,
Text,
TouchableOpacity,
Modal,
TouchableWithoutFeedback,
Platform,
Image as NativeImage,
} from "react-native";
import React, { useEffect, useState } from "react";
import { useTailwind } from "tailwind-rn";
import { Image } from "expo-image";
import ImageViewer from "../ImageViewer";
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";
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
export default function Post({ isBlur, data }) {
const blur = data?.top ? false : isBlur;
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);
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]}
placeholder={blurhash}
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
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-end h-8")}
>
{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>
) : (
<View style={tailwind("flex flex-row items-center")}>
<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 ? "已赞" : "点赞"}
</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({ blur, media }) {
const navigation = useNavigation();
const tailwind = useTailwind();
const [isModalVisible, setIsModalVisible] = useState(false);
const [imageIndex, setImageIndex] = useState();
const images = media.map((item) => ({ url: item.urls[0] }));
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={
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 ? (Platform.OS === "ios" ? 100 : 10) : 0}
source={item.urls[0]}
placeholder={blurhash}
contentFit="cover"
transition={100}
cachePolicy="disk"
/>
</TouchableOpacity>
))
) : (
<TouchableOpacity
activeOpacity={1}
onPress={
blur
? () =>
navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
})
: () => {
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,
}}
>
<Image
style={tailwind("w-full h-full rounded")}
blurRadius={blur ? (Platform.OS === "ios" ? 130 : 10) : 0}
source={media[0].urls[0]}
placeholder={blurhash}
contentFit="contain"
contentPosition="left top"
transition={100}
cachePolicy="disk"
/>
</TouchableOpacity>
)}
<ImageViewer
isVisible={isModalVisible}
setIsVisible={setIsModalVisible}
imageUrls={images}
index={imageIndex}
/>
</View>
);
}
//媒体为视频时展示封面的组件
function PosterDisplay({ blur, 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")}
blurRadius={blur ? (Platform.OS === "ios" ? 130 : 10) : 0}
source={media.cover_urls[0]}
placeholder={blurhash}
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>
);
}