tiefen_space_app/App.jsx

645 lines
24 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, 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 StreamerInviteUser from "./screeens/StreamerInviteUser";
import Setting from "./screeens/Setting";
import WechatWaitingToAdd from "./screeens/WechatWaitingToAdd";
import Search from "./screeens/Search";
import CreatePost from "./screeens/CreatePost";
import WebWithHeader from "./screeens/WebWithHeader";
import WebWithoutHeader from "./screeens/WebWithoutHeader";
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";
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
});
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() {
try {
//重新获取设备和app信息
storeAppInfo();
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\/(\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;
if (!checked) return; //用户未同意隐私政策时不获取剪贴板信息
getClipboardContent();
}, [checked]);
//登录后获取用户剪贴板内容
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}>
<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="StreamerInviteUser"
component={StreamerInviteUser}
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="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="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);
}}
/>
</NavigationContainer>
</View>
<Toast />
<UpdateModal
visible={isUpdateModalVisible}
setVisible={setIsUpdateModalVisible}
data={versionData}
/>
</SafeAreaProvider>
</AuthContext.Provider>
</TailwindProvider>
);
};
export default Sentry.wrap(App);