feat: 更新应用版本号,优化用户数据获取逻辑,简化认证流程
This commit is contained in:
parent
43857594d2
commit
6bb78c11cd
|
|
@ -10,7 +10,7 @@ export const IOS_UNIVERSAL_LINK = 'duooomi.bowong.cn'
|
|||
// 原生版本,原生代码变更时需要更新此版本号
|
||||
export const VERSION = '1.5.0'
|
||||
// JavaScript版本,JS代码变更时需要更新此版本号
|
||||
export const APP_VERSION = 'dev202602041525'
|
||||
export const APP_VERSION = 'dev202602041658'
|
||||
|
||||
const ALIPAY_SCHEMA = 'alipay2021006119657394'
|
||||
const ALIPAY_SCHEMA_SANDBOX = 'alipay9021000158673972'
|
||||
|
|
|
|||
|
|
@ -61,10 +61,6 @@ const CustomTabBar = observer(function CustomTabBar(props: any) {
|
|||
const activeRouteName = routes[activeIndex]?.name || 'index'
|
||||
|
||||
const handleChange = (route: string) => {
|
||||
// if (!isLogin && route !== 'index') {
|
||||
// props.navigation.push('auth')
|
||||
// return
|
||||
// }
|
||||
props.navigation.navigate(route)
|
||||
}
|
||||
return <BottomNav currentRoute={activeRouteName} onRouteChange={handleChange} />
|
||||
|
|
@ -74,23 +70,11 @@ export default function Layout() {
|
|||
const router = useRouter()
|
||||
useEffect(() => {
|
||||
const timer = setTimeout(() => {
|
||||
router.replace('(tabs)/sync')
|
||||
router.replace('/(tabs)/sync')
|
||||
}, 100) // 小延迟确保导航准备好
|
||||
|
||||
return () => clearTimeout(timer)
|
||||
}, [router])
|
||||
|
||||
useEffect(() => {
|
||||
// 配置WebBrowser
|
||||
// const setupWebBrowser = async () => {
|
||||
// try {
|
||||
// await configureWebBrowser()
|
||||
// } catch (error) {
|
||||
// console.warn('Failed to configure WebBrowser:', error)
|
||||
// }
|
||||
// }
|
||||
// setupWebBrowser()
|
||||
}, [])
|
||||
|
||||
return <Tabs screenOptions={{ headerShown: false }} tabBar={(props: any) => <CustomTabBar {...props} />}></Tabs>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -68,6 +68,7 @@ const Index = observer(function Index() {
|
|||
|
||||
useEffect(() => {
|
||||
console.log('expo env------------', process.env.EXPO_PUBLIC_ENV)
|
||||
userStore.getUserData()
|
||||
}, [])
|
||||
|
||||
/** ================= refs(核心) ================= */
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
import '../global.css'
|
||||
import 'react-native-reanimated'
|
||||
|
||||
import { DarkTheme, DefaultTheme, ThemeProvider, useIsFocused } from '@react-navigation/native'
|
||||
import { DarkTheme, DefaultTheme, ThemeProvider } from '@react-navigation/native'
|
||||
import * as Sentry from '@sentry/react-native'
|
||||
import { useKeepAwake } from 'expo-keep-awake'
|
||||
import { router, Stack, useNavigationContainerRef, usePathname } from 'expo-router'
|
||||
import { Stack, useNavigationContainerRef, usePathname } from 'expo-router'
|
||||
import { ShareIntentProvider } from 'expo-share-intent'
|
||||
import { StatusBar } from 'expo-status-bar'
|
||||
import { useCallback, useEffect } from 'react'
|
||||
import { useEffect } from 'react'
|
||||
import { GestureHandlerRootView } from 'react-native-gesture-handler'
|
||||
import { KeyboardProvider } from 'react-native-keyboard-controller'
|
||||
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context'
|
||||
|
|
@ -17,8 +17,6 @@ import { bleManager } from '@/ble/managers/bleManager'
|
|||
import { HotUpdate } from '@/components/hot-update'
|
||||
import { useColorScheme } from '@/hooks/use-color-scheme'
|
||||
import { setupGlobalFetchLogger } from '@/lib/fetch-logger'
|
||||
import { useUserSession } from '@/stores/userStore'
|
||||
import { storage } from '@/utils'
|
||||
|
||||
const isProd = process.env.EXPO_PUBLIC_ENV !== 'development'
|
||||
|
||||
|
|
@ -40,6 +38,8 @@ export const unstable_settings = {
|
|||
function RootLayout() {
|
||||
const ref = useNavigationContainerRef()
|
||||
|
||||
const currentRoute = usePathname()
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref?.current) return
|
||||
|
||||
|
|
@ -92,15 +92,15 @@ function RootLayout() {
|
|||
<ShareIntentProvider>
|
||||
<Providers>
|
||||
<Stack>
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="auth" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="forgotPassword" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="pointList" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="webview" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="scan" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="settings" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="service" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="privacy" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="(tabs)" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="auth" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="forgotPassword" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="pointList" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="webview" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="scan" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="settings" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="service" options={{ headerShown: false }} />
|
||||
<Stack.Screen name="privacy" options={{ headerShown: false }} />
|
||||
</Stack>
|
||||
</Providers>
|
||||
</ShareIntentProvider>
|
||||
|
|
@ -110,24 +110,16 @@ function RootLayout() {
|
|||
function Providers({ children }: { children: React.ReactNode }) {
|
||||
const colorScheme = useColorScheme()
|
||||
const currentRoute = usePathname()
|
||||
// const session = useUserSession()
|
||||
|
||||
useUserSession()
|
||||
useKeepAwake()
|
||||
|
||||
const isFocused = useIsFocused()
|
||||
|
||||
const loadLogin = useCallback(async () => {
|
||||
const isLogin = await storage.get('isLogin')
|
||||
if (!isLogin && currentRoute !== '/auth') {
|
||||
router.replace('/auth')
|
||||
}
|
||||
}, [currentRoute])
|
||||
|
||||
useEffect(() => {
|
||||
if (isFocused) {
|
||||
loadLogin()
|
||||
}
|
||||
}, [isFocused, loadLogin])
|
||||
// useEffect(() => {
|
||||
// if (session.isPending) return
|
||||
// if (!isLoggedIn && !currentRoute.includes('/auth')) {
|
||||
// router.replace('/auth')
|
||||
// }
|
||||
// }, [session.isPending, isLoggedIn, currentRoute])
|
||||
|
||||
return (
|
||||
<SafeAreaProvider>
|
||||
|
|
|
|||
94
app/auth.tsx
94
app/auth.tsx
|
|
@ -8,7 +8,7 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
|
|||
|
||||
import { APP_VERSION, VERSION } from '@/app.config'
|
||||
import BannerSection from '@/components/BannerSection'
|
||||
import { getSession, phoneNumber, setAuthToken, signIn } from '@/lib/auth'
|
||||
import { phoneNumber, setAuthToken, signIn } from '@/lib/auth'
|
||||
import { isValidPhone } from '@/utils'
|
||||
|
||||
const APP_NAME = '多米'
|
||||
|
|
@ -33,50 +33,19 @@ export default function Auth() {
|
|||
const [currentBranch, setCurrentBranch] = useState<string>('unknown')
|
||||
const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
|
||||
|
||||
// 获取当前分支信息
|
||||
useEffect(() => {
|
||||
const getBranchInfo = async () => {
|
||||
try {
|
||||
if (!Updates.isEnabled) {
|
||||
setCurrentBranch('development')
|
||||
return
|
||||
}
|
||||
|
||||
// 方法1: 使用 Updates.manifest (同步)
|
||||
const manifest = Updates.manifest
|
||||
|
||||
// 方法2: 或者尝试从更新中获取
|
||||
let branch = 'production' // 默认值
|
||||
|
||||
if (manifest?.extra?.eas?.branch) {
|
||||
branch = manifest.extra.eas.branch
|
||||
} else if (manifest?.metadata?.branchName) {
|
||||
branch = manifest.metadata.branchName
|
||||
} else if (Updates.channel) {
|
||||
branch = Updates.channel
|
||||
}
|
||||
|
||||
// 方法3: 尝试获取当前运行的更新信息
|
||||
try {
|
||||
const currentlyRunning = await Updates.fetchUpdateAsync()
|
||||
if (currentlyRunning.manifest?.extra?.eas?.branch) {
|
||||
branch = currentlyRunning.manifest.extra.eas.branch
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('Failed to fetch current update:', e)
|
||||
}
|
||||
|
||||
setCurrentBranch(branch)
|
||||
console.log('Current branch:', branch)
|
||||
console.log('Updates.channel:', Updates.channel)
|
||||
console.log('Manifest:', manifest)
|
||||
} catch (error) {
|
||||
console.warn('Failed to get branch info:', error)
|
||||
setCurrentBranch('unknown')
|
||||
}
|
||||
if (!Updates.isEnabled) {
|
||||
setCurrentBranch('development')
|
||||
return
|
||||
}
|
||||
const manifest = Updates.manifest
|
||||
if (manifest?.extra?.eas?.branch) {
|
||||
setCurrentBranch(manifest.extra.eas.branch)
|
||||
} else if (Updates.channel) {
|
||||
setCurrentBranch(Updates.channel)
|
||||
} else {
|
||||
setCurrentBranch('production')
|
||||
}
|
||||
|
||||
getBranchInfo()
|
||||
}, [])
|
||||
|
||||
const canSendCode = useMemo(() => {
|
||||
|
|
@ -174,29 +143,24 @@ export default function Auth() {
|
|||
setLoading(true)
|
||||
Toast.showLoading({ title: '正在验证...' })
|
||||
try {
|
||||
// 验证验证码,如果后端配置了 signUpOnVerification,新用户会自动注册并创建 session
|
||||
// disableSession: false 表示验证成功后创建 session(自动登录)
|
||||
await new Promise<void>((resolve, reject) => {
|
||||
phoneNumber.verify(
|
||||
{ phoneNumber: phone, code: code, disableSession: false },
|
||||
{
|
||||
onSuccess: async (ctx: any) => {
|
||||
const authToken = ctx.response.headers.get('set-auth-token')
|
||||
if (authToken) {
|
||||
setAuthToken(authToken)
|
||||
}
|
||||
await getSession()
|
||||
Toast.show({ title: '登录成功!' })
|
||||
setTimeout(() => {
|
||||
router.replace('/(tabs)')
|
||||
}, 500)
|
||||
},
|
||||
onError: (ctx: any) => {
|
||||
reject(new Error(ctx.error.message))
|
||||
},
|
||||
const result = await phoneNumber.verify(
|
||||
{ phoneNumber: phone, code: code, disableSession: false },
|
||||
{
|
||||
onSuccess: async (ctx: any) => {
|
||||
const authToken = ctx.response.headers.get('set-auth-token')
|
||||
if (authToken) {
|
||||
setAuthToken(authToken)
|
||||
}
|
||||
},
|
||||
)
|
||||
})
|
||||
},
|
||||
)
|
||||
|
||||
if (result.error) {
|
||||
Toast.show({ title: result.error.message || '验证失败' })
|
||||
} else {
|
||||
Toast.show({ title: '登录成功!' })
|
||||
router.replace('/(tabs)')
|
||||
}
|
||||
} catch (error: any) {
|
||||
console.error('登录/注册失败:', error)
|
||||
Toast.show({ title: error.message || '操作失败,请稍后重试' })
|
||||
|
|
|
|||
|
|
@ -85,6 +85,9 @@ export const authClient = createAuthClient({
|
|||
trustedOrigins: ['duooomi://', 'https://api.mixvideo.bowong.cc'],
|
||||
storage,
|
||||
scheme: 'duooomi',
|
||||
sessionOptions: {
|
||||
refetchOnWindowFocus: false,
|
||||
},
|
||||
fetchOptions: {
|
||||
headers: {
|
||||
'x-ownerid': OWNER_ID,
|
||||
|
|
|
|||
|
|
@ -1,6 +1,3 @@
|
|||
import { storage as storage2 } from '../utils/storage'
|
||||
import { storage } from './storage.native'
|
||||
|
||||
interface FetchLoggerOptions {
|
||||
enableLogging?: boolean
|
||||
logRequest?: boolean
|
||||
|
|
@ -26,26 +23,7 @@ export const createFetchWithLogger = (options: FetchLoggerOptions = {}) => {
|
|||
const method = init?.method || 'GET'
|
||||
|
||||
try {
|
||||
const token = await storage.getItem('token')
|
||||
|
||||
if (token) {
|
||||
init = {
|
||||
...init,
|
||||
headers: {
|
||||
...init?.headers,
|
||||
authorization: `Bearer ${token}`,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
const response = await originalFetch(input, init)
|
||||
|
||||
if (response.status === 401) {
|
||||
console.warn('🔐 401 未授权,清空登录状态')
|
||||
// router.replace('/auth')
|
||||
storage2.set('isLogin', false)
|
||||
}
|
||||
|
||||
return response
|
||||
} catch (error) {
|
||||
const duration = Date.now() - startTime
|
||||
|
|
|
|||
|
|
@ -1,15 +1,13 @@
|
|||
import { root } from '@repo/core'
|
||||
import { DeviceController } from '@repo/sdk'
|
||||
import * as Application from 'expo-application'
|
||||
import { router } from 'expo-router'
|
||||
import { makeAutoObservable, runInAction } from 'mobx'
|
||||
import { useEffect } from 'react'
|
||||
import { Platform } from 'react-native'
|
||||
|
||||
import { signOut as authSignOut, useSession } from '@/lib/auth'
|
||||
import { getSession, signOut as authSignOut } from '@/lib/auth'
|
||||
import { storage } from '@/utils'
|
||||
|
||||
// console.log('useSession---------------', useSession)
|
||||
|
||||
interface User {
|
||||
id: string
|
||||
email: string
|
||||
|
|
@ -33,17 +31,17 @@ interface User {
|
|||
interface Session {
|
||||
user?: User
|
||||
session?: {
|
||||
id: string
|
||||
userId: string
|
||||
expiresAt: Date
|
||||
token: string
|
||||
createdAt: Date
|
||||
updatedAt: Date
|
||||
ipAddress?: string | null
|
||||
userAgent?: string | null
|
||||
userId: string
|
||||
impersonatedBy?: string | null
|
||||
activeOrganizationId?: string | null
|
||||
activeTeamId?: string | null
|
||||
impersonatedBy?: string | null
|
||||
ipAddress?: string | null
|
||||
token?: string | null
|
||||
userAgent?: string | null
|
||||
id: string
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -77,18 +75,31 @@ class UserStore {
|
|||
await storage.set('isLogin', !!user)
|
||||
}
|
||||
|
||||
// 设置会话信息
|
||||
async setSession(session: Session | null) {
|
||||
runInAction(() => {
|
||||
this.session = session
|
||||
this.user = session?.user || null
|
||||
this.isLogin = !!session?.user
|
||||
async getUserData() {
|
||||
const sessionRes = await getSession()
|
||||
|
||||
if (session?.user) {
|
||||
this.bindDevice()
|
||||
}
|
||||
// console.log('getUserData sessionRes--------:', JSON.stringify(sessionRes))
|
||||
|
||||
const data = sessionRes.data
|
||||
if (!data) {
|
||||
this.reset()
|
||||
router.replace('/auth')
|
||||
return
|
||||
}
|
||||
|
||||
const { session, user } = data
|
||||
|
||||
// console.log('获取用户信息--------:', JSON.stringify(data))
|
||||
|
||||
runInAction(() => {
|
||||
this.session = { session, user }
|
||||
this.user = user || null
|
||||
this.isLogin = !!user
|
||||
})
|
||||
await storage.set('isLogin', !!session?.user)
|
||||
|
||||
if (user?.id) {
|
||||
this.bindDevice()
|
||||
}
|
||||
}
|
||||
|
||||
// 设置加载状态
|
||||
|
|
@ -118,18 +129,13 @@ class UserStore {
|
|||
}
|
||||
}
|
||||
|
||||
// 从session更新用户状态
|
||||
updateFromSession(sessionData: Session | null) {
|
||||
this.setSession(sessionData)
|
||||
}
|
||||
|
||||
// 重置store状态
|
||||
reset() {
|
||||
this.user = null
|
||||
this.session = null
|
||||
this.isLoading = false
|
||||
this.error = null
|
||||
this.scannedQR = null
|
||||
this.setUser(null)
|
||||
}
|
||||
|
||||
// ==================== 二维码相关方法 ====================
|
||||
|
|
@ -180,16 +186,3 @@ class UserStore {
|
|||
|
||||
// 创建单例实例
|
||||
export const userStore = new UserStore()
|
||||
|
||||
// 创建一个hook来使用session数据更新store
|
||||
export const useUserSession = () => {
|
||||
const session = useSession()
|
||||
|
||||
// 使用useEffect在副作用中更新store状态,避免在render过程中修改
|
||||
useEffect(() => {
|
||||
userStore.setLoading(session.isPending)
|
||||
userStore.updateFromSession(session.data)
|
||||
}, [session.isPending, session.data])
|
||||
|
||||
return session
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue