- 在AppBootResourceGate组件中集成react-i18next实现资源加载文本的国际化 - 修改DesktopAnimal组件中的loading dots key以提高渲染性能 - 在DesktopControl组件中添加useRef和useEffect钩子管理定时器清理逻辑 - 将DesktopTitle组件重构为MessageBroadcast组件并增强其响应式设计 - 更新DesktopSupportModal组件中的客户服务文本为国际化格式 - 在AuthSession模块中实现本地存储数据清理时保留关键偏好设置 - 调整多个游戏组件的样式类以改进移动端适配效果 - 移除未使用的桌面提取功能相关代码文件 - 更新GitNexus索引统计数据反映最新的代码变更
233 lines
5.4 KiB
TypeScript
233 lines
5.4 KiB
TypeScript
import {
|
|
APP_PREFERENCES_STORAGE_KEY,
|
|
AUDIO_PREFERENCES_STORAGE_KEY,
|
|
LOGIN_PROMPT_DEDUP_MS,
|
|
} from '@/constants'
|
|
import i18n from '@/i18n'
|
|
import { notify } from '@/lib/notify'
|
|
import { queryClient } from '@/lib/query/query-client'
|
|
import { useAuthStore } from '@/store/auth'
|
|
import { useModalStore } from '@/store/modal'
|
|
import type {
|
|
AuthSessionInput,
|
|
AuthUser,
|
|
ClearAuthenticatedSessionOptions,
|
|
CurrentUserInitializer,
|
|
RefreshSessionHandler,
|
|
UnauthorizedSessionOptions,
|
|
} from '@/type'
|
|
|
|
let currentUserInitializer: CurrentUserInitializer | null = null
|
|
let refreshSessionHandler: RefreshSessionHandler | null = null
|
|
let authInitializationPromise: Promise<void> | null = null
|
|
let refreshSessionPromise: Promise<boolean> | null = null
|
|
let lastLoginPromptAt = 0
|
|
|
|
const PRESERVED_LOCAL_STORAGE_KEYS = [
|
|
APP_PREFERENCES_STORAGE_KEY,
|
|
AUDIO_PREFERENCES_STORAGE_KEY,
|
|
]
|
|
|
|
function clearBrowserStorageData() {
|
|
if (typeof localStorage !== 'undefined') {
|
|
const preserved = PRESERVED_LOCAL_STORAGE_KEYS.map(
|
|
(key) => [key, localStorage.getItem(key)] as const,
|
|
)
|
|
|
|
localStorage.clear()
|
|
|
|
for (const [key, value] of preserved) {
|
|
if (value !== null) {
|
|
localStorage.setItem(key, value)
|
|
}
|
|
}
|
|
}
|
|
|
|
if (typeof sessionStorage !== 'undefined') {
|
|
sessionStorage.clear()
|
|
}
|
|
}
|
|
|
|
function hasClearableSessionState() {
|
|
const snapshot = useAuthStore.getState()
|
|
|
|
return Boolean(
|
|
snapshot.status !== 'anonymous' ||
|
|
snapshot.accessToken ||
|
|
snapshot.refreshToken ||
|
|
snapshot.currentUser ||
|
|
snapshot.apiAuthToken ||
|
|
snapshot.apiAuthTokenExpiresAt ||
|
|
snapshot.apiAuthServerTime,
|
|
)
|
|
}
|
|
|
|
function hasRecordedUnauthorizedSession() {
|
|
const snapshot = useAuthStore.getState()
|
|
|
|
return snapshot.status === 'anonymous' && Boolean(snapshot.lastUnauthorizedAt)
|
|
}
|
|
|
|
export function registerCurrentUserInitializer(
|
|
initializer: CurrentUserInitializer | null,
|
|
) {
|
|
currentUserInitializer = initializer
|
|
}
|
|
|
|
export function registerRefreshSessionHandler(
|
|
handler: RefreshSessionHandler | null,
|
|
) {
|
|
refreshSessionHandler = handler
|
|
}
|
|
|
|
export function isAuthenticated() {
|
|
const snapshot = useAuthStore.getState()
|
|
|
|
return snapshot.status === 'authenticated' && Boolean(snapshot.accessToken)
|
|
}
|
|
|
|
export function clearAuthenticatedSession({
|
|
clearBrowserStorage = true,
|
|
clearQueryCache = true,
|
|
}: ClearAuthenticatedSessionOptions = {}) {
|
|
const alreadyUnauthorized = hasRecordedUnauthorizedSession()
|
|
|
|
if (!alreadyUnauthorized) {
|
|
useAuthStore.getState().markUnauthorized()
|
|
}
|
|
|
|
if (clearQueryCache && !alreadyUnauthorized) {
|
|
queryClient.clear()
|
|
}
|
|
|
|
if (clearBrowserStorage && !alreadyUnauthorized) {
|
|
clearBrowserStorageData()
|
|
}
|
|
}
|
|
|
|
export function handleUnauthorizedSession({
|
|
clearBrowserStorage = false,
|
|
openLoginModal = false,
|
|
showLoginRequiredToast = false,
|
|
}: UnauthorizedSessionOptions = {}) {
|
|
clearAuthenticatedSession({
|
|
clearBrowserStorage,
|
|
clearQueryCache: hasClearableSessionState(),
|
|
})
|
|
|
|
if (!openLoginModal && !showLoginRequiredToast) {
|
|
return
|
|
}
|
|
|
|
const modalStore = openLoginModal ? useModalStore.getState() : null
|
|
|
|
if (modalStore?.modals.desktopLogin) {
|
|
return
|
|
}
|
|
|
|
const now = Date.now()
|
|
const shouldPrompt = now - lastLoginPromptAt > LOGIN_PROMPT_DEDUP_MS
|
|
|
|
if (!shouldPrompt) {
|
|
return
|
|
}
|
|
|
|
lastLoginPromptAt = now
|
|
|
|
if (showLoginRequiredToast) {
|
|
notify.warning(i18n.t('commonUi.toast.loginRequired'))
|
|
}
|
|
|
|
if (openLoginModal) {
|
|
modalStore?.openExclusiveModal('desktopLogin')
|
|
}
|
|
}
|
|
|
|
export function handleInvalidTokenSession() {
|
|
handleUnauthorizedSession({
|
|
clearBrowserStorage: true,
|
|
openLoginModal: true,
|
|
showLoginRequiredToast: true,
|
|
})
|
|
}
|
|
|
|
export async function initializeAuthSession() {
|
|
if (authInitializationPromise) {
|
|
return authInitializationPromise
|
|
}
|
|
|
|
authInitializationPromise = (async () => {
|
|
await useAuthStore.persist.rehydrate()
|
|
|
|
const snapshot = useAuthStore.getState()
|
|
|
|
if (
|
|
!snapshot.accessToken ||
|
|
snapshot.currentUser ||
|
|
!currentUserInitializer
|
|
) {
|
|
return
|
|
}
|
|
|
|
const currentUser = await currentUserInitializer()
|
|
|
|
useAuthStore.getState().setCurrentUser(currentUser)
|
|
})().finally(() => {
|
|
authInitializationPromise = null
|
|
})
|
|
|
|
return authInitializationPromise
|
|
}
|
|
|
|
export async function hydrateCurrentUser(initializer: CurrentUserInitializer) {
|
|
const currentUser = await initializer()
|
|
|
|
useAuthStore.getState().setCurrentUser(currentUser)
|
|
|
|
return currentUser
|
|
}
|
|
|
|
export async function tryRefreshAuthSession() {
|
|
if (refreshSessionPromise) {
|
|
return refreshSessionPromise
|
|
}
|
|
|
|
const snapshot = useAuthStore.getState()
|
|
|
|
if (!snapshot.refreshToken || !refreshSessionHandler) {
|
|
return false
|
|
}
|
|
|
|
const refreshToken = snapshot.refreshToken
|
|
|
|
refreshSessionPromise = (async () => {
|
|
try {
|
|
const nextSession = await refreshSessionHandler(refreshToken)
|
|
|
|
if (!nextSession?.accessToken) {
|
|
handleUnauthorizedSession()
|
|
|
|
return false
|
|
}
|
|
|
|
useAuthStore.getState().startSession({
|
|
accessToken: nextSession.accessToken,
|
|
accessTokenExpiresAt:
|
|
nextSession.accessTokenExpiresAt ?? snapshot.accessTokenExpiresAt,
|
|
currentUser: nextSession.currentUser ?? snapshot.currentUser,
|
|
refreshToken: nextSession.refreshToken ?? snapshot.refreshToken,
|
|
})
|
|
|
|
return true
|
|
} catch {
|
|
handleUnauthorizedSession()
|
|
|
|
return false
|
|
} finally {
|
|
refreshSessionPromise = null
|
|
}
|
|
})()
|
|
|
|
return refreshSessionPromise
|
|
}
|