feat: 更新应用版本号,优化用户数据获取逻辑,简化认证流程

This commit is contained in:
康猛 2026-02-04 18:38:11 +08:00
parent 43857594d2
commit 6bb78c11cd
8 changed files with 88 additions and 173 deletions

View File

@ -10,7 +10,7 @@ export const IOS_UNIVERSAL_LINK = 'duooomi.bowong.cn'
// 原生版本,原生代码变更时需要更新此版本号 // 原生版本,原生代码变更时需要更新此版本号
export const VERSION = '1.5.0' export const VERSION = '1.5.0'
// JavaScript版本JS代码变更时需要更新此版本号 // JavaScript版本JS代码变更时需要更新此版本号
export const APP_VERSION = 'dev202602041525' export const APP_VERSION = 'dev202602041658'
const ALIPAY_SCHEMA = 'alipay2021006119657394' const ALIPAY_SCHEMA = 'alipay2021006119657394'
const ALIPAY_SCHEMA_SANDBOX = 'alipay9021000158673972' const ALIPAY_SCHEMA_SANDBOX = 'alipay9021000158673972'

View File

@ -61,10 +61,6 @@ const CustomTabBar = observer(function CustomTabBar(props: any) {
const activeRouteName = routes[activeIndex]?.name || 'index' const activeRouteName = routes[activeIndex]?.name || 'index'
const handleChange = (route: string) => { const handleChange = (route: string) => {
// if (!isLogin && route !== 'index') {
// props.navigation.push('auth')
// return
// }
props.navigation.navigate(route) props.navigation.navigate(route)
} }
return <BottomNav currentRoute={activeRouteName} onRouteChange={handleChange} /> return <BottomNav currentRoute={activeRouteName} onRouteChange={handleChange} />
@ -74,23 +70,11 @@ export default function Layout() {
const router = useRouter() const router = useRouter()
useEffect(() => { useEffect(() => {
const timer = setTimeout(() => { const timer = setTimeout(() => {
router.replace('(tabs)/sync') router.replace('/(tabs)/sync')
}, 100) // 小延迟确保导航准备好 }, 100) // 小延迟确保导航准备好
return () => clearTimeout(timer) return () => clearTimeout(timer)
}, [router]) }, [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> return <Tabs screenOptions={{ headerShown: false }} tabBar={(props: any) => <CustomTabBar {...props} />}></Tabs>
} }

View File

@ -68,6 +68,7 @@ const Index = observer(function Index() {
useEffect(() => { useEffect(() => {
console.log('expo env------------', process.env.EXPO_PUBLIC_ENV) console.log('expo env------------', process.env.EXPO_PUBLIC_ENV)
userStore.getUserData()
}, []) }, [])
/** ================= refs核心 ================= */ /** ================= refs核心 ================= */

View File

@ -1,13 +1,13 @@
import '../global.css' import '../global.css'
import 'react-native-reanimated' 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 * as Sentry from '@sentry/react-native'
import { useKeepAwake } from 'expo-keep-awake' 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 { ShareIntentProvider } from 'expo-share-intent'
import { StatusBar } from 'expo-status-bar' import { StatusBar } from 'expo-status-bar'
import { useCallback, useEffect } from 'react' import { useEffect } from 'react'
import { GestureHandlerRootView } from 'react-native-gesture-handler' import { GestureHandlerRootView } from 'react-native-gesture-handler'
import { KeyboardProvider } from 'react-native-keyboard-controller' import { KeyboardProvider } from 'react-native-keyboard-controller'
import { SafeAreaProvider, SafeAreaView } from 'react-native-safe-area-context' 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 { HotUpdate } from '@/components/hot-update'
import { useColorScheme } from '@/hooks/use-color-scheme' import { useColorScheme } from '@/hooks/use-color-scheme'
import { setupGlobalFetchLogger } from '@/lib/fetch-logger' import { setupGlobalFetchLogger } from '@/lib/fetch-logger'
import { useUserSession } from '@/stores/userStore'
import { storage } from '@/utils'
const isProd = process.env.EXPO_PUBLIC_ENV !== 'development' const isProd = process.env.EXPO_PUBLIC_ENV !== 'development'
@ -40,6 +38,8 @@ export const unstable_settings = {
function RootLayout() { function RootLayout() {
const ref = useNavigationContainerRef() const ref = useNavigationContainerRef()
const currentRoute = usePathname()
useEffect(() => { useEffect(() => {
if (!ref?.current) return if (!ref?.current) return
@ -92,15 +92,15 @@ function RootLayout() {
<ShareIntentProvider> <ShareIntentProvider>
<Providers> <Providers>
<Stack> <Stack>
<Stack.Screen name="(tabs)" options={{ headerShown: false }} /> <Stack.Screen name="(tabs)" options={{ headerShown: false }} />
<Stack.Screen name="auth" options={{ headerShown: false }} /> <Stack.Screen name="auth" options={{ headerShown: false }} />
<Stack.Screen name="forgotPassword" options={{ headerShown: false }} /> <Stack.Screen name="forgotPassword" options={{ headerShown: false }} />
<Stack.Screen name="pointList" options={{ headerShown: false }} /> <Stack.Screen name="pointList" options={{ headerShown: false }} />
<Stack.Screen name="webview" options={{ headerShown: false }} /> <Stack.Screen name="webview" options={{ headerShown: false }} />
<Stack.Screen name="scan" options={{ headerShown: false }} /> <Stack.Screen name="scan" options={{ headerShown: false }} />
<Stack.Screen name="settings" options={{ headerShown: false }} /> <Stack.Screen name="settings" options={{ headerShown: false }} />
<Stack.Screen name="service" options={{ headerShown: false }} /> <Stack.Screen name="service" options={{ headerShown: false }} />
<Stack.Screen name="privacy" options={{ headerShown: false }} /> <Stack.Screen name="privacy" options={{ headerShown: false }} />
</Stack> </Stack>
</Providers> </Providers>
</ShareIntentProvider> </ShareIntentProvider>
@ -110,24 +110,16 @@ function RootLayout() {
function Providers({ children }: { children: React.ReactNode }) { function Providers({ children }: { children: React.ReactNode }) {
const colorScheme = useColorScheme() const colorScheme = useColorScheme()
const currentRoute = usePathname() const currentRoute = usePathname()
// const session = useUserSession()
useUserSession()
useKeepAwake() useKeepAwake()
const isFocused = useIsFocused() // useEffect(() => {
// if (session.isPending) return
const loadLogin = useCallback(async () => { // if (!isLoggedIn && !currentRoute.includes('/auth')) {
const isLogin = await storage.get('isLogin') // router.replace('/auth')
if (!isLogin && currentRoute !== '/auth') { // }
router.replace('/auth') // }, [session.isPending, isLoggedIn, currentRoute])
}
}, [currentRoute])
useEffect(() => {
if (isFocused) {
loadLogin()
}
}, [isFocused, loadLogin])
return ( return (
<SafeAreaProvider> <SafeAreaProvider>

View File

@ -8,7 +8,7 @@ import { KeyboardAwareScrollView } from 'react-native-keyboard-controller'
import { APP_VERSION, VERSION } from '@/app.config' import { APP_VERSION, VERSION } from '@/app.config'
import BannerSection from '@/components/BannerSection' import BannerSection from '@/components/BannerSection'
import { getSession, phoneNumber, setAuthToken, signIn } from '@/lib/auth' import { phoneNumber, setAuthToken, signIn } from '@/lib/auth'
import { isValidPhone } from '@/utils' import { isValidPhone } from '@/utils'
const APP_NAME = '多米' const APP_NAME = '多米'
@ -33,50 +33,19 @@ export default function Auth() {
const [currentBranch, setCurrentBranch] = useState<string>('unknown') const [currentBranch, setCurrentBranch] = useState<string>('unknown')
const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null) const countdownTimerRef = useRef<ReturnType<typeof setInterval> | null>(null)
// 获取当前分支信息
useEffect(() => { useEffect(() => {
const getBranchInfo = async () => { if (!Updates.isEnabled) {
try { setCurrentBranch('development')
if (!Updates.isEnabled) { return
setCurrentBranch('development') }
return const manifest = Updates.manifest
} if (manifest?.extra?.eas?.branch) {
setCurrentBranch(manifest.extra.eas.branch)
// 方法1: 使用 Updates.manifest (同步) } else if (Updates.channel) {
const manifest = Updates.manifest setCurrentBranch(Updates.channel)
} else {
// 方法2: 或者尝试从更新中获取 setCurrentBranch('production')
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')
}
} }
getBranchInfo()
}, []) }, [])
const canSendCode = useMemo(() => { const canSendCode = useMemo(() => {
@ -174,29 +143,24 @@ export default function Auth() {
setLoading(true) setLoading(true)
Toast.showLoading({ title: '正在验证...' }) Toast.showLoading({ title: '正在验证...' })
try { try {
// 验证验证码,如果后端配置了 signUpOnVerification新用户会自动注册并创建 session const result = await phoneNumber.verify(
// disableSession: false 表示验证成功后创建 session自动登录 { phoneNumber: phone, code: code, disableSession: false },
await new Promise<void>((resolve, reject) => { {
phoneNumber.verify( onSuccess: async (ctx: any) => {
{ phoneNumber: phone, code: code, disableSession: false }, const authToken = ctx.response.headers.get('set-auth-token')
{ if (authToken) {
onSuccess: async (ctx: any) => { setAuthToken(authToken)
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))
},
}, },
) },
}) )
if (result.error) {
Toast.show({ title: result.error.message || '验证失败' })
} else {
Toast.show({ title: '登录成功!' })
router.replace('/(tabs)')
}
} catch (error: any) { } catch (error: any) {
console.error('登录/注册失败:', error) console.error('登录/注册失败:', error)
Toast.show({ title: error.message || '操作失败,请稍后重试' }) Toast.show({ title: error.message || '操作失败,请稍后重试' })

View File

@ -85,6 +85,9 @@ export const authClient = createAuthClient({
trustedOrigins: ['duooomi://', 'https://api.mixvideo.bowong.cc'], trustedOrigins: ['duooomi://', 'https://api.mixvideo.bowong.cc'],
storage, storage,
scheme: 'duooomi', scheme: 'duooomi',
sessionOptions: {
refetchOnWindowFocus: false,
},
fetchOptions: { fetchOptions: {
headers: { headers: {
'x-ownerid': OWNER_ID, 'x-ownerid': OWNER_ID,

View File

@ -1,6 +1,3 @@
import { storage as storage2 } from '../utils/storage'
import { storage } from './storage.native'
interface FetchLoggerOptions { interface FetchLoggerOptions {
enableLogging?: boolean enableLogging?: boolean
logRequest?: boolean logRequest?: boolean
@ -26,26 +23,7 @@ export const createFetchWithLogger = (options: FetchLoggerOptions = {}) => {
const method = init?.method || 'GET' const method = init?.method || 'GET'
try { try {
const token = await storage.getItem('token')
if (token) {
init = {
...init,
headers: {
...init?.headers,
authorization: `Bearer ${token}`,
},
}
}
const response = await originalFetch(input, init) const response = await originalFetch(input, init)
if (response.status === 401) {
console.warn('🔐 401 未授权,清空登录状态')
// router.replace('/auth')
storage2.set('isLogin', false)
}
return response return response
} catch (error) { } catch (error) {
const duration = Date.now() - startTime const duration = Date.now() - startTime

View File

@ -1,15 +1,13 @@
import { root } from '@repo/core' import { root } from '@repo/core'
import { DeviceController } from '@repo/sdk' import { DeviceController } from '@repo/sdk'
import * as Application from 'expo-application' import * as Application from 'expo-application'
import { router } from 'expo-router'
import { makeAutoObservable, runInAction } from 'mobx' import { makeAutoObservable, runInAction } from 'mobx'
import { useEffect } from 'react'
import { Platform } from 'react-native' 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' import { storage } from '@/utils'
// console.log('useSession---------------', useSession)
interface User { interface User {
id: string id: string
email: string email: string
@ -33,17 +31,17 @@ interface User {
interface Session { interface Session {
user?: User user?: User
session?: { session?: {
id: string
userId: string
expiresAt: Date expiresAt: Date
token: string
createdAt: Date createdAt: Date
updatedAt: Date updatedAt: Date
ipAddress?: string | null
userAgent?: string | null
userId: string
impersonatedBy?: string | null
activeOrganizationId?: string | null activeOrganizationId?: string | null
activeTeamId?: string | null activeTeamId?: string | null
impersonatedBy?: string | null id: string
ipAddress?: string | null
token?: string | null
userAgent?: string | null
} }
} }
@ -77,18 +75,31 @@ class UserStore {
await storage.set('isLogin', !!user) await storage.set('isLogin', !!user)
} }
// 设置会话信息 async getUserData() {
async setSession(session: Session | null) { const sessionRes = await getSession()
runInAction(() => {
this.session = session
this.user = session?.user || null
this.isLogin = !!session?.user
if (session?.user) { // console.log('getUserData sessionRes--------:', JSON.stringify(sessionRes))
this.bindDevice()
} 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状态 // 重置store状态
reset() { reset() {
this.user = null
this.session = null this.session = null
this.isLoading = false this.isLoading = false
this.error = null this.error = null
this.scannedQR = null this.scannedQR = null
this.setUser(null)
} }
// ==================== 二维码相关方法 ==================== // ==================== 二维码相关方法 ====================
@ -180,16 +186,3 @@ class UserStore {
// 创建单例实例 // 创建单例实例
export const userStore = new 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
}