Merge pull request '重新封装ImageViewer组件;为ImageViewer组件增加付费跳转功能;完善空间相关功能' (#6) from imageviewer into main

Reviewed-on: https://git.wishpal.cn/wishpal_ironfan/tiefen_space_app/pulls/6
This commit is contained in:
yezian 2024-04-23 22:22:47 +08:00
commit ad6a1a89d8
9 changed files with 674 additions and 573 deletions

1017
App.jsx

File diff suppressed because it is too large Load Diff

View File

@ -16,23 +16,37 @@ import Toast from "react-native-toast-message";
import { get } from "../../utils/storeInfo"; import { get } from "../../utils/storeInfo";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import MyModal from "../MyModal"; import MyModal from "../MyModal";
import { Image } from "expo-image";
import { LinearProgress, Button } from "@rneui/themed";
import { LinearGradient } from "expo-linear-gradient";
//isVisible boolean
//setIsVisible function
//imageUrls [{ url: string }] //imageUrls [{ url: string }]
//index int //index int
export default function ImageViewer({ export function ImageViewer({
isVisible, isShow,
setIsVisible, onClose,
imageUrls, imageUrls,
index, index,
lockedStartIndex,
unlockUrl,
onChange,
}) { }) {
const tailwind = useTailwind(); const tailwind = useTailwind();
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const navigation = useNavigation(); const navigation = useNavigation();
const [isVisible, setIsVisible] = useState(false);
useEffect(() => {
setIsVisible(isShow);
}, [isShow]);
const closeImageViewer = () => {
setIsVisible(false);
onClose && onClose();
};
const checkRole = useCallback(async () => { const checkRole = useCallback(async () => {
const account = await get("account"); const account = await get("account");
const role = account.role; const role = account.role;
@ -73,7 +87,7 @@ export default function ImageViewer({
saveToLocal(); saveToLocal();
return; return;
} }
setIsVisible(false); closeImageViewer();
navigation.navigate("WebWithoutHeader", { navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip", uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
}); });
@ -130,10 +144,50 @@ export default function ImageViewer({
<OriginImageViewer <OriginImageViewer
imageUrls={imageUrls} imageUrls={imageUrls}
index={index} index={index}
onClick={() => setIsVisible(false)} onClick={closeImageViewer}
onSwipeDown={() => setIsVisible(false)} onSwipeDown={closeImageViewer}
onChange={(index) => onChange(index)}
enableSwipeDown enableSwipeDown
backgroundColor="#07050A" backgroundColor="#07050A"
renderImage={(props) => {
if (index >= lockedStartIndex) {
return (
<View>
<Image
{...props}
blurRadius={Platform.OS === "ios" ? 100 : 10}
/>
<View
style={tailwind(
"absolute flex justify-center items-center top-0 left-0 bg-[#00000080] w-full h-full"
)}
>
<Button
onPress={() => {
closeImageViewer();
navigation.navigate("WebWithoutHeader", {
uri: unlockUrl,
});
}}
ViewComponent={LinearGradient}
radius="999"
size="md"
linearGradientProps={{
colors: ["#FF668B", "#FF66F0"],
start: { x: 0, y: 0.5 },
end: { x: 1, y: 0.5 },
}}
titleStyle={tailwind("text-base font-medium")}
containerStyle={tailwind("w-full px-4")}
>
立即解锁
</Button>
</View>
</View>
);
}
return <Image {...props} />;
}}
renderFooter={ renderFooter={
Platform.OS === "android" Platform.OS === "android"
? (index) => ( ? (index) => (
@ -178,7 +232,7 @@ export default function ImageViewer({
}} }}
confirm={() => { confirm={() => {
setIsVipModalVisible(false); setIsVipModalVisible(false);
setIsVisible(false); closeImageViewer();
navigation.navigate("WebWithoutHeader", { navigation.navigate("WebWithoutHeader", {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip", uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
}); });

View File

@ -10,7 +10,6 @@ import {
import React, { useEffect, useState, useCallback } from "react"; import React, { useEffect, useState, useCallback } from "react";
import { useTailwind } from "tailwind-rn"; import { useTailwind } from "tailwind-rn";
import { Image } from "expo-image"; import { Image } from "expo-image";
import ImageViewer from "../ImageViewer";
import VideoModal from "../VideoModal"; import VideoModal from "../VideoModal";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import { follow, unfollow, block } from "../../utils/relation"; import { follow, unfollow, block } from "../../utils/relation";
@ -18,6 +17,7 @@ import baseRequest from "../../utils/baseRequest";
import { generateSignature } from "../../utils/crypto"; import { generateSignature } from "../../utils/crypto";
import Toast from "react-native-toast-message"; import Toast from "react-native-toast-message";
import { get } from "../../utils/storeInfo"; import { get } from "../../utils/storeInfo";
import { useImageViewer } from "../../context/ImageViewProvider";
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk"; const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
@ -303,10 +303,10 @@ export default function Post({ isBlur, data }) {
function ImageDisplay({ blur, media }) { function ImageDisplay({ blur, media }) {
const navigation = useNavigation(); const navigation = useNavigation();
const tailwind = useTailwind(); const tailwind = useTailwind();
const [isModalVisible, setIsModalVisible] = useState(false);
const [imageIndex, setImageIndex] = useState();
const images = media.map((item) => ({ url: item.urls[0] })); const images = media.map((item) => ({ url: item.urls[0] }));
const { showImageViewer } = useImageViewer();
if (images.length === 0) return null; if (images.length === 0) return null;
return ( return (
@ -323,8 +323,10 @@ function ImageDisplay({ blur, media }) {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip", uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
}) })
: () => { : () => {
setIsModalVisible(true); showImageViewer({
setImageIndex(index); imageUrls: images,
index: index,
});
} }
} }
style={{ aspectRatio: 1, ...tailwind("basis-1/3 p-0.5") }} style={{ aspectRatio: 1, ...tailwind("basis-1/3 p-0.5") }}
@ -350,8 +352,10 @@ function ImageDisplay({ blur, media }) {
uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip", uri: process.env.EXPO_PUBLIC_WEB_URL + "/vip",
}) })
: () => { : () => {
setIsModalVisible(true); showImageViewer({
setImageIndex(0); imageUrls: images,
index: 0,
});
} }
} }
style={{ style={{
@ -373,12 +377,6 @@ function ImageDisplay({ blur, media }) {
/> />
</TouchableOpacity> </TouchableOpacity>
)} )}
<ImageViewer
isVisible={isModalVisible}
setIsVisible={setIsModalVisible}
imageUrls={images}
index={imageIndex}
/>
</View> </View>
); );
} }

View File

@ -10,7 +10,6 @@ import {
import React, { useEffect, useState } from "react"; import React, { useEffect, useState } from "react";
import { useTailwind } from "tailwind-rn"; import { useTailwind } from "tailwind-rn";
import { Image } from "expo-image"; import { Image } from "expo-image";
import ImageViewer from "../ImageViewer";
import VideoModal from "../VideoModal"; import VideoModal from "../VideoModal";
import { useNavigation } from "@react-navigation/native"; import { useNavigation } from "@react-navigation/native";
import formatTimestamp from "../../utils/formatTimestamp"; import formatTimestamp from "../../utils/formatTimestamp";
@ -22,6 +21,7 @@ import { get } from "../../utils/storeInfo";
import { Icon } from "@rneui/themed"; import { Icon } from "@rneui/themed";
import ParsedText from "react-native-parsed-text"; import ParsedText from "react-native-parsed-text";
import * as Linking from "expo-linking"; import * as Linking from "expo-linking";
import { useImageViewer } from "../../context/ImageViewProvider";
//todo: //todo:
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk"; const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
@ -200,6 +200,7 @@ export default function SpacePost({ data }) {
{data.media_component.video_ids?.length === 0 || {data.media_component.video_ids?.length === 0 ||
data.media_component.video_ids === null ? ( data.media_component.video_ids === null ? (
<ImageDisplay <ImageDisplay
data={data}
isUnlocked={data.is_zone_moment_unlocked} isUnlocked={data.is_zone_moment_unlocked}
visibleRange={data.media_visible_range} visibleRange={data.media_visible_range}
mediaAmount={data.media_amount} mediaAmount={data.media_amount}
@ -208,9 +209,21 @@ export default function SpacePost({ data }) {
) : ( ) : (
<TouchableOpacity <TouchableOpacity
activeOpacity={1} activeOpacity={1}
onPress={() => { onPress={
setShowVideo(true); data.is_zone_moment_unlocked === 1
}} ? () => {
setShowVideo(true);
}
: () =>
navigation.navigate("WebWithoutHeader", {
uri:
process.env.EXPO_PUBLIC_WEB_URL +
"/zone/pay/" +
data?.zid +
"/h5_zone_moment/" +
data?.id,
})
}
> >
<View> <View>
<PosterDisplay <PosterDisplay
@ -442,17 +455,15 @@ export default function SpacePost({ data }) {
} }
// //
function ImageDisplay({ isUnlocked, visibleRange, mediaAmount, media }) { function ImageDisplay({ data, isUnlocked, visibleRange, mediaAmount, media }) {
const navigation = useNavigation();
const tailwind = useTailwind(); const tailwind = useTailwind();
const [isModalVisible, setIsModalVisible] = useState(false);
//url使 //url使
const displayMedia = media.concat( const displayMedia = media.concat(
new Array(mediaAmount - media.length).fill(media[0]) new Array(mediaAmount - media.length).fill(media[0])
); );
const [imageIndex, setImageIndex] = useState(); const { showImageViewer } = useImageViewer();
const images = displayMedia.map((item) => { const images = displayMedia.map((item) => {
return { url: item.urls[0] }; return { url: item.urls[0] };
}); });
@ -470,8 +481,17 @@ function ImageDisplay({ isUnlocked, visibleRange, mediaAmount, media }) {
activeOpacity={1} activeOpacity={1}
key={index} key={index}
onPress={() => { onPress={() => {
setIsModalVisible(true); showImageViewer({
setImageIndex(index); imageUrls: images,
index: index,
lockedStartIndex: visibleRange,
unlockUrl:
process.env.EXPO_PUBLIC_WEB_URL +
"/zone/pay/" +
data?.zid +
"/h5_zone_moment/" +
data?.id,
});
}} }}
style={ style={
displayMedia.length > 1 displayMedia.length > 1
@ -546,12 +566,6 @@ function ImageDisplay({ isUnlocked, visibleRange, mediaAmount, media }) {
</TouchableOpacity> </TouchableOpacity>
); );
})} })}
<ImageViewer
isVisible={isModalVisible}
setIsVisible={setIsModalVisible}
imageUrls={images}
index={imageIndex}
/>
</View> </View>
); );
} }

View File

@ -0,0 +1,37 @@
import React, { useState, createContext, useContext } from "react";
import { ImageViewer } from "../components/ImageViewer";
const ImageViewerContext = createContext();
export const ImageViewerProvider = ({ children }) => {
const [imageViewerProps, setImageViewerProps] = useState(null);
const showImageViewer = (props) => {
setImageViewerProps({ ...props, isShow: true });
};
const closeImageViewer = () => {
setImageViewerProps(null);
};
const changeImageViewerIndex = (index) => {
setImageViewerProps((prev) => {
return { ...prev, index: index };
});
};
return (
<ImageViewerContext.Provider value={{ showImageViewer }}>
{children}
{imageViewerProps && (
<ImageViewer
{...imageViewerProps}
onClose={closeImageViewer}
onChange={changeImageViewerIndex}
/>
)}
</ImageViewerContext.Provider>
);
};
export const useImageViewer = () => useContext(ImageViewerContext);

View File

@ -19,7 +19,7 @@ import VideoModal from "../../components/VideoModal";
import SpaceIntroduceSkeleton from "./skeleton"; import SpaceIntroduceSkeleton from "./skeleton";
import { usePreventScreenCapture } from "expo-screen-capture"; import { usePreventScreenCapture } from "expo-screen-capture";
import { useFocusEffect } from "@react-navigation/native"; import { useFocusEffect } from "@react-navigation/native";
import ImageViewer from "../../components/ImageViewer"; import { useImageViewer } from "../../context/ImageViewProvider";
export default function SpaceIntroduce({ navigation, route }) { export default function SpaceIntroduce({ navigation, route }) {
usePreventScreenCapture(); usePreventScreenCapture();
@ -27,6 +27,8 @@ export default function SpaceIntroduce({ navigation, route }) {
const insets = useSafeAreaInsets(); const insets = useSafeAreaInsets();
const headerHeight = useHeaderHeight(); const headerHeight = useHeaderHeight();
const { showImageViewer } = useImageViewer();
const [data, setData] = useState({}); const [data, setData] = useState({});
const [isLoading, setIsloading] = useState(true); const [isLoading, setIsloading] = useState(true);
const getData = async () => { const getData = async () => {
@ -79,10 +81,6 @@ export default function SpaceIntroduce({ navigation, route }) {
}, []) }, [])
); );
//modal
const [imagesVisible, setImagesVisible] = useState(false);
//modal
const [imageIndex, setImageIndex] = useState("");
const images = data?.streamer_ext?.album?.images?.map((image, index) => { const images = data?.streamer_ext?.album?.images?.map((image, index) => {
if (index > 4) return; if (index > 4) return;
return image?.urls[0]; return image?.urls[0];
@ -277,8 +275,10 @@ export default function SpaceIntroduce({ navigation, route }) {
<TouchableOpacity <TouchableOpacity
activeOpacity={1} activeOpacity={1}
onPress={() => { onPress={() => {
setImageIndex(index); showImageViewer({
setImagesVisible(true); imageUrls: imagesForImageViewer,
index: index,
});
}} }}
key={index} key={index}
style={tailwind("basis-1/3 p-0.5")} style={tailwind("basis-1/3 p-0.5")}
@ -384,13 +384,6 @@ export default function SpaceIntroduce({ navigation, route }) {
setVisible={setShowVideo} setVisible={setShowVideo}
url={data?.streamer_ext?.shorts?.videos[0]?.urls[0]} url={data?.streamer_ext?.shorts?.videos[0]?.urls[0]}
/> />
{/* 展示图片的modal */}
<ImageViewer
isVisible={imagesVisible}
setIsVisible={setImagesVisible}
imageUrls={imagesForImageViewer}
index={imageIndex}
/>
</View> </View>
); );
} }

View File

@ -328,7 +328,7 @@ export default function AgencySetting({ navigation, route }) {
ID{data.third_partner_account.user_id} ID{data.third_partner_account.user_id}
</Text> </Text>
<Text style={tailwind("text-white text-base font-medium")}> <Text style={tailwind("text-white text-base font-medium")}>
分成比例{data.sharing_ratio * 100}% 分成比例{(data.sharing_ratio * 100).toFixed()}%
</Text> </Text>
</View> </View>
)} )}

View File

@ -313,7 +313,7 @@ export default function CollaboratorSetting({ navigation, route }) {
<Text <Text
style={tailwind("text-white text-xs font-medium ml-0.5")} style={tailwind("text-white text-xs font-medium ml-0.5")}
> >
{item?.sharing_ratio * 100}% {(item?.sharing_ratio * 100).toFixed()}%
</Text> </Text>
</View> </View>
</View> </View>
@ -374,7 +374,7 @@ export default function CollaboratorSetting({ navigation, route }) {
ID{selfData[0]?.collaborator_account?.user_id} ID{selfData[0]?.collaborator_account?.user_id}
</Text> </Text>
<Text style={tailwind("text-white text-base font-medium")}> <Text style={tailwind("text-white text-base font-medium")}>
分成比例{selfData[0]?.sharing_ratio * 100}% 分成比例{(selfData[0]?.sharing_ratio * 100).toFixed()}%
</Text> </Text>
</View> </View>
<View style={tailwind("mt-8")}> <View style={tailwind("mt-8")}>
@ -411,7 +411,7 @@ export default function CollaboratorSetting({ navigation, route }) {
总分成比例 总分成比例
</Text> </Text>
<Text style={tailwind("text-[#F53030] text-3xl font-medium my-2")}> <Text style={tailwind("text-[#F53030] text-3xl font-medium my-2")}>
{data?.zone_third_partner?.sharing_ratio * 100}% {(data?.zone_third_partner?.sharing_ratio * 100).toFixed()}%
</Text> </Text>
<Text style={tailwind("text-[#FFFFFF80] text-sm")}> <Text style={tailwind("text-[#FFFFFF80] text-sm")}>
修改比例请联系平台客服 修改比例请联系平台客服

View File

@ -14,7 +14,6 @@ import React, { useState, useEffect } from "react";
import { useSafeAreaInsets } from "react-native-safe-area-context"; import { useSafeAreaInsets } from "react-native-safe-area-context";
import { useTailwind } from "tailwind-rn"; import { useTailwind } from "tailwind-rn";
import { Image } from "expo-image"; import { Image } from "expo-image";
import ImageViewer from "../../components/ImageViewer";
import Swiper from "react-native-swiper"; import Swiper from "react-native-swiper";
import { Divider, Icon } from "@rneui/themed"; import { Divider, Icon } from "@rneui/themed";
import VideoModal from "../../components/VideoModal"; import VideoModal from "../../components/VideoModal";
@ -30,6 +29,7 @@ import { get } from "../../utils/storeInfo";
import StreamerProfileSkeleton from "./skeleton"; import StreamerProfileSkeleton from "./skeleton";
import { generateSignature } from "../../utils/crypto"; import { generateSignature } from "../../utils/crypto";
import Svg, { Path } from "react-native-svg"; import Svg, { Path } from "react-native-svg";
import { useImageViewer } from "../../context/ImageViewProvider";
const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk"; const blurhash = "LcKUTa%gOYWBYRt6xuoJo~s8V@fk";
@ -257,10 +257,9 @@ export default function StreamerProfile({ navigation, route }) {
const MySwiper = () => { const MySwiper = () => {
//modal //modal
const [videoVisible, setVideoVisible] = useState(false); const [videoVisible, setVideoVisible] = useState(false);
//modal
const [imagesVisible, setImagesVisible] = useState(false); const { showImageViewer } = useImageViewer();
//modal
const [imageIndex, setImageIndex] = useState("");
const images = data?.album?.images?.map((image) => image?.urls[0]); const images = data?.album?.images?.map((image) => image?.urls[0]);
const imagesForImageViewer = images?.map((url) => ({ url })); const imagesForImageViewer = images?.map((url) => ({ url }));
return ( return (
@ -312,8 +311,10 @@ export default function StreamerProfile({ navigation, route }) {
) : ( ) : (
<TouchableWithoutFeedback <TouchableWithoutFeedback
onPress={() => { onPress={() => {
setImageIndex(index - 1); showImageViewer({
setImagesVisible(true); imageUrls: imagesForImageViewer,
index: index - 1,
});
}} }}
> >
<Image <Image
@ -335,13 +336,6 @@ export default function StreamerProfile({ navigation, route }) {
setVisible={setVideoVisible} setVisible={setVideoVisible}
url={data?.shorts?.videos[0]?.urls[0]} url={data?.shorts?.videos[0]?.urls[0]}
/> />
{/* 展示图片的modal */}
<ImageViewer
isVisible={imagesVisible}
setIsVisible={setImagesVisible}
imageUrls={imagesForImageViewer}
index={imageIndex}
/>
</> </>
); );
}; };