From 3bc113bf9cb48d96d4fd4598de9052ccac15f6f2 Mon Sep 17 00:00:00 2001 From: imeepos Date: Tue, 27 Jan 2026 11:52:59 +0800 Subject: [PATCH] feat: implement app update checks and add app configuration file --- app.config.js | 121 ++++++++++++++++++++++++++++++++++++++++++++++++ app/_layout.tsx | 45 +++++++++++++++++- package.json | 6 ++- 3 files changed, 170 insertions(+), 2 deletions(-) create mode 100644 app.config.js diff --git a/app.config.js b/app.config.js new file mode 100644 index 0000000..318344e --- /dev/null +++ b/app.config.js @@ -0,0 +1,121 @@ +export const PROJECT_ID = '97a8e5b1-e6e8-45aa-ad95-f273b6c307ef' +export const APPNAME = 'popcore' +export const EXPO_PROJECT = 'popcore' +export const SCHEME = 'popcore' +export const EXPO_OWER = 'bowong' +export const ANDROID_ID = 'com.bowong.popcore' +export const IOS_ID = ANDROID_ID + +// 原生版本,原生代码变更时需要更新此版本号 +export const VERSION = '1.0.0' +// JavaScript版本,JS代码变更时需要更新此版本号 +export const APP_VERSION = 'dev202601271200' + +export default ({ config }) => { + return { + ...config, + expo: { + name: APPNAME, + slug: EXPO_PROJECT, + version: VERSION, + orientation: 'portrait', + icon: './assets/images/icon.png', + scheme: SCHEME, + userInterfaceStyle: 'automatic', + owner: EXPO_OWER, + newArchEnabled: true, + + ios: { + supportsTablet: true, + infoPlist: { + NSBluetoothPeripheralUsageDescription: + 'This app uses Bluetooth to act as a peripheral device for testing and development purposes.', + ITSAppUsesNonExemptEncryption: false, + }, + bundleIdentifier: IOS_ID, + }, + + android: { + adaptiveIcon: { + backgroundColor: '#E6F4FE', + foregroundImage: './assets/images/android-icon-foreground.png', + backgroundImage: './assets/images/android-icon-background.png', + monochromeImage: './assets/images/android-icon-monochrome.png', + }, + edgeToEdgeEnabled: true, + predictiveBackGestureEnabled: false, + permissions: [ + 'android.permission.BLUETOOTH', + 'android.permission.BLUETOOTH_ADMIN', + 'android.permission.BLUETOOTH_SCAN', + 'android.permission.BLUETOOTH_CONNECT', + 'android.permission.BLUETOOTH_ADVERTISE', + 'android.permission.ACCESS_FINE_LOCATION', + 'android.permission.READ_EXTERNAL_STORAGE', + 'android.permission.WRITE_EXTERNAL_STORAGE', + 'android.permission.READ_MEDIA_VISUAL_USER_SELECTED', + 'android.permission.ACCESS_MEDIA_LOCATION', + 'android.permission.READ_MEDIA_IMAGES', + 'android.permission.READ_MEDIA_VIDEO', + 'android.permission.READ_MEDIA_AUDIO', + 'android.permission.RECORD_AUDIO', + ], + package: ANDROID_ID, + }, + + web: { + output: 'static', + favicon: './assets/images/favicon.png', + bundler: 'metro', + }, + + plugins: [ + 'expo-router', + [ + 'react-native-ble-plx', + { + isBackgroundEnabled: true, + modes: ['peripheral', 'central'], + bluetoothAlwaysPermission: 'Allow $(PRODUCT_NAME) to connect to bluetooth devices', + }, + ], + [ + 'expo-media-library', + { + photosPermission: 'Allow $(PRODUCT_NAME) to access your photos.', + savePhotosPermission: 'Allow $(PRODUCT_NAME) to save photos.', + isAccessMediaLocationEnabled: true, + }, + ], + [ + 'expo-image-picker', + { + photosPermission: 'The app accesses your photos to let you upload your avatar or post images.', + }, + ], + ], + + experiments: { + typedRoutes: true, + reactCompiler: true, + }, + + extra: { + router: {}, + eas: { + projectId: PROJECT_ID, + }, + }, + + runtimeVersion: { + policy: 'appVersion', + }, + + updates: { + url: `https://u.expo.dev/${PROJECT_ID}`, + fallbackToCacheTimeout: 0, + checkAutomatically: 'NEVER', + }, + }, + } +} diff --git a/app/_layout.tsx b/app/_layout.tsx index e670a54..307b715 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -6,7 +6,8 @@ import { StatusBar } from 'expo-status-bar' import { useEffect } from 'react' import 'react-native-reanimated' import { GestureHandlerRootView } from 'react-native-gesture-handler' -import { View, ActivityIndicator } from 'react-native' +import { View, ActivityIndicator, AppState } from 'react-native' +import * as Updates from 'expo-updates' import { useColorScheme } from '@/hooks/use-color-scheme' import { KeyboardProvider } from 'react-native-keyboard-controller' @@ -112,6 +113,48 @@ export default function RootLayout() { function Providers({ children }: { children: React.ReactNode }) { const colorScheme = useColorScheme() + + // 热更新检查 + useEffect(() => { + // 检查更新的函数 + async function checkForUpdates() { + // 开发环境不检查更新 + if (__DEV__) return + + try { + const update = await Updates.checkForUpdateAsync() + if (update.isAvailable) { + await Updates.fetchUpdateAsync() + // 提示用户重启应用 + ;(global as any).toast?.show({ + type: 'success', + message: '发现新版本,即将重启应用', + }) + // 延迟重启,让用户看到提示 + setTimeout(() => { + Updates.reloadAsync() + }, 2000) + } + } catch (error) { + console.error('检查更新失败:', error) + } + } + + // App 启动时检查 + checkForUpdates() + + // 监听 App 状态变化,从后台返回前台时检查 + const subscription = AppState.addEventListener('change', (nextAppState) => { + if (nextAppState === 'active') { + checkForUpdates() + } + }) + + return () => { + subscription.remove() + } + }, []) + return ( diff --git a/package.json b/package.json index 8447aaf..03f8a58 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,11 @@ "lint": "expo lint", "test": "./node_modules/.bin/jest", "test:watch": "./node_modules/.bin/jest --watch", - "test:coverage": "./node_modules/.bin/jest --coverage" + "test:coverage": "./node_modules/.bin/jest --coverage", + "update:dev": "eas update --branch development --message", + "update:preview": "eas update --branch preview --message", + "update:production": "eas update --branch production --message", + "update:auto": "eas update --auto" }, "dependencies": { "@better-auth/expo": "1.3.34",