app.rn.tsx 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279
  1. import {
  2. PropsWithChildren,
  3. useEffect,
  4. useRef,
  5. useCallback,
  6. useState,
  7. } from 'react';
  8. import SplashScreen from 'react-native-splash-screen';
  9. import Taro from '@tarojs/taro';
  10. import './app.scss';
  11. import AsyncStorage from '@react-native-async-storage/async-storage';
  12. import useRootStore from '@/store';
  13. import { ToastProvider } from '@/contexts/ToastContext';
  14. import { View } from '@tarojs/components';
  15. import { services } from '@/models'
  16. import NetInfo from '@react-native-community/netinfo';
  17. import WindowDimensionsProvider from './hooks/useWindowDimensions';
  18. import { getDictType, handleAreaData } from './api';
  19. import UpdateModal from '@/components/UpdateModal';
  20. const App = ({ children }: PropsWithChildren) => {
  21. const spaceCheckInterval = useRef<NodeJS.Timeout | null>(null);
  22. const {
  23. UPDATA_ANTHOR_TOKEN,
  24. UPDATA_SYSTEM_INFO,
  25. UPDATA_USER_INFO,
  26. UPDATE_NETWORK_STATUS,
  27. } = useRootStore.getState();
  28. const userInfo = useRootStore(state => state.userInfo);
  29. const [isOnline, setIsOnline] = useState<boolean>(true); // 网络状态状态
  30. const networkListenerRef = useRef<any>(null); // 存储网络监听器引用
  31. // 获取系统信息
  32. const getSystemInfo = useCallback(async () => {
  33. try {
  34. const res = await Taro.getSystemInfo();
  35. console.log('getSystemInfo:', res);
  36. UPDATA_SYSTEM_INFO(res as any);
  37. } catch (error) {
  38. console.error('获取系统信息失败:', error);
  39. }
  40. }, [UPDATA_SYSTEM_INFO]);
  41. // 获取存储的token
  42. const handleGetStorage = useCallback(async (): Promise<void> => {
  43. try {
  44. // 优先从Zustand store获取token
  45. const store = useRootStore.getState();
  46. console.log('Zustand store状态:', {
  47. isStorageReady: store.isStorageReady,
  48. hasToken: !!store.token,
  49. hasUserInfo: !!store.userInfo,
  50. token: store.token,
  51. userInfo: store.userInfo
  52. });
  53. // 等待存储准备完成
  54. if (store.isStorageReady) {
  55. if (store.token) {
  56. console.log('从Zustand store获取token成功:', store.token);
  57. UPDATA_ANTHOR_TOKEN(store.token);
  58. return;
  59. }
  60. } else {
  61. // 如果store还没准备好,等待一下再重试
  62. let retryCount = 0;
  63. const maxRetries = 10;
  64. const retryInterval = 100; // 100ms
  65. const checkStorageReady = async (): Promise<void> => {
  66. while (retryCount < maxRetries && !store.isStorageReady) {
  67. await new Promise(resolve => setTimeout(resolve, retryInterval));
  68. retryCount++;
  69. }
  70. if (store.isStorageReady && store.token) {
  71. console.log('等待后从Zustand store获取token成功:', store.token);
  72. UPDATA_ANTHOR_TOKEN(store.token);
  73. return;
  74. }
  75. // 如果Zustand没有数据,尝试从Taro存储获取作为备选
  76. try {
  77. const { data } = await Taro.getStorage({ key: 'ACCESS_TOKEN' });
  78. if (data) {
  79. console.log('从Taro存储获取token成功:', data);
  80. UPDATA_ANTHOR_TOKEN(data);
  81. }
  82. } catch (fallbackError) {
  83. console.log('从Taro存储获取token也失败,跳过');
  84. }
  85. };
  86. await checkStorageReady();
  87. }
  88. } catch (error) {
  89. console.error('获取token失败:', error);
  90. // 即使失败也继续,避免阻塞启动
  91. }
  92. }, [UPDATA_ANTHOR_TOKEN]);
  93. // 存储空间检查
  94. const storageSpaceHandler = useCallback(async () => {
  95. try {
  96. const keys = await AsyncStorage.getAllKeys();
  97. console.log('Stored keys count:', keys.length);
  98. } catch (error) {
  99. console.error('存储空间检查失败:', error);
  100. }
  101. }, []);
  102. // 获取用户信息
  103. const setUserInfo = useCallback(async () => {
  104. try {
  105. // 优先从Zustand store获取用户信息
  106. const store = useRootStore.getState();
  107. // 等待存储准备完成
  108. if (store.isStorageReady) {
  109. if (store.userInfo) {
  110. console.log('从Zustand store获取用户信息成功:', store.userInfo);
  111. await services.user.createUserFromServer(store.userInfo);
  112. return;
  113. }
  114. } else {
  115. // 如果store还没准备好,等待一下再重试
  116. let retryCount = 0;
  117. const maxRetries = 10;
  118. const retryInterval = 100; // 100ms
  119. const checkStorageReady = async (): Promise<void> => {
  120. while (retryCount < maxRetries && !store.isStorageReady) {
  121. await new Promise(resolve => setTimeout(resolve, retryInterval));
  122. retryCount++;
  123. }
  124. if (store.isStorageReady && store.userInfo) {
  125. console.log('等待后从Zustand store获取用户信息成功:', store.userInfo);
  126. await services.user.createUserFromServer(store.userInfo);
  127. return;
  128. }
  129. // 如果Zustand没有数据,尝试从Taro存储获取作为备选
  130. try {
  131. const { data: user } = await Taro.getStorage({ key: 'USER_INFO' });
  132. if (user) {
  133. console.log('从Taro存储获取用户信息成功:', user);
  134. UPDATA_USER_INFO(user); // 同步到Zustand store
  135. await services.user.createUserFromServer(user);
  136. } else {
  137. console.log('未找到用户信息,可能需要重新登录');
  138. }
  139. } catch (fallbackError) {
  140. console.log('从Taro存储获取用户信息也失败,跳过');
  141. }
  142. };
  143. await checkStorageReady();
  144. }
  145. } catch (error) {
  146. console.error('获取用户信息失败:', error);
  147. console.log('用户信息获取失败是正常的,可能用户未登录或首次启动');
  148. }
  149. }, [UPDATA_USER_INFO]);
  150. // 初始化应用
  151. const initApp = useCallback(async () => {
  152. try {
  153. await Promise.all([
  154. handleGetStorage(),
  155. getSystemInfo(),
  156. setUserInfo(),
  157. ]);
  158. } catch (error) {
  159. console.error('初始化应用失败:', error);
  160. } finally {
  161. // 无论成功失败都隐藏启动页
  162. SplashScreen.hide();
  163. }
  164. }, [handleGetStorage, getSystemInfo, setUserInfo]);
  165. // 初始化网络状态监听
  166. const setupNetworkListener = useCallback(() => {
  167. try {
  168. // 首先获取当前网络状态
  169. NetInfo.fetch().then(state => {
  170. const connected = state.isConnected ?? false;
  171. setIsOnline(connected);
  172. UPDATE_NETWORK_STATUS(connected);
  173. console.log('初始化网络状态:', connected);
  174. });
  175. // 订阅网络状态变化
  176. networkListenerRef.current = NetInfo.addEventListener(state => {
  177. const connected = state.isConnected ?? false;
  178. // 只有状态真正改变时才更新和记录日志
  179. setIsOnline(prevConnected => {
  180. if (prevConnected !== connected) {
  181. UPDATE_NETWORK_STATUS(connected);
  182. console.log('网络状态改变:', connected);
  183. }
  184. return connected;
  185. });
  186. });
  187. }
  188. catch (error) {
  189. console.error('设置网络监听失败:', error);
  190. }
  191. }, [UPDATE_NETWORK_STATUS]);
  192. useEffect(() => {
  193. const initializeApp = async () => {
  194. await initApp();
  195. // 启动存储空间检查定时器
  196. spaceCheckInterval.current = setInterval(storageSpaceHandler, 60_000);
  197. // 设置网络状态监听
  198. setupNetworkListener();
  199. };
  200. initializeApp();
  201. // 清理函数
  202. return () => {
  203. if (spaceCheckInterval.current) {
  204. clearInterval(spaceCheckInterval.current);
  205. }
  206. // 移除网络监听
  207. networkListenerRef.current();
  208. };
  209. }, [initApp, storageSpaceHandler, setupNetworkListener]);
  210. useEffect(() => {
  211. if (!isOnline) {
  212. Taro.showToast({ title: '当前网络不可用', icon: 'error' });
  213. }
  214. }, [isOnline]);
  215. useEffect(() => {
  216. const fetchDictData = async () => {
  217. try {
  218. await getDictType();
  219. await handleAreaData();
  220. } catch (error) {
  221. console.error('初始化数据失败:', error);
  222. }
  223. };
  224. fetchDictData();
  225. }, [userInfo.id]);
  226. return (
  227. <ToastProvider>
  228. <View style={{ height: '100%', overflow: 'hidden' }}>
  229. <WindowDimensionsProvider>
  230. {children}
  231. </WindowDimensionsProvider>
  232. {/* 应用更新弹窗 - 自包含组件 */}
  233. {/* <UpdateModal /> */}
  234. </View>
  235. </ToastProvider>
  236. );
  237. };
  238. export default App;