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((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(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 ( {(!initializing && fontsLoaded) && ( ), // headerBackground: Platform.OS === 'android' ? () => : undefined, // headerLeft: ({ canGoBack }) => canGoBack && , animation: 'ios_from_right' }}> } {(initializing || !fontsLoaded) && } ); }