| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207 |
- import { AnimatedSplashOverlay } from '@/components/animated-icon';
- import { antdTheme } from '@/constants/antd-theme';
- import { Colors } from '@/constants/theme';
- import '@/global.css';
- import api from '@/utils/api';
- import { AuthProvider } from '@/utils/auth';
- import { getGlobalStorage } from '@/utils/storage';
- import { Modal, Provider, Toast } from '@ant-design/react-native';
- import type { Theme } from '@react-navigation/native';
- import {
- DefaultTheme as ReactNavigationDefaultTheme,
- ThemeProvider,
- } from '@react-navigation/native';
- import * as application from 'expo-application';
- import { BlurView } from 'expo-blur';
- import { useFonts } from 'expo-font';
- import { Image } from 'expo-image';
- import { Stack } from 'expo-router';
- import * as SplashScreen from 'expo-splash-screen';
- import * as updates from 'expo-updates';
- import { useEffect, useState } from 'react';
- import { Linking, Platform, View } from 'react-native';
- SplashScreen.preventAutoHideAsync();
- // import * as app from 'expo-'
- export const DefaultTheme: Theme = {
- ...ReactNavigationDefaultTheme,
- colors: {
- ...ReactNavigationDefaultTheme.colors,
- primary: antdTheme.brand_primary,
- background: antdTheme.fill_body,
- card: antdTheme.fill_base,
- text: antdTheme.color_text_base,
- border: antdTheme.border_color_base,
- notification: antdTheme.brand_error,
- },
- };
- export default function RootLayout() {
- const [initializing, setInitlizing] = useState(true);
- // const colorScheme = useColorScheme();
- const [fontsLoaded] = useFonts({
- antoutline: require('@ant-design/icons-react-native/fonts/antoutline.ttf'),
- });
- useEffect(() => {
- const now = Date.now() / 1000;
- async function checkApp() {
- try {
- const lastTime = parseInt(getGlobalStorage().getString('last_update_app') || '0');
- if (now - lastTime < 15 * 60000) {
- return;
- }
- const {
- version,
- package: pkg,
- type,
- force,
- } = await api.post<{ version: string, package: string, type: 'apk' | 'url', force: boolean }>('common/check_version', {
- platform: Platform.OS,
- version: application.nativeApplicationVersion,
- }, { timeout: 5000 });
- getGlobalStorage().set('last_update_app', now);
- if (!version || version === application.nativeApplicationVersion) {
- return;
- }
- const va = version.split('.');
- let vb = (application.nativeApplicationVersion || '').split('.');
- let update = false;
- for (let i = 0; i < va.length; i++) {
- if (vb.length <= i || parseInt(va[i]) > parseInt(vb[i])) {
- update = true;
- break;
- }
- }
- if (!update) {
- return;
- }
- SplashScreen.hide();
- let ok = true;
- do {
- ok = await new Promise<boolean>((resolve, reject) => {
- const actions: any[] = [
- {
- text: '立即更新',
- onPress: async () => {
- if (type === 'url' || type === 'apk') {
- if (pkg) {
- if (await Linking.canOpenURL(pkg)) {
- Linking.openURL(pkg);
- resolve(true);
- return;
- }
- }
- Modal.alert('无法自动更新', "请前往应用商店更新");
- resolve(false);
- return;
- }
- // todo other
- resolve(false);
- }
- }
- ];
- if (!force) {
- actions.unshift({
- text: '否',
- style: { color: Colors.textSecondary },
- onPress: () => {
- resolve(true);
- }
- });
- }
- Modal.alert(`${version}可更新`, force ? '请更新后继续使用' : '是否立即更新?', actions);
- });
- } while (!ok);
- } catch (e) {
- console.warn(e);
- }
- }
- (async () => {
- if (__DEV__) {
- setInitlizing(false);
- SplashScreen.hide();
- return;
- }
- await checkApp();
- SplashScreen.hide();
- while (true) {
- let res = await new Promise<string | void>(async (resolve, reject) => {
- const res = await updates.checkForUpdateAsync();
- if (res.isAvailable) {
- Modal.alert("发现热更新", "需要立即下载", [
- {
- text: '确认',
- onPress: async () => {
- const l = Toast.loading("正在下载更新 ...");
- try {
- await updates.fetchUpdateAsync();
- Toast.remove(l);
- await updates.reloadAsync();
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
- } catch (e) {
- Toast.remove(l);
- Modal.alert("提示", "更新遇到问题", [{
- text: '确认',
- onPress: resolve,
- }], () => { resolve(); return true });
- }
- }
- }
- ], () => false);
- } else {
- resolve('updateok');
- }
- });
- if (res === 'updateok') {
- break;
- }
- }
- setInitlizing(false);
- })();
- }, []);
- return (
- <ThemeProvider value={DefaultTheme}>
- <Provider theme={antdTheme}>
- {(!initializing && fontsLoaded) &&
- <AuthProvider>
- <AnimatedSplashOverlay />
- <Stack screenOptions={{
- headerShown: true,
- headerTransparent: true,
- headerBackButtonDisplayMode: 'minimal',
- headerBackground: () => (
- <BlurView intensity={80} tint="light" style={{ flex: 1 }} />
- ),
- // headerBackground: Platform.OS === 'android' ? () => <View style={{ flex: 1, backgroundColor: 'rgba(255, 255, 255, 0.9)' }} /> : undefined,
- // headerLeft: ({ canGoBack }) => canGoBack && <Icon name="arrow-left" size={24} />,
- animation: 'ios_from_right'
- }}>
- <Stack.Screen name="(tabs)" options={{ headerShown: false, }} />
- <Stack.Screen name="sign-in" options={{ headerShown: false, animation: 'fade_from_bottom' }} />
- <Stack.Screen name="sign-up" options={{ headerShown: false, animation: 'ios_from_right' }} />
- </Stack>
- </AuthProvider>}
- </Provider>
- {(initializing || !fontsLoaded) && <View className='absolute left-0 top-0 right-0 bottom-0 bg-[#f6f6f6] z-50'>
- <Image contentFit='contain' style={{ flex: 1 }} source={require('@/assets/images/uploading.jpg')} />
- </View>}
- </ThemeProvider>
- );
- }
|