feat(sdk): 更新 SDK 设计文档和 API 重构
- 添加 expiresAt 和 refreshUserSig 参数支持自动续签 - 修改 PushSDK 初始化方式,自动完成设备注册和厂商初始化 - 调整过期续签策略,从提前 15 分钟改为提前 5 分钟触发 - 重构 RN SDK 文档结构,简化安装和使用方式 - 更新统一登录流程,支持 profile 信息传递 - 添加 IM 数据库自动隔离功能 - 修复 Android 群消息聚合问题 - 补充自动化测试验证和错误处理机制
这个提交包含在:
父节点
3e8b9431c7
当前提交
0ca5d43fdb
@ -43,7 +43,9 @@ export interface UserProfile {
|
|||||||
|
|
||||||
export interface AuthResult {
|
export interface AuthResult {
|
||||||
demoToken: string
|
demoToken: string
|
||||||
|
demoTokenExpiresAt?: number | null
|
||||||
imToken: string
|
imToken: string
|
||||||
|
imTokenExpiresAt?: number | null
|
||||||
profile: UserProfile
|
profile: UserProfile
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,6 +66,14 @@ export const demoApi = {
|
|||||||
})
|
})
|
||||||
},
|
},
|
||||||
|
|
||||||
|
refreshImToken(appId: string = APP_ID): Promise<{ imToken: string; imTokenExpiresAt?: number | null }> {
|
||||||
|
return request('/auth/refresh-im', {
|
||||||
|
method: 'POST',
|
||||||
|
skipAuth: false,
|
||||||
|
params: { appId },
|
||||||
|
})
|
||||||
|
},
|
||||||
|
|
||||||
getProfile(): Promise<UserProfile> {
|
getProfile(): Promise<UserProfile> {
|
||||||
return request('/user/profile')
|
return request('/user/profile')
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,16 +1,12 @@
|
|||||||
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
import React, { createContext, useCallback, useContext, useEffect, useState } from 'react'
|
||||||
import { Alert, Linking } from 'react-native'
|
import { Alert, Linking } from 'react-native'
|
||||||
import { XuqmSDK, ImSDK, UpdateSDK } from '@xuqm/rn-sdk'
|
import { XuqmSDK, UpdateSDK } from '@xuqm/rn-sdk'
|
||||||
import { demoApi, type UserProfile } from '../api/demo'
|
import { demoApi, type UserProfile } from '../api/demo'
|
||||||
import { save, load, clearSession, K } from '../utils/storage'
|
import { save, load, clearSession, K } from '../utils/storage'
|
||||||
import pluginMeta from '../../plugin.json'
|
import pluginMeta from '../../plugin.json'
|
||||||
|
|
||||||
const APP_ID = 'ak_demo_chat'
|
const APP_ID = 'ak_demo_chat'
|
||||||
|
|
||||||
function buildImDbName(appId: string, userId: string): string {
|
|
||||||
return `xuqm_im_${appId}_${userId}`
|
|
||||||
}
|
|
||||||
|
|
||||||
interface AuthState {
|
interface AuthState {
|
||||||
ready: boolean
|
ready: boolean
|
||||||
userId: string | null
|
userId: string | null
|
||||||
@ -30,11 +26,21 @@ const AuthContext = createContext<AuthContextValue | null>(null)
|
|||||||
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
export function AuthProvider({ children }: { children: React.ReactNode }) {
|
||||||
const [state, setState] = useState<AuthState>({ ready: false, userId: null, profile: null })
|
const [state, setState] = useState<AuthState>({ ready: false, userId: null, profile: null })
|
||||||
|
|
||||||
const initSDK = useCallback(async (profile: UserProfile, imToken: string) => {
|
const initSDK = useCallback(async (profile: UserProfile, imToken: string, imTokenExpiresAt?: number | null) => {
|
||||||
const appKey = profile.appId || APP_ID
|
const appKey = profile.appId || APP_ID
|
||||||
await XuqmSDK.initialize({ appKey })
|
await XuqmSDK.initialize({ appKey })
|
||||||
const appId = appKey
|
await XuqmSDK.login({
|
||||||
await ImSDK.loginWithToken(profile.userId, imToken, buildImDbName(appId, profile.userId))
|
userId: profile.userId,
|
||||||
|
userSig: imToken,
|
||||||
|
expiresAt: imTokenExpiresAt ?? undefined,
|
||||||
|
profile,
|
||||||
|
refreshUserSig: async () => {
|
||||||
|
const refreshed = await demoApi.refreshImToken(appKey)
|
||||||
|
await save(K.IM_TOKEN, refreshed.imToken)
|
||||||
|
await save(K.IM_TOKEN_EXPIRES_AT, refreshed.imTokenExpiresAt ?? null)
|
||||||
|
return { userSig: refreshed.imToken, expiresAt: refreshed.imTokenExpiresAt ?? undefined }
|
||||||
|
},
|
||||||
|
})
|
||||||
setState({ ready: true, userId: profile.userId, profile })
|
setState({ ready: true, userId: profile.userId, profile })
|
||||||
|
|
||||||
UpdateSDK.checkAppUpdate()
|
UpdateSDK.checkAppUpdate()
|
||||||
@ -60,9 +66,10 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
try {
|
try {
|
||||||
const demoToken = await load<string>(K.DEMO_TOKEN)
|
const demoToken = await load<string>(K.DEMO_TOKEN)
|
||||||
const imToken = await load<string>(K.IM_TOKEN)
|
const imToken = await load<string>(K.IM_TOKEN)
|
||||||
|
const imTokenExpiresAt = await load<number | null>(K.IM_TOKEN_EXPIRES_AT)
|
||||||
const profile = await load<UserProfile>(K.PROFILE)
|
const profile = await load<UserProfile>(K.PROFILE)
|
||||||
if (demoToken && imToken && profile) {
|
if (demoToken && imToken && profile) {
|
||||||
await initSDK(profile, imToken)
|
await initSDK(profile, imToken, imTokenExpiresAt ?? undefined)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
@ -73,11 +80,12 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
restore()
|
restore()
|
||||||
}, [initSDK])
|
}, [initSDK])
|
||||||
|
|
||||||
const handleAuthResult = useCallback(async (result: { demoToken: string; imToken: string; profile: UserProfile }) => {
|
const handleAuthResult = useCallback(async (result: { demoToken: string; demoTokenExpiresAt?: number | null; imToken: string; imTokenExpiresAt?: number | null; profile: UserProfile }) => {
|
||||||
await save(K.DEMO_TOKEN, result.demoToken)
|
await save(K.DEMO_TOKEN, result.demoToken)
|
||||||
await save(K.IM_TOKEN, result.imToken)
|
await save(K.IM_TOKEN, result.imToken)
|
||||||
|
await save(K.IM_TOKEN_EXPIRES_AT, result.imTokenExpiresAt ?? null)
|
||||||
await save(K.PROFILE, result.profile)
|
await save(K.PROFILE, result.profile)
|
||||||
await initSDK(result.profile, result.imToken)
|
await initSDK(result.profile, result.imToken, result.imTokenExpiresAt ?? undefined)
|
||||||
}, [initSDK])
|
}, [initSDK])
|
||||||
|
|
||||||
const login = useCallback(async (userId: string, password: string) => {
|
const login = useCallback(async (userId: string, password: string) => {
|
||||||
@ -91,7 +99,7 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
|
|||||||
}, [handleAuthResult])
|
}, [handleAuthResult])
|
||||||
|
|
||||||
const logout = useCallback(async () => {
|
const logout = useCallback(async () => {
|
||||||
ImSDK.disconnect()
|
await XuqmSDK.logout()
|
||||||
await clearSession()
|
await clearSession()
|
||||||
setState({ ready: true, userId: null, profile: null })
|
setState({ ready: true, userId: null, profile: null })
|
||||||
}, [])
|
}, [])
|
||||||
|
|||||||
@ -3,6 +3,7 @@ import AsyncStorage from '@react-native-async-storage/async-storage'
|
|||||||
export const K = {
|
export const K = {
|
||||||
DEMO_TOKEN: '@xuqm:demoToken',
|
DEMO_TOKEN: '@xuqm:demoToken',
|
||||||
IM_TOKEN: '@xuqm:imToken',
|
IM_TOKEN: '@xuqm:imToken',
|
||||||
|
IM_TOKEN_EXPIRES_AT: '@xuqm:imTokenExpiresAt',
|
||||||
PROFILE: '@xuqm:profile',
|
PROFILE: '@xuqm:profile',
|
||||||
CONTACTS: '@xuqm:contacts',
|
CONTACTS: '@xuqm:contacts',
|
||||||
} as const
|
} as const
|
||||||
@ -22,5 +23,5 @@ export async function remove(key: string): Promise<void> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function clearSession(): Promise<void> {
|
export async function clearSession(): Promise<void> {
|
||||||
await Promise.all([K.DEMO_TOKEN, K.IM_TOKEN, K.PROFILE].map(k => AsyncStorage.removeItem(k)))
|
await Promise.all([K.DEMO_TOKEN, K.IM_TOKEN, K.IM_TOKEN_EXPIRES_AT, K.PROFILE].map(k => AsyncStorage.removeItem(k)))
|
||||||
}
|
}
|
||||||
|
|||||||
正在加载...
在新工单中引用
屏蔽一个用户