671 lines
22 KiB
JavaScript
671 lines
22 KiB
JavaScript
import {
|
||
View,
|
||
Dimensions,
|
||
TouchableOpacity,
|
||
TouchableWithoutFeedback,
|
||
Modal,
|
||
ActivityIndicator,
|
||
Text,
|
||
Alert,
|
||
ScrollView,
|
||
Image as NativeImage,
|
||
} from "react-native";
|
||
import React, { useState, useEffect } from "react";
|
||
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
||
import { useTailwind } from "tailwind-rn";
|
||
import { Image } from "expo-image";
|
||
import ImageViewer from "react-native-image-zoom-viewer";
|
||
import Swiper from "react-native-swiper";
|
||
import { Divider, Icon } from "@rneui/themed";
|
||
import VideoModal from "../../components/VideoModal";
|
||
import { useHeaderHeight } from "@react-navigation/elements";
|
||
import * as Clipboard from "expo-clipboard";
|
||
import * as Linking from "expo-linking";
|
||
import GetWechatModal from "../../components/GetWechatModal";
|
||
import SubmitWechatModal from "../../components/SubmitWechatModal";
|
||
import Toast from "react-native-toast-message";
|
||
import baseRequest from "../../utils/baseRequest";
|
||
import { follow, unfollow, checkRelation, block } from "../../utils/relation";
|
||
import { get } from "../../utils/storeInfo";
|
||
import StreamerProfileSkeleton from "./skeleton";
|
||
import { generateSignature } from "../../utils/crypto";
|
||
import Svg, { Path } from "react-native-svg";
|
||
|
||
export default function StreamerProfile({ navigation, route }) {
|
||
const screenWidth = Dimensions.get("window").width;
|
||
const tailwind = useTailwind();
|
||
const insets = useSafeAreaInsets();
|
||
const headerHeight = useHeaderHeight();
|
||
|
||
//设置header右侧按钮功能
|
||
useEffect(() => {
|
||
navigation.setOptions({
|
||
headerRight: () => (
|
||
<Icon
|
||
type="ionicon"
|
||
name="ellipsis-vertical"
|
||
size={24}
|
||
color="white"
|
||
onPress={() => setNavModal(true)}
|
||
/>
|
||
),
|
||
});
|
||
}, []);
|
||
|
||
//页面数据
|
||
const [data, setData] = useState({});
|
||
const [isLoading, setIsloading] = useState(true);
|
||
useEffect(() => {
|
||
const getData = async () => {
|
||
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
|
||
try {
|
||
const base = await baseRequest();
|
||
const signature = await generateSignature({
|
||
mid: route.params.mid,
|
||
...base,
|
||
});
|
||
//获取主播数据
|
||
const detailResponse = await fetch(
|
||
`${apiUrl}/api/streamer/list_ext_by_mid?signature=${signature}`,
|
||
{
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
mid: route.params.mid,
|
||
...base,
|
||
}),
|
||
}
|
||
);
|
||
const detailData = await detailResponse.json();
|
||
if (detailData.ret === -1) {
|
||
Toast.show({
|
||
type: "error",
|
||
text1: detailData.msg,
|
||
topOffset: 60,
|
||
});
|
||
return;
|
||
}
|
||
//获取当前所有平台
|
||
const signature2 = await generateSignature({
|
||
...base,
|
||
});
|
||
const allPlatformsResponse = await fetch(
|
||
`${apiUrl}/api/platform/list?signature=${signature2}`,
|
||
{
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
...base,
|
||
}),
|
||
}
|
||
);
|
||
const allPlatformsData = await allPlatformsResponse.json();
|
||
if (allPlatformsData.ret === -1) {
|
||
Toast.show({
|
||
type: "error",
|
||
text1: allPlatformsData.msg,
|
||
topOffset: 60,
|
||
});
|
||
return;
|
||
}
|
||
//获取主播当前所有平台
|
||
const signature3 = await generateSignature({
|
||
mid: route.params.mid,
|
||
...base,
|
||
});
|
||
const streamerPlatformResponse = await fetch(
|
||
`${apiUrl}/api/streamer_link/list_by_mid?signature=${signature3}`,
|
||
{
|
||
method: "POST",
|
||
headers: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
body: JSON.stringify({
|
||
mid: route.params.mid,
|
||
...base,
|
||
}),
|
||
}
|
||
);
|
||
const streamerPlatformData = await streamerPlatformResponse.json();
|
||
if (streamerPlatformData.ret === -1) {
|
||
Toast.show({
|
||
type: "error",
|
||
text1: streamerPlatformData.msg,
|
||
topOffset: 60,
|
||
});
|
||
return;
|
||
}
|
||
const platformsWithIcon = streamerPlatformData.data.list.map((item) => {
|
||
return {
|
||
...item,
|
||
link_icon: allPlatformsData.data[item.link_no]?.icon,
|
||
};
|
||
});
|
||
setData({
|
||
...detailData.data.streamer_ext,
|
||
wechat_lock_type: detailData.data.wechat_lock_type,
|
||
platforms: platformsWithIcon,
|
||
});
|
||
setIsloading(false);
|
||
} catch (error) {
|
||
console.error(error);
|
||
}
|
||
};
|
||
getData();
|
||
}, []);
|
||
|
||
//媒体展示组件
|
||
const MySwiper = () => {
|
||
//控制视频modal可见性
|
||
const [videoVisible, setVideoVisible] = useState(false);
|
||
//控制图片modal可见性
|
||
const [imagesVisible, setImagesVisible] = useState(false);
|
||
//控制图片modal中打开的图片索引
|
||
const [imageIndex, setImageIndex] = useState("");
|
||
const images = data?.album?.images?.map((image) => image?.urls[0]);
|
||
const imagesForImageViewer = images?.map((url) => ({ url }));
|
||
return (
|
||
<>
|
||
<Swiper
|
||
autoplay
|
||
width={screenWidth}
|
||
height={screenWidth}
|
||
activeDotStyle={tailwind("bg-[#FF669E]")}
|
||
>
|
||
{data?.album !== undefined &&
|
||
data?.cover !== undefined &&
|
||
[...data?.cover?.images, ...data?.album?.images].map(
|
||
(item, index) => (
|
||
<View key={index}>
|
||
{index === 0 ? (
|
||
<TouchableOpacity
|
||
activeOpacity={1}
|
||
style={tailwind(
|
||
"w-full h-full flex items-center justify-center"
|
||
)}
|
||
onPress={() => setVideoVisible(true)}
|
||
>
|
||
<Image
|
||
source={item?.urls[0]}
|
||
contentFit="cover"
|
||
transition={1000}
|
||
cachePolicy="disk"
|
||
style={tailwind("w-full h-full")}
|
||
/>
|
||
<View
|
||
style={tailwind(
|
||
"absolute flex items-center justify-center"
|
||
)}
|
||
>
|
||
<Svg viewBox="0 0 1024 1024" width="50" height="50">
|
||
<Path
|
||
d="M512 512m-512 0a44 44 0 1 0 1024 0 44 44 0 1 0-1024 0Z"
|
||
fill="#000000"
|
||
opacity="0.5"
|
||
></Path>
|
||
<Path
|
||
d="M746.327273 534.035416 418.181818 723.708144C401.890909 733.017235 380.945455 721.380871 380.945455 701.599053L380.945455 322.253598C380.945455 303.635416 401.890909 290.835416 418.181818 300.144507L746.327273 489.817235C763.781818 500.289962 763.781818 523.562689 746.327273 534.035416L746.327273 534.035416Z"
|
||
fill="#FFFFFF"
|
||
></Path>
|
||
</Svg>
|
||
</View>
|
||
</TouchableOpacity>
|
||
) : (
|
||
<TouchableWithoutFeedback
|
||
onPress={() => {
|
||
setImageIndex(index - 1);
|
||
setImagesVisible(true);
|
||
}}
|
||
>
|
||
<Image
|
||
source={item?.urls[0]}
|
||
contentFit="cover"
|
||
transition={1000}
|
||
cachePolicy="disk"
|
||
style={tailwind("w-full h-full")}
|
||
/>
|
||
</TouchableWithoutFeedback>
|
||
)}
|
||
</View>
|
||
)
|
||
)}
|
||
</Swiper>
|
||
{/* 展示视频的modal */}
|
||
<VideoModal
|
||
visible={videoVisible}
|
||
setVisible={setVideoVisible}
|
||
url={data?.shorts?.videos[0]?.urls[0]}
|
||
/>
|
||
{/* 展示图片的modal */}
|
||
<Modal visible={imagesVisible} statusBarTranslucent transparent={true}>
|
||
<TouchableWithoutFeedback onPress={() => setImagesVisible(false)}>
|
||
<ImageViewer
|
||
imageUrls={imagesForImageViewer}
|
||
enableSwipeDown
|
||
saveToLocalByLongPress={false}
|
||
onSwipeDown={() => setImagesVisible(false)}
|
||
index={imageIndex}
|
||
loadingRender={() => <ActivityIndicator size="large" />}
|
||
/>
|
||
</TouchableWithoutFeedback>
|
||
</Modal>
|
||
</>
|
||
);
|
||
};
|
||
|
||
//拉黑、举报组件
|
||
const [navModal, setNavModal] = useState(false);
|
||
const NavbarRightModal = () => {
|
||
const handleFeedback = () => {
|
||
navigation.navigate("MessageDetail", { mid: 1 });
|
||
setNavModal(false);
|
||
};
|
||
const handleBlock = async () => {
|
||
const account = await get("account");
|
||
const subMid = account.mid;
|
||
const objMid = route.params.mid;
|
||
await block(subMid, objMid);
|
||
setNavModal(false);
|
||
};
|
||
return (
|
||
<Modal
|
||
visible={navModal}
|
||
transparent={true}
|
||
statusBarTranslucent
|
||
animationType="fade"
|
||
>
|
||
<TouchableWithoutFeedback onPress={() => setNavModal(false)}>
|
||
<View
|
||
style={{
|
||
backgroundColor: "rgba(0,0,0,0.3)",
|
||
...tailwind("flex-1"),
|
||
}}
|
||
>
|
||
<View
|
||
style={{
|
||
...tailwind("items-center absolute bg-white rounded-lg p-2"),
|
||
top: headerHeight,
|
||
right: 24,
|
||
}}
|
||
>
|
||
<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>
|
||
</View>
|
||
</TouchableWithoutFeedback>
|
||
</Modal>
|
||
);
|
||
};
|
||
|
||
// 平台列表
|
||
const PlatformList = ({ item }) => {
|
||
return (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row justify-between items-center h-12 mt-2 p-2 rounded-xl border-2 border-[#2c2b2f]"
|
||
)}
|
||
>
|
||
<View style={tailwind("flex-row items-center w-1/3")}>
|
||
<Image
|
||
source={item?.link_icon?.images[0]?.urls[0]}
|
||
style={{ aspectRatio: "1/1", ...tailwind("w-8") }}
|
||
/>
|
||
<Text style={tailwind("text-sm text-white ml-1")}>
|
||
{item?.link_name}:
|
||
</Text>
|
||
<Text
|
||
numberOfLines={1}
|
||
ellipsizeMode="tail"
|
||
style={tailwind("text-sm text-white ml-1")}
|
||
>
|
||
{item?.nickname}
|
||
</Text>
|
||
</View>
|
||
<View style={tailwind("flex-row")}>
|
||
<TouchableOpacity
|
||
onPress={async () => {
|
||
await Clipboard.setStringAsync(item.url);
|
||
Alert.alert(null, "复制成功");
|
||
}}
|
||
style={tailwind("flex-row items-center")}
|
||
>
|
||
<NativeImage source={require("../../assets/icon/24DP/copy.png")} />
|
||
<Text style={tailwind("text-xs text-white")}>复制</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
onPress={() => Linking.openURL(item.url)}
|
||
style={tailwind("flex-row items-center ml-4")}
|
||
>
|
||
<NativeImage source={require("../../assets/icon/24DP/goto.png")} />
|
||
<Text style={tailwind("text-xs text-white")}>前往</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
</View>
|
||
);
|
||
};
|
||
|
||
//点击关注按钮
|
||
const [isFollowed, setIsFollowed] = useState(false);
|
||
const handleFollow = async () => {
|
||
const account = await get("account");
|
||
if (isFollowed) {
|
||
await unfollow(account.mid, data?.mid);
|
||
setIsFollowed(!isFollowed);
|
||
} else {
|
||
await follow(account.mid, data?.mid);
|
||
setIsFollowed(!isFollowed);
|
||
}
|
||
};
|
||
|
||
//加载页面时检查关注状态
|
||
useEffect(() => {
|
||
const getRelationData = async () => {
|
||
const account = await get("account");
|
||
const subMid = account.mid;
|
||
const objMid = route.params.mid;
|
||
const temIsFollowed = await checkRelation(subMid, objMid, 0);
|
||
setIsFollowed(temIsFollowed);
|
||
};
|
||
getRelationData();
|
||
}, []);
|
||
|
||
//点击私聊
|
||
const handleSendMessage = () => {
|
||
navigation.navigate("MessageDetail", {
|
||
mid: route.params.mid,
|
||
});
|
||
};
|
||
|
||
//点击查看微信按钮
|
||
const [isAddWechatModalVisible, setIsAddWechatModalVisible] = useState(false);
|
||
|
||
//加载初始骨架屏
|
||
if (isLoading) return <StreamerProfileSkeleton />;
|
||
|
||
return (
|
||
<View style={{ ...tailwind("flex-1"), paddingBottom: insets.bottom }}>
|
||
<ScrollView
|
||
style={{
|
||
paddingBottom: insets.bottom,
|
||
paddingLeft: insets.left,
|
||
paddingRight: insets.right,
|
||
...tailwind("flex-1"),
|
||
}}
|
||
>
|
||
{/* 媒体展示、在线状态 */}
|
||
<MySwiper />
|
||
<View style={tailwind("flex flex-1 mt-4")}>
|
||
<View style={tailwind("flex-1 px-4")}>
|
||
{/* 昵称、认证 */}
|
||
<View style={tailwind("flex flex-row items-center")}>
|
||
<Text
|
||
style={tailwind("text-2xl text-white font-medium mr-1")}
|
||
numberOfLines={1}
|
||
ellipsizeMode="tail"
|
||
>
|
||
{data?.name}
|
||
</Text>
|
||
<NativeImage
|
||
source={require("../../assets/icon/others/verification.png")}
|
||
/>
|
||
</View>
|
||
{/* tag*/}
|
||
<View style={tailwind("flex-row my-2.5")}>
|
||
{data?.tag !== undefined &&
|
||
data?.tag.map((item, index) => {
|
||
if (index > 2) return;
|
||
return (
|
||
<View
|
||
key={index}
|
||
style={{
|
||
...tailwind("py-1 px-2 rounded-md mr-2"),
|
||
backgroundColor: "#FF669E",
|
||
}}
|
||
>
|
||
<Text style={tailwind("text-xs text-white")}>{item}</Text>
|
||
</View>
|
||
);
|
||
})}
|
||
</View>
|
||
{/* 标签 */}
|
||
<View style={tailwind("flex-row flex-wrap pb-4")}>
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/ID.png")}
|
||
/>
|
||
<Text style={tailwind("text-white text-xs font-medium ml-0.5")}>
|
||
{data?.user_id}
|
||
</Text>
|
||
</View>
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/fan.png")}
|
||
/>
|
||
<Text style={tailwind("text-white text-xs font-medium ml-0.5")}>
|
||
{`全网粉丝 : ${data?.fans}万`}
|
||
</Text>
|
||
</View>
|
||
{data?.age && (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
{data?.gender === 1 ? (
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/female.png")}
|
||
/>
|
||
) : (
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/male.png")}
|
||
/>
|
||
)}
|
||
<Text
|
||
style={tailwind("text-white text-xs font-medium ml-0.5")}
|
||
>
|
||
{data?.age}岁
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{data?.height && (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/height.png")}
|
||
/>
|
||
<Text
|
||
style={tailwind("text-white text-xs font-medium ml-0.5")}
|
||
>
|
||
{`${data?.height}cm`}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{data?.weight && (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/weight.png")}
|
||
/>
|
||
<Text
|
||
style={tailwind("text-white text-xs font-medium ml-0.5")}
|
||
>
|
||
{`${data?.weight}kg`}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{data?.constellation && (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/constellation.png")}
|
||
/>
|
||
<Text
|
||
style={tailwind("text-white text-xs font-medium ml-0.5")}
|
||
>
|
||
{data?.constellation}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
{data?.city && (
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center py-0.5 px-2 mr-2 my-1 bg-[#FFFFFF1A] rounded-full"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/icon/12DP/location.png")}
|
||
/>
|
||
<Text
|
||
style={tailwind("text-white text-xs font-medium ml-0.5")}
|
||
>
|
||
{data?.city}
|
||
</Text>
|
||
</View>
|
||
)}
|
||
</View>
|
||
<View
|
||
style={tailwind("h-[3px] rounded-full bg-[#FFFFFF26]")}
|
||
></View>
|
||
{/* 个性签名 */}
|
||
<View style={tailwind("my-4")}>
|
||
<Text style={tailwind("text-base font-medium text-white mb-2")}>
|
||
个性签名
|
||
</Text>
|
||
<Text style={tailwind("text-sm text-[#FFFFFFB2]")}>
|
||
{data?.bio}
|
||
</Text>
|
||
</View>
|
||
<View
|
||
style={tailwind("h-[3px] rounded-full bg-[#FFFFFF26]")}
|
||
></View>
|
||
{/* 平台 */}
|
||
<View style={tailwind("my-4")}>
|
||
<Text style={tailwind("text-base text-white font-medium mb-2")}>
|
||
来这找我玩
|
||
</Text>
|
||
<View
|
||
style={tailwind(
|
||
"flex-row items-center p-2 h-12 rounded-xl border-2 border-[#2c2b2f]"
|
||
)}
|
||
>
|
||
<NativeImage
|
||
source={require("../../assets/images/platform_wechat.png")}
|
||
style={{ aspectRatio: "1/1", ...tailwind("w-8") }}
|
||
/>
|
||
<Text style={tailwind("text-sm text-white ml-1")}>微信:</Text>
|
||
<Text
|
||
onPress={() => setIsAddWechatModalVisible(true)}
|
||
style={tailwind("text-sm text-[#3B69B8] ml-1")}
|
||
numberOfLines={1}
|
||
ellipsizeMode="tail"
|
||
>
|
||
点击查看
|
||
</Text>
|
||
</View>
|
||
{data?.platforms?.map((item) => (
|
||
<PlatformList key={item?.id} item={item} />
|
||
))}
|
||
</View>
|
||
</View>
|
||
</View>
|
||
</ScrollView>
|
||
{/* 关注、私聊、查看微信 */}
|
||
<View
|
||
style={{
|
||
borderTopColor: "#FFFFFF26",
|
||
...tailwind(
|
||
"flex-row py-2 px-4 h-[4.5rem] border-t items-center justify-between"
|
||
),
|
||
}}
|
||
>
|
||
<TouchableOpacity
|
||
onPress={handleFollow}
|
||
style={{
|
||
backgroundColor: "#FFFFFF1A",
|
||
...tailwind(
|
||
"flex-row items-center justify-center h-10 px-6 rounded-full mr-4"
|
||
),
|
||
}}
|
||
>
|
||
<Text style={tailwind("text-base text-white font-medium")}>
|
||
{isFollowed ? "已关注" : "关注"}
|
||
</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
onPress={handleSendMessage}
|
||
style={tailwind(
|
||
"flex-row bg-[#FFFFFF1A] items-center justify-center h-10 px-6 rounded-full mr-4"
|
||
)}
|
||
>
|
||
<Text style={tailwind("text-base text-white font-medium")}>私聊</Text>
|
||
</TouchableOpacity>
|
||
<TouchableOpacity
|
||
onPress={() => setIsAddWechatModalVisible(true)}
|
||
style={tailwind(
|
||
"flex-row flex-1 bg-[#FF669E] items-center justify-center h-10 rounded-full"
|
||
)}
|
||
>
|
||
<Text style={tailwind("text-base text-white font-medium")}>
|
||
添加微信
|
||
</Text>
|
||
</TouchableOpacity>
|
||
</View>
|
||
{/* 拉黑、举报Modal */}
|
||
<NavbarRightModal />
|
||
{/* 查看微信Modal */}
|
||
{data?.wechat_lock_type === 0 ? (
|
||
<GetWechatModal
|
||
visible={isAddWechatModalVisible}
|
||
setVisible={setIsAddWechatModalVisible}
|
||
streamerMid={route.params.mid}
|
||
/>
|
||
) : (
|
||
<SubmitWechatModal
|
||
visible={isAddWechatModalVisible}
|
||
setVisible={setIsAddWechatModalVisible}
|
||
streamerMid={route.params.mid}
|
||
/>
|
||
)}
|
||
</View>
|
||
);
|
||
}
|