118 行
2.9 KiB
TypeScript
118 行
2.9 KiB
TypeScript
|
|
import { _clearToken, setUserId as setCommonUserId, getUserId as getCommonUserId, XuqmSDK as CommonXuqmSDK } from '@xuqm/rn-common'
|
||
|
|
import { ImSDK } from '@xuqm/rn-im'
|
||
|
|
import { PushSDK } from '@xuqm/rn-push'
|
||
|
|
import type { UserProfile } from '@xuqm/rn-im'
|
||
|
|
|
||
|
|
export interface UnifiedLoginRefreshResult {
|
||
|
|
userSig: string
|
||
|
|
expiresAt?: number
|
||
|
|
}
|
||
|
|
|
||
|
|
export interface UnifiedLoginOptions {
|
||
|
|
userId: string
|
||
|
|
userSig: string
|
||
|
|
profile?: Pick<UserProfile, 'nickname' | 'avatar' | 'gender'>
|
||
|
|
expiresAt?: number
|
||
|
|
refreshUserSig?: () => Promise<UnifiedLoginRefreshResult>
|
||
|
|
}
|
||
|
|
|
||
|
|
type SessionState = UnifiedLoginOptions
|
||
|
|
|
||
|
|
const REFRESH_GRACE_MS = 5 * 60 * 1000
|
||
|
|
const REFRESH_RETRY_MS = 30 * 1000
|
||
|
|
|
||
|
|
let currentSession: SessionState | null = null
|
||
|
|
let refreshTimer: ReturnType<typeof setTimeout> | null = null
|
||
|
|
let refreshInFlight = false
|
||
|
|
|
||
|
|
function clearRefreshTimer(): void {
|
||
|
|
if (refreshTimer) {
|
||
|
|
clearTimeout(refreshTimer)
|
||
|
|
refreshTimer = null
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
async function applyLoginSession(session: SessionState): Promise<void> {
|
||
|
|
clearRefreshTimer()
|
||
|
|
setCommonUserId(session.userId)
|
||
|
|
try {
|
||
|
|
await ImSDK.loginWithUserSig(session.userId, session.userSig)
|
||
|
|
} catch (error) {
|
||
|
|
setCommonUserId(null)
|
||
|
|
currentSession = null
|
||
|
|
throw error
|
||
|
|
}
|
||
|
|
currentSession = session
|
||
|
|
try {
|
||
|
|
await PushSDK.initialize(session.userId)
|
||
|
|
} catch (error) {
|
||
|
|
void error
|
||
|
|
}
|
||
|
|
scheduleRefresh()
|
||
|
|
}
|
||
|
|
|
||
|
|
async function refreshSession(): Promise<void> {
|
||
|
|
const session = currentSession
|
||
|
|
if (refreshInFlight || !session?.refreshUserSig) return
|
||
|
|
refreshInFlight = true
|
||
|
|
try {
|
||
|
|
const refreshed = await session.refreshUserSig()
|
||
|
|
if (currentSession !== session) return
|
||
|
|
await applyLoginSession({
|
||
|
|
...session,
|
||
|
|
userSig: refreshed.userSig,
|
||
|
|
expiresAt: refreshed.expiresAt ?? session.expiresAt,
|
||
|
|
})
|
||
|
|
} catch (error) {
|
||
|
|
clearRefreshTimer()
|
||
|
|
refreshTimer = setTimeout(() => {
|
||
|
|
void refreshSession()
|
||
|
|
}, REFRESH_RETRY_MS)
|
||
|
|
if (CommonXuqmSDK.getUserId() && process.env.NODE_ENV !== 'production') {
|
||
|
|
console.warn('[XuqmSDK] userSig refresh failed, will retry', error)
|
||
|
|
}
|
||
|
|
} finally {
|
||
|
|
refreshInFlight = false
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
function scheduleRefresh(): void {
|
||
|
|
clearRefreshTimer()
|
||
|
|
const session = currentSession
|
||
|
|
if (!session?.refreshUserSig || !session.expiresAt) return
|
||
|
|
const delayMs = Math.max(session.expiresAt - Date.now() - REFRESH_GRACE_MS, 0)
|
||
|
|
refreshTimer = setTimeout(() => {
|
||
|
|
void refreshSession()
|
||
|
|
}, delayMs)
|
||
|
|
}
|
||
|
|
|
||
|
|
async function login(options: UnifiedLoginOptions): Promise<void> {
|
||
|
|
if (currentSession) {
|
||
|
|
await logout()
|
||
|
|
}
|
||
|
|
await applyLoginSession(options)
|
||
|
|
}
|
||
|
|
|
||
|
|
async function logout(): Promise<void> {
|
||
|
|
clearRefreshTimer()
|
||
|
|
const userId = currentSession?.userId ?? getCommonUserId()
|
||
|
|
currentSession = null
|
||
|
|
refreshInFlight = false
|
||
|
|
setCommonUserId(null)
|
||
|
|
if (userId) {
|
||
|
|
try {
|
||
|
|
await PushSDK.logout(userId)
|
||
|
|
} catch (error) {
|
||
|
|
void error
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ImSDK.disconnect()
|
||
|
|
await _clearToken()
|
||
|
|
}
|
||
|
|
|
||
|
|
export const XuqmSDK = {
|
||
|
|
...CommonXuqmSDK,
|
||
|
|
login,
|
||
|
|
logout,
|
||
|
|
}
|