refactor(core): introduce EventBus and refactor store infrastructure
- Add EventBus for decoupled event-driven communication between services - Add EventSubscriber component for centralized event handling - Add requestDedupe utility for shared request deduplication - Refactor api/wsService to use eventBus instead of direct router navigation - Extract MessageMapper.toCachedMessages for consistent message mapping - Remove deprecated BaseManager and CacheBus classes - Improve @mention rendering with memberMap support in segment rendering - Update hooks to use useRef instead of useState for fetch tracking
This commit is contained in:
@@ -6,11 +6,10 @@
|
||||
*/
|
||||
|
||||
import AsyncStorage from '@react-native-async-storage/async-storage';
|
||||
import { CommonActions } from '@react-navigation/native';
|
||||
import Constants from 'expo-constants';
|
||||
import { Platform } from 'react-native';
|
||||
|
||||
import { hrefVerificationGuide } from '../navigation/hrefs';
|
||||
import { eventBus } from '@/core/events/EventBus';
|
||||
|
||||
// 生产地址 https://bbs.littlelan.cn
|
||||
const getBaseUrl = () => {
|
||||
@@ -74,37 +73,27 @@ interface JwtPayload {
|
||||
// API 客户端类
|
||||
class ApiClient {
|
||||
private baseUrl: string;
|
||||
private navigation: any = null;
|
||||
private expoRouter: any = null;
|
||||
private verificationModalShown = false;
|
||||
// 刷新锁:防止并发刷新
|
||||
private refreshLock: Promise<boolean> | null = null;
|
||||
|
||||
constructor(baseUrl: string) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
setExpoRouter(router: any) {
|
||||
this.expoRouter = router;
|
||||
}
|
||||
|
||||
private handleVerificationRequired() {
|
||||
if (this.verificationModalShown) return;
|
||||
this.verificationModalShown = true;
|
||||
if (this.expoRouter) {
|
||||
this.expoRouter.push(hrefVerificationGuide());
|
||||
}
|
||||
eventBus.emit({ type: 'SHOW_VERIFICATION_MODAL' });
|
||||
setTimeout(() => {
|
||||
this.verificationModalShown = false;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
// 设置导航对象(用于处理401跳转)
|
||||
setNavigation(navigation: any) {
|
||||
this.navigation = navigation;
|
||||
private navigateToLogin() {
|
||||
eventBus.emit({ type: 'NAVIGATE', payload: { path: '/(auth)/login' } });
|
||||
eventBus.emit({ type: 'AUTH_LOGOUT' });
|
||||
}
|
||||
|
||||
// 解析 JWT token 获取 payload
|
||||
private parseJwt(token: string): JwtPayload | null {
|
||||
try {
|
||||
const parts = token.split('.');
|
||||
@@ -208,19 +197,10 @@ class ApiClient {
|
||||
if (this.isTokenExpiringSoon(token)) {
|
||||
const refreshed = await this.refreshToken();
|
||||
if (!refreshed) {
|
||||
// 刷新失败,清除 token 并跳转登录
|
||||
await this.clearToken();
|
||||
if (this.navigation) {
|
||||
this.navigation.dispatch(
|
||||
CommonActions.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Login' }],
|
||||
})
|
||||
);
|
||||
}
|
||||
this.navigateToLogin();
|
||||
throw new ApiError(401, '登录已过期,请重新登录');
|
||||
}
|
||||
// 刷新成功,使用新 token
|
||||
const newToken = await this.getToken();
|
||||
if (newToken) {
|
||||
headers['Authorization'] = `Bearer ${newToken}`;
|
||||
@@ -246,23 +226,13 @@ class ApiClient {
|
||||
|
||||
// 处理 401 未授权
|
||||
if (response.status === 401) {
|
||||
// 尝试刷新 token
|
||||
const refreshed = await this.refreshToken();
|
||||
if (!refreshed) {
|
||||
// 刷新失败,清除 token 并跳转登录
|
||||
await this.clearToken();
|
||||
if (this.navigation) {
|
||||
this.navigation.dispatch(
|
||||
CommonActions.reset({
|
||||
index: 0,
|
||||
routes: [{ name: 'Login' }],
|
||||
})
|
||||
);
|
||||
}
|
||||
this.navigateToLogin();
|
||||
throw new ApiError(401, '登录已过期,请重新登录');
|
||||
}
|
||||
|
||||
// 刷新成功后重试请求
|
||||
return this.request(method, path, params, body);
|
||||
}
|
||||
|
||||
|
||||
@@ -38,8 +38,8 @@ class WebRTCManager {
|
||||
// ICE restart 相关状态
|
||||
private reconnectAttempts = 0;
|
||||
private readonly MAX_RECONNECT_ATTEMPTS = 3;
|
||||
private reconnectTimer: NodeJS.Timeout | null = null;
|
||||
private disconnectTimer: NodeJS.Timeout | null = null;
|
||||
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private disconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
async initialize(iceServers: ICEServer[] = []): Promise<void> {
|
||||
if (this.peerConnection) {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { AppState, AppStateStatus } from 'react-native';
|
||||
|
||||
import { api, WS_URL } from './api';
|
||||
import { hrefVerificationGuide } from '../navigation/hrefs';
|
||||
import { eventBus } from '@/core/events/EventBus';
|
||||
import { MessageCategory, SystemMessageType, SystemMessageExtraData, MessageSegment } from '../types/dto';
|
||||
import { systemNotificationService } from './systemNotificationService';
|
||||
|
||||
@@ -300,8 +300,8 @@ class WebSocketService {
|
||||
private reconnectAttempts = 0;
|
||||
private maxReconnectAttempts = 20;
|
||||
private reconnectDelay = 3000;
|
||||
private reconnectTimer: NodeJS.Timeout | null = null;
|
||||
private heartbeatTimer: NodeJS.Timeout | null = null;
|
||||
private reconnectTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private heartbeatTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
private heartbeatInterval = 30000; // 30秒心跳
|
||||
private messageHandlers: Map<WSMessageType, MessageHandler[]> = new Map();
|
||||
private connectionHandlers: ConnectionHandler[] = [];
|
||||
@@ -318,7 +318,7 @@ class WebSocketService {
|
||||
|
||||
// 降级状态
|
||||
private isFallbackMode = false;
|
||||
private fallbackCheckTimer: NodeJS.Timeout | null = null;
|
||||
private fallbackCheckTimer: ReturnType<typeof setTimeout> | null = null;
|
||||
|
||||
private getWSUrl(token: string | null): string {
|
||||
return `${WS_URL}?token=${encodeURIComponent(token || '')}`;
|
||||
@@ -1122,20 +1122,12 @@ class WebSocketService {
|
||||
private handleVerificationRequired(): void {
|
||||
if (this.verificationModalShown) return;
|
||||
this.verificationModalShown = true;
|
||||
if (this.routerRef) {
|
||||
this.routerRef.push(hrefVerificationGuide());
|
||||
}
|
||||
eventBus.emit({ type: 'SHOW_VERIFICATION_MODAL' });
|
||||
setTimeout(() => {
|
||||
this.verificationModalShown = false;
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
private routerRef: any = null;
|
||||
|
||||
setRouter(router: any): void {
|
||||
this.routerRef = router;
|
||||
}
|
||||
|
||||
private startHeartbeat(): void {
|
||||
this.stopHeartbeat();
|
||||
this.heartbeatTimer = setInterval(() => {
|
||||
|
||||
Reference in New Issue
Block a user