|
|
@@ -1,18 +1,27 @@
|
|
|
+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 { Icon, Modal, Provider, Toast } from '@ant-design/react-native';
|
|
|
import {
|
|
|
DefaultTheme as ReactNavigationDefaultTheme,
|
|
|
Theme,
|
|
|
ThemeProvider,
|
|
|
} from '@react-navigation/native';
|
|
|
-import * as updates from 'expo-updates';
|
|
|
-
|
|
|
-import { AnimatedSplashOverlay } from '@/components/animated-icon';
|
|
|
-import { antdTheme } from '@/constants/antd-theme';
|
|
|
-import { AuthProvider } from '@/utils/auth';
|
|
|
-import { Icon, Modal, Provider, Toast } from '@ant-design/react-native';
|
|
|
+import * as application from 'expo-application';
|
|
|
import { useFonts } from 'expo-font';
|
|
|
+import { Image } from 'expo-image';
|
|
|
import { Stack, useRouter } from 'expo-router';
|
|
|
+import * as SplashScreen from 'expo-splash-screen';
|
|
|
+import * as updates from 'expo-updates';
|
|
|
import { useEffect, useState } from 'react';
|
|
|
+import { Linking, Platform } from 'react-native';
|
|
|
+
|
|
|
+SplashScreen.preventAutoHideAsync();
|
|
|
+
|
|
|
// import * as app from 'expo-'
|
|
|
export const DefaultTheme: Theme = {
|
|
|
...ReactNavigationDefaultTheme,
|
|
|
@@ -27,52 +36,131 @@ export const DefaultTheme: Theme = {
|
|
|
},
|
|
|
};
|
|
|
|
|
|
+
|
|
|
export default function RootLayout() {
|
|
|
const [initing, setIniting] = useState(true);
|
|
|
|
|
|
-
|
|
|
+
|
|
|
// const colorScheme = useColorScheme();
|
|
|
const [fontsLoaded] = useFonts({
|
|
|
antoutline: require('@ant-design/icons-react-native/fonts/antoutline.ttf'),
|
|
|
});
|
|
|
+
|
|
|
useEffect(() => {
|
|
|
-
|
|
|
- (async ()=> {
|
|
|
- // try {
|
|
|
- // const appVersion = JSON.parse(getGlobalStorage().getString('app_version') || 'null')
|
|
|
- // if (!appVersion && appVersion.version && appVersion.version != expo)
|
|
|
- // const appVersion = api.get('common/check_version')
|
|
|
- // }
|
|
|
- try {
|
|
|
+ const now = Date.now() / 1000;
|
|
|
|
|
|
- const res = await updates.checkForUpdateAsync();
|
|
|
- // alert(JSON.stringify(res))
|
|
|
- if (res.isAvailable) {
|
|
|
-
|
|
|
- Modal.alert("提示", "发现新版本", [
|
|
|
- {
|
|
|
- text: '确认',
|
|
|
- onPress: async ()=> {
|
|
|
- const l = Toast.loading("正在更新 ...");
|
|
|
- try {
|
|
|
- await updates.fetchUpdateAsync();
|
|
|
- await updates.reloadAsync();
|
|
|
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
- } catch(e) {
|
|
|
- Modal.alert("提示", "更新遇到问题" );
|
|
|
+ async function checkApp() {
|
|
|
+ try {
|
|
|
+ const lastTime = parseInt(getGlobalStorage().getString('last_update_app') || '0');
|
|
|
+ if (now - lastTime < 900000) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+SplashScreen.hide();
|
|
|
+ // setIniting(false);
|
|
|
+ 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: 3000});
|
|
|
+ getGlobalStorage().set('last_update_app', now);
|
|
|
+ if (version === application.nativeApplicationVersion) {
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+ 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 true;
|
|
|
+ }
|
|
|
+ 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(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ Modal.alert('无法自动更新', "请前往应用商店更新");
|
|
|
+ resolve(true);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ resolve(true);
|
|
|
+ }
|
|
|
}
|
|
|
- Toast.remove(l);
|
|
|
+ ];
|
|
|
+ if (!force) {
|
|
|
+ actions.unshift({
|
|
|
+ text: '否',
|
|
|
+ style: { color: Colors.textSecondary },
|
|
|
+ onPress: () => {
|
|
|
+ resolve(true);
|
|
|
+ }
|
|
|
+ });
|
|
|
}
|
|
|
- }
|
|
|
- ], ()=>false);
|
|
|
+ Modal.alert(`${version}可更新`, force ? '请更新后继续使用' : '是否立即更新?', actions);
|
|
|
+ });
|
|
|
+ } while (!ok);
|
|
|
+ return true;
|
|
|
+ } catch (e) {
|
|
|
+ console.warn(e);
|
|
|
+ return true;
|
|
|
}
|
|
|
-
|
|
|
- // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
- } catch(e) {
|
|
|
+ }
|
|
|
+
|
|
|
+ (async () => {
|
|
|
+ if (!await checkApp()) {
|
|
|
+ SplashScreen.hide();
|
|
|
+ setIniting(false);
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+
|
|
|
+ const res = await updates.checkForUpdateAsync();
|
|
|
+ // alert(JSON.stringify(res))
|
|
|
+ if (res.isAvailable) {
|
|
|
+
|
|
|
+ Modal.alert("发现热更新", "需要立即下载", [
|
|
|
+ {
|
|
|
+ text: '确认',
|
|
|
+ onPress: async () => {
|
|
|
+ const l = Toast.loading("正在下载更新 ...");
|
|
|
+ try {
|
|
|
+ await updates.fetchUpdateAsync();
|
|
|
+ await updates.reloadAsync();
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
+ } catch (e) {
|
|
|
+ Modal.alert("提示", "更新遇到问题");
|
|
|
+ }
|
|
|
+ Toast.remove(l);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ ], () => false);
|
|
|
+ }
|
|
|
+
|
|
|
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
|
+ } catch (e) {
|
|
|
// alert(e?.message || e+"");
|
|
|
}
|
|
|
setIniting(false);
|
|
|
-
|
|
|
+
|
|
|
})();
|
|
|
|
|
|
}, []);
|
|
|
@@ -85,15 +173,21 @@ export default function RootLayout() {
|
|
|
<ThemeProvider value={DefaultTheme}>
|
|
|
<Provider theme={antdTheme}>
|
|
|
{(!initing && fontsLoaded) &&
|
|
|
- <AuthProvider>
|
|
|
- <AnimatedSplashOverlay />
|
|
|
- <Stack screenOptions={{ headerShown: false, headerTransparent: true, headerLeft:({canGoBack})=>canGoBack&&<Icon name="arrow-left" onPress={router.back} />}}>
|
|
|
- <Stack.Screen name="(tabs)" />
|
|
|
- <Stack.Screen name="sign-in" />
|
|
|
- <Stack.Screen name="web" options={{headerShown: true}} />
|
|
|
- </Stack>
|
|
|
- </AuthProvider>}
|
|
|
+ <AuthProvider>
|
|
|
+ <AnimatedSplashOverlay />
|
|
|
+ <Stack screenOptions={{
|
|
|
+ headerShown: false,
|
|
|
+ headerTransparent: true,
|
|
|
+ headerLeft: ({canGoBack}) => canGoBack && <Icon name="arrow-left" onPress={router.back} />,
|
|
|
+ animation: 'ios_from_right'
|
|
|
+ }}>
|
|
|
+ <Stack.Screen name="(tabs)" />
|
|
|
+ <Stack.Screen name="sign-in" options={{animation: 'fade_from_bottom'}} />
|
|
|
+ <Stack.Screen name="web" options={{ headerShown: true }} />
|
|
|
+ </Stack>
|
|
|
+ </AuthProvider>}
|
|
|
</Provider>
|
|
|
+ {initing && <Image contentFit='contain' source={require('@/assets/images/uploading.jpg')} />}
|
|
|
</ThemeProvider>
|
|
|
);
|
|
|
}
|