refactor(core): introduce EventBus and refactor store infrastructure
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 2m46s
Frontend CI / ota-android (push) Successful in 10m34s
Frontend CI / build-android-apk (push) Successful in 1h15m8s

- 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:
lafay
2026-04-12 18:14:29 +08:00
parent 4b5ce1ba21
commit 6610d2f173
28 changed files with 378 additions and 512 deletions

View File

@@ -1,25 +1,16 @@
/**
* 帖子相关 React Hooks
*/
import { useCallback, useEffect, useState } from 'react';
import { useCallback, useEffect, useRef } from 'react';
import { usePostManagerStore } from './postStore';
import { postManager } from './PostManager';
import type { Post } from '../../types';
import { POST_LIST_PAGE_SIZE } from '../postListSources';
import type { PostListTab } from '../postListSources';
// ==================== usePostList ====================
export interface UsePostListResult {
posts: Post[];
isLoading: boolean;
refresh: () => Promise<void>;
}
/**
* 获取帖子列表 hook
*/
export function usePostList(
type: PostListTab = 'hot',
page = 1,
@@ -33,17 +24,18 @@ export function usePostList(
const key = `list:${type}:${page}:${pageSize}`;
return state.loadingListKeys.has(key);
});
const [hasFetched, setHasFetched] = useState(false);
const lastFetchedRef = useRef<string | null>(null);
useEffect(() => {
if (!hasFetched) {
setHasFetched(true);
const key = `${type}:${page}:${pageSize}`;
if (lastFetchedRef.current !== key) {
lastFetchedRef.current = key;
usePostManagerStore.getState().setLoadingList(type, page, pageSize, true);
postManager.getPosts(type, page, pageSize).finally(() => {
usePostManagerStore.getState().setLoadingList(type, page, pageSize, false);
});
}
}, [type, page, pageSize, hasFetched]);
}, [type, page, pageSize]);
const refresh = useCallback(async () => {
usePostManagerStore.getState().setLoadingList(type, page, pageSize, true);
@@ -57,17 +49,12 @@ export function usePostList(
return { posts, isLoading, refresh };
}
// ==================== usePostDetail ====================
export interface UsePostDetailResult {
post: Post | null;
isLoading: boolean;
refresh: () => Promise<Post | null>;
}
/**
* 获取帖子详情 hook
*/
export function usePostDetail(postId: string | undefined | null): UsePostDetailResult {
const post = usePostManagerStore(state =>
postId ? (state.detailsMap.get(postId)?.data ?? null) : null
@@ -75,18 +62,18 @@ export function usePostDetail(postId: string | undefined | null): UsePostDetailR
const isLoading = usePostManagerStore(state =>
postId ? state.loadingDetailIds.has(postId) : false
);
const [hasFetched, setHasFetched] = useState(false);
const lastFetchedRef = useRef<string | null>(null);
useEffect(() => {
if (!postId) return;
if (!hasFetched) {
setHasFetched(true);
if (lastFetchedRef.current !== postId) {
lastFetchedRef.current = postId;
usePostManagerStore.getState().setLoadingDetail(postId, true);
postManager.getPostDetail(postId).finally(() => {
usePostManagerStore.getState().setLoadingDetail(postId, false);
});
}
}, [postId, hasFetched]);
}, [postId]);
const refresh = useCallback(async () => {
if (!postId) return null;
@@ -99,4 +86,4 @@ export function usePostDetail(postId: string | undefined | null): UsePostDetailR
}, [postId]);
return { post, isLoading, refresh };
}
}