tiefen_space_app/App.jsx

857 lines
34 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import React, {
useState,
useEffect,
useReducer,
useMemo,
createContext,
useCallback,
} from "react";
import {
View,
Text,
Image as NativeImage,
TouchableOpacity,
} from "react-native";
import * as SplashScreen from "expo-splash-screen";
import HomeTab from "./screeens/HomeTab";
import Login from "./screeens/Login";
import StreamerProfile from "./screeens/StreamerProfile";
import UserProfile from "./screeens/UserProfile";
import { createNativeStackNavigator } from "@react-navigation/native-stack";
import { NavigationContainer, DefaultTheme } from "@react-navigation/native";
import { SafeAreaProvider } from "react-native-safe-area-context";
import { TailwindProvider } from "tailwind-rn";
import utilities from "./tailwind.json";
import ForgetPassword from "./screeens/Login/ForgetPassword";
import SetPassword from "./screeens/Login/SetPassword";
import Toast from "react-native-toast-message";
import loginReducer from "./utils/loginReducer";
import MessageDetail from "./screeens/Messages/MessageDetail";
import EditUserProfile from "./screeens/EditUserProfile";
import Wallet from "./screeens/Wallet";
import StreamerPosts from "./screeens/StreamerPosts";
import UnlockedWechat from "./screeens/UnlockedWechat";
import EditStreamerProfile from "./screeens/EditStreamerProfile";
import Relationship from "./screeens/Relationship";
import StreamerVerification from "./screeens/StreamerVerification";
import EditPlatformOrder from "./screeens/EditPlatformOrder";
import Setting from "./screeens/Setting";
import WechatWaitingToAdd from "./screeens/WechatWaitingToAdd";
import Search from "./screeens/Search";
import CreatePost from "./screeens/CreatePost";
import CreateImagePost from "./screeens/CreateImagePost";
import CreateVideoPost from "./screeens/CreateVideoPost";
import WebWithHeader from "./screeens/WebWithHeader";
import WebWithoutHeader from "./screeens/WebWithoutHeader";
import SpaceIntroduce from "./screeens/SpaceIntroduce";
import StreamerSpace from "./screeens/StreamerSpace";
import CreateSpace from "./screeens/CreateSpace";
import SpaceSetting from "./screeens/SpaceSetting";
import ShareSpace from "./screeens/ShareSpace";
import EditSpacePost from "./screeens/EditSpacePost";
import VisibleToOneselfSpacePosts from "./screeens/VisibleToOneselfSpacePosts";
import EditStreamerInfo from "./screeens/EditStreamerInfo";
import EditStreamerMedia from "./screeens/EditStreamerMedia";
import PostPurchasers from "./screeens/PostPurchasers";
import UpdateModal from "./components/UpdateModal";
import StreamerNavigatorModal from "./components/StreamerNavigatorModal";
import { Icon } from "@rneui/themed";
import { StatusBar } from "expo-status-bar";
import { save, get, remove, storeAppInfo } from "./utils/storeInfo";
import baseRequest from "./utils/baseRequest";
import { generateSignature } from "./utils/crypto";
import * as Clipboard from "expo-clipboard";
import PrivatyModal from "./components/PrivatyModal";
import * as Sentry from "@sentry/react-native";
import { ImageViewerProvider } from "./context/ImageViewProvider";
const RootStack = createNativeStackNavigator();
export const AuthContext = createContext("");
SplashScreen.preventAutoHideAsync();
Sentry.init({
dsn: "https://decf8ef204a5d5144c27608bf86851ce@o4506958331904000.ingest.us.sentry.io/4506958351630336",
debug: false, // If `true`, Sentry will try to print out useful debugging information if something goes wrong with sending the event. Set it to `false` in production
enabled: !__DEV__,
});
const App = () => {
//app主题
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: "#07050A",
},
};
//保存用户剪贴板内容
const [inviterCode, setInviterCode] = useState();
//未同意用户协议和隐私政策时展示隐私弹窗
const [isPrivatyModalOpen, setIsPrivatyModalOpen] = useState(false);
//隐私弹窗是否同意
const [checked, setChecked] = useState(false);
//如果是第一次打开app则展示隐私弹窗
useEffect(() => {
const handlePrivatyModal = async () => {
const notFirstTimeOpenApp = await get("not_first_time_open_app");
if (!notFirstTimeOpenApp) {
setIsPrivatyModalOpen(true);
save("not_first_time_open_app", 1);
return;
}
};
handlePrivatyModal();
}, []);
//获取环境变量
const apiUrl = process.env.EXPO_PUBLIC_API_URL;
//登录逻辑
const [state, dispatch] = useReducer(loginReducer, {
isSignin: false,
userToken: null,
});
const authContext = useMemo(
() => ({
signIn: async (data, mobilePhone, regionCode) => {
await save("token", data.data.token);
await save("account", data.data.account);
await save("mobile_phone", mobilePhone);
await save("region_code", regionCode);
dispatch({ type: "SIGN_IN", token: data.data.token });
},
signOut: async () => {
const base = await baseRequest();
const account = await get("account");
const signature = await generateSignature({
mid: account?.mid,
...base,
});
try {
await fetch(`${apiUrl}/api/login/logout?signature=${signature}`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
mid: account?.mid,
...base,
}),
});
await remove("token");
await remove("account");
} catch (error) {
console.error(error);
} finally {
dispatch({ type: "SIGN_OUT" });
}
},
inviterCode: inviterCode,
checked: checked,
setChecked: setChecked,
}),
[inviterCode, checked]
);
//控制开屏图片出现
const [appIsReady, setAppIsReady] = useState(false);
//控制更新弹窗出现
const [isUpdateModalVisible, setIsUpdateModalVisible] = useState(false);
//保存最新版本信息
const [versionData, setVersionData] = useState({});
useEffect(() => {
async function prepare() {
await storeAppInfo();
try {
const token = await get("token");
const account = await get("account");
const base = await baseRequest();
const signature = await generateSignature({
...base,
});
//检查当前token是否有效
if (token && account) {
const response = await fetch(
`${apiUrl}/api/login/validate?signature=${signature}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
...base,
}),
}
);
const data = await response.json();
if (data.ret === 1) {
dispatch({ type: "SIGN_IN", token: token });
}
}
//检查更新
const checkUpdataResponse = await fetch(
`${apiUrl}/api/version/is_there_a_new_version_available?signature=${signature}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
...base,
}),
}
);
const checkUpdataData = await checkUpdataResponse.json();
if (checkUpdataData.ret === -1) {
return;
}
if (checkUpdataData.data.result) {
setIsUpdateModalVisible(true);
setVersionData(checkUpdataData.data);
}
} catch (e) {
console.warn(e);
} finally {
setAppIsReady(true);
}
}
prepare();
}, []);
//获取用户剪贴板内容
const getClipboardContent = useCallback(async () => {
const content = await Clipboard.getStringAsync();
const pattern = /https:\/\/tiefen\.fun\/(?:zone\/)?(\d+)/;
const match = content.match(pattern);
if (match && match[1]) {
const intInviterCode = parseInt(match[1], 10);
setInviterCode(intInviterCode);
await Clipboard.setStringAsync("");
return;
}
setInviterCode(null);
}, []);
//登录后获取用户剪贴板内容
useEffect(() => {
if (!state.isSignin) return;
getClipboardContent();
}, [state.isSignin]);
//当用户已经登录查看根据剪贴板内容展示主播Modal
const [streamerNavigatorModalStatus, setStreamerNavigatorModalStatus] =
useState({
open: false,
data: {},
});
useEffect(() => {
const getInviterData = async () => {
//获取主播数据
if (!inviterCode) return;
if (!state.isSignin) return;
if (isUpdateModalVisible) return;
try {
const base = await baseRequest();
const signature = await generateSignature({
user_id: inviterCode,
...base,
});
const inviterResponse = await fetch(
`${apiUrl}/api/streamer/list_ext_by_user_id?signature=${signature}`,
{
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
user_id: inviterCode,
...base,
}),
}
);
const inviterData = await inviterResponse.json();
if (inviterData.ret === -1) {
return;
}
setStreamerNavigatorModalStatus({
open: true,
data: inviterData.data.streamer_ext,
});
} catch (e) {
console.warn(e);
}
};
getInviterData();
}, [state.isSignin, inviterCode, isUpdateModalVisible]);
//展示和隐藏splash
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<TailwindProvider utilities={utilities}>
<AuthContext.Provider value={authContext}>
<SafeAreaProvider>
<StatusBar style="light" translucent />
<View style={{ flex: 1, backgroundColor: "#07050A" }}>
<NavigationContainer onReady={onLayoutRootView} theme={MyTheme}>
<ImageViewerProvider>
<RootStack.Navigator>
{state.isSignin ? (
<>
<RootStack.Screen
name="HomeTab"
component={HomeTab}
options={{
headerShown: false,
}}
/>
<RootStack.Screen
name="StreamerProfile"
component={StreamerProfile}
options={({ navigation }) => ({
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.goBack()}
>
<NativeImage
source={require("./assets/icon/others/goback.png")}
/>
</TouchableOpacity>
),
headerRight: () => (
<Icon
type="ionicon"
name="ellipsis-vertical"
size={24}
color="white"
/>
),
headerTransparent: true,
title: "",
})}
/>
<RootStack.Screen
name="UserProfile"
component={UserProfile}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
headerTransparent: true,
title: "",
})}
/>
<RootStack.Screen
name="WechatWaitingToAdd"
component={WechatWaitingToAdd}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "待添加微信",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="MessageDetail"
component={MessageDetail}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditUserProfile"
component={EditUserProfile}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="Wallet"
component={Wallet}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
headerTransparent: true,
title: "我的钱包",
headerTitleStyle: { color: "white" },
})}
/>
<RootStack.Screen
name="Setting"
component={Setting}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="Relationship"
component={Relationship}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "关系",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="StreamerVerification"
component={StreamerVerification}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="UnlockedWechat"
component={UnlockedWechat}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "已解锁微信",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditStreamerProfile"
component={EditStreamerProfile}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "编辑主页",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditPlatformOrder"
component={EditPlatformOrder}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "编辑平台",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="StreamerPosts"
component={StreamerPosts}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "动态",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="Search"
component={Search}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="CreatePost"
component={CreatePost}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "发布动态",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="CreateImagePost"
component={CreateImagePost}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "图文动态(空间内)",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="CreateVideoPost"
component={CreateVideoPost}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "视频动态(空间内)",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="WebWithHeader"
component={WebWithHeader}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="WebWithoutHeader"
component={WebWithoutHeader}
options={({ navigation }) => ({
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.goBack()}
>
<NativeImage
source={require("./assets/icon/others/goback.png")}
/>
</TouchableOpacity>
),
headerTransparent: true,
title: "",
})}
/>
<RootStack.Screen
name="SpaceIntroduce"
component={SpaceIntroduce}
options={({ navigation }) => ({
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.goBack()}
>
<NativeImage
source={require("./assets/icon/others/goback.png")}
/>
</TouchableOpacity>
),
headerTransparent: true,
title: "",
})}
/>
<RootStack.Screen
name="StreamerSpace"
component={StreamerSpace}
options={({ navigation }) => ({
headerLeft: () => (
<TouchableOpacity
onPress={() => navigation.goBack()}
>
<NativeImage
source={require("./assets/icon/others/goback.png")}
/>
</TouchableOpacity>
),
headerTransparent: true,
title: "",
})}
/>
<RootStack.Screen
name="CreateSpace"
component={CreateSpace}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
headerRight: () => (
<TouchableOpacity
onPress={() =>
navigation.navigate("WebWithHeader", {
title: "平台准则",
uri: `${process.env.EXPO_PUBLIC_WEB_URL}/doc/platformguidelines`,
})
}
>
<Text style={{ color: "#FF669E", fontSize: 16 }}>
平台准则
</Text>
</TouchableOpacity>
),
title: "开通空间",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="SpaceSetting"
component={SpaceSetting}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="ShareSpace"
component={ShareSpace}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "分享空间",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditSpacePost"
component={EditSpacePost}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "重新编辑",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="VisibleToOneselfSpacePosts"
component={VisibleToOneselfSpacePosts}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "审核未通过",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditStreamerInfo"
component={EditStreamerInfo}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "编辑资料",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="EditStreamerMedia"
component={EditStreamerMedia}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "照片墙",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
<RootStack.Screen
name="PostPurchasers"
component={PostPurchasers}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
title: "已购用户",
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
</>
) : (
<>
<RootStack.Screen
name="Login"
component={Login}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="ForgetPassword"
component={ForgetPassword}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="SetPassword"
component={SetPassword}
options={{ headerShown: false }}
/>
<RootStack.Screen
name="WebWithHeader"
component={WebWithHeader}
options={({ navigation }) => ({
headerLeft: () => (
<Icon
type="ionicon"
name="chevron-back"
size={32}
color="white"
onPress={() => navigation.goBack()}
/>
),
headerTitleStyle: { color: "white" },
headerStyle: { backgroundColor: "#07050A" },
})}
/>
</>
)}
</RootStack.Navigator>
<StreamerNavigatorModal
status={streamerNavigatorModalStatus}
setStatus={setStreamerNavigatorModalStatus}
/>
<PrivatyModal
visible={isPrivatyModalOpen}
setVisible={setIsPrivatyModalOpen}
confirm={() => {
setIsPrivatyModalOpen(false);
setChecked(true);
}}
/>
</ImageViewerProvider>
</NavigationContainer>
</View>
<Toast />
<UpdateModal
visible={isUpdateModalVisible}
setVisible={setIsUpdateModalVisible}
data={versionData}
/>
</SafeAreaProvider>
</AuthContext.Provider>
</TailwindProvider>
);
};
export default Sentry.wrap(App);