diff --git a/app.json b/app.json index 5aaca0c..b256186 100644 --- a/app.json +++ b/app.json @@ -2,7 +2,7 @@ "expo": { "name": "威友", "slug": "qojo", - "version": "0.0.2", + "version": "1.0.0", "orientation": "default", "icon": "./assets/icon.png", "userInterfaceStyle": "automatic", diff --git a/app/(app)/_layout.tsx b/app/(app)/_layout.tsx index 45da32a..09b6e0a 100644 --- a/app/(app)/_layout.tsx +++ b/app/(app)/_layout.tsx @@ -1,24 +1,54 @@ -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; +import { ActivityIndicator, View } from 'react-native'; import { Redirect } from 'expo-router'; import { AppRouteStack } from '../../src/app-navigation/AppRouteStack'; import { messageManager, useAuthStore } from '../../src/stores'; import { useRegisterPushDevice } from '../../src/hooks'; import { hrefAuthLogin } from '../../src/navigation/hrefs'; +import { useAppColors } from '../../src/theme'; export default function AppLayout() { const isAuthenticated = useAuthStore((s) => s.isAuthenticated); + const fetchCurrentUser = useAuthStore((s) => s.fetchCurrentUser); const userID = useAuthStore((s) => s.currentUser?.id); + const colors = useAppColors(); + const [verified, setVerified] = useState(false); + + useEffect(() => { + // 冷启动 token 校验:仅鉴权分组 (app) 需要。 + // 公开页(/privacy、/terms)和 (auth) 分组在根布局直接渲染,不受此校验影响, + // 因此冷启动 401 不会把公开页顶到 /login。 + fetchCurrentUser().finally(() => setVerified(true)); + }, [fetchCurrentUser]); + + useRegisterPushDevice(isAuthenticated, userID); useEffect(() => { messageManager.initialize(); }, []); - useRegisterPushDevice(isAuthenticated, userID); - - if (!isAuthenticated) { - return ; + // 持久化状态显示已登录则立即渲染(后台静默校验) + if (isAuthenticated) { + return ; } - return ; + // 未登录且校验未完成,显示 loading + if (!verified) { + return ( + + + + ); + } + + // 校验完成仍未登录,跳转登录页 + return ; } diff --git a/app/(app)/_layout.web.tsx b/app/(app)/_layout.web.tsx index 93122a5..65bfb88 100644 --- a/app/(app)/_layout.web.tsx +++ b/app/(app)/_layout.web.tsx @@ -1,5 +1,5 @@ -import { useEffect } from 'react'; -import { useWindowDimensions } from 'react-native'; +import { useEffect, useState } from 'react'; +import { ActivityIndicator, View, useWindowDimensions } from 'react-native'; import { Redirect } from 'expo-router'; import { AppDesktopShell } from '../../src/app-navigation/AppDesktopShell'; @@ -7,23 +7,50 @@ import { AppRouteStack } from '../../src/app-navigation/AppRouteStack'; import { BREAKPOINTS } from '../../src/hooks'; import { messageManager, useAuthStore } from '../../src/stores'; import { hrefAuthLogin } from '../../src/navigation/hrefs'; +import { useAppColors } from '../../src/theme'; export default function AppLayoutWeb() { const { width } = useWindowDimensions(); const isAuthenticated = useAuthStore((s) => s.isAuthenticated); + const fetchCurrentUser = useAuthStore((s) => s.fetchCurrentUser); const useDesktopShell = width >= BREAKPOINTS.desktop; + const colors = useAppColors(); + const [verified, setVerified] = useState(false); + + useEffect(() => { + // 冷启动 token 校验:仅鉴权分组 (app) 需要。 + // 公开页(/privacy、/terms)和 (auth) 分组在根布局直接渲染,不受此校验影响。 + fetchCurrentUser().finally(() => setVerified(true)); + }, [fetchCurrentUser]); useEffect(() => { messageManager.initialize(); }, []); - if (!isAuthenticated) { - return ; + // 持久化状态显示已登录则立即渲染(后台静默校验) + if (isAuthenticated) { + if (useDesktopShell) { + return ; + } + return ; } - if (useDesktopShell) { - return ; + // 未登录且校验未完成,显示 loading + if (!verified) { + return ( + + + + ); } - return ; + // 校验完成仍未登录,跳转登录页 + return ; } diff --git a/app/_layout.tsx b/app/_layout.tsx index a37a86e..8069ae5 100644 --- a/app/_layout.tsx +++ b/app/_layout.tsx @@ -1,5 +1,5 @@ import '../src/polyfills'; -import React, { useEffect, useRef, useState } from 'react'; +import React, { useEffect, useRef } from 'react'; import { AppState, AppStateStatus, Platform, View, ActivityIndicator } from 'react-native'; import { Stack, useRouter } from 'expo-router'; import { StatusBar } from 'expo-status-bar'; @@ -11,8 +11,6 @@ import * as SystemUI from 'expo-system-ui'; import { useFonts } from 'expo-font'; import { registerNotificationPresentationHandler } from '@/services/notification'; -import { api } from '@/services/core'; -import { wsService } from '@/services/core'; import { EventSubscriber } from '../src/infrastructure/EventSubscriber'; import { ThemeBootstrap, @@ -24,7 +22,6 @@ import { import AppPromptBar from '../src/components/common/AppPromptBar'; import AppDialogHost from '../src/components/common/AppDialogHost'; import { installAlertOverride } from '@/services/ui'; -import { useAuthStore } from '../src/stores'; import { checkForAPKUpdate } from '@/services/platform'; import { CallScreen, IncomingCallModal, FloatingCallWindow } from '../src/components/call'; import { jpushService } from '@/services/notification/jpushService'; @@ -104,36 +101,6 @@ function SystemChrome() { return null; } -function SessionGate({ children }: { children: React.ReactNode }) { - const isAuthenticated = useAuthStore((s) => s.isAuthenticated); - const fetchCurrentUser = useAuthStore((s) => s.fetchCurrentUser); - const colors = useAppColors(); - const [verified, setVerified] = useState(false); - - useEffect(() => { - fetchCurrentUser().finally(() => setVerified(true)); - }, [fetchCurrentUser]); - - // If persisted state shows authenticated, render immediately (background verify) - // If not authenticated and not yet verified, show loading - if (!isAuthenticated && !verified) { - return ( - - - - ); - } - // If verified and not authenticated, the Redirect in (app)/_layout.tsx handles it - return <>{children}; -} - function NotificationBootstrap() { const appState = useRef(AppState.currentState); const permissionRequested = useRef(false); @@ -280,18 +247,16 @@ function ThemedStack() { - - - - - - - - - - - - + + + + + + + + + + ); } diff --git a/package.json b/package.json index 92824d9..bcba61a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "with_you", - "version": "0.0.2", + "version": "1.0.0", "main": "expo-router/entry", "scripts": { "start": "expo start",