Files
frontend/src/stores/userStore.ts

327 lines
9.7 KiB
TypeScript
Raw Normal View History

/**
*
* 使Zustand进行状态管理
* API
*/
import { create } from 'zustand';
import { User, Post, Notification, NotificationBadge } from '../types';
import { PaginatedData } from '../services/api';
import {
authService,
postService,
notificationService,
} from '../services';
import { userManager } from './userManager';
import { messageManager } from './messageManager';
interface UserState {
// 状态
users: User[];
userCache: Record<string, User>; // 用户缓存
posts: Post[];
notifications: Notification[];
notificationBadge: NotificationBadge;
messageUnreadCount: number;
// 加载状态
isLoadingPosts: boolean;
isLoadingNotifications: boolean;
// 搜索历史
searchHistory: string[];
// 操作
fetchUser: (userId: string) => Promise<User | undefined>;
fetchUserPosts: (userId: string, page?: number) => Promise<Post[]>;
fetchPosts: (type: 'follow' | 'hot' | 'latest', page?: number) => Promise<PaginatedData<Post>>;
fetchNotifications: (type?: string, page?: number) => Promise<Notification[]>;
markNotificationAsRead: (notificationId: string) => Promise<void>;
markAllNotificationsAsRead: () => Promise<void>;
fetchNotificationBadge: () => Promise<void>;
setMessageUnreadCount: (count: number) => void;
addSearchHistory: (keyword: string) => void;
clearSearchHistory: () => void;
// 关注用户操作authService 不管理状态,所以保留在 store
followUser: (userId: string) => Promise<void>;
unfollowUser: (userId: string) => Promise<void>;
// 刷新数据
refreshAll: () => Promise<void>;
}
export const useUserStore = create<UserState>((set, get) => {
return {
// 初始状态
users: [],
userCache: {},
posts: [],
notifications: [],
notificationBadge: {
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
},
messageUnreadCount: 0,
searchHistory: [],
isLoadingPosts: false,
isLoadingNotifications: false,
// 获取用户信息
fetchUser: async (userId: string) => {
// 先检查缓存
const cached = get().userCache[userId];
if (cached) return cached;
try {
const user = await userManager.getUserById(userId);
if (user) {
set(state => ({
userCache: { ...state.userCache, [userId]: user }
}));
return user;
}
} catch (error) {
console.error('获取用户信息失败:', error);
}
return undefined;
},
// 获取用户帖子
fetchUserPosts: async (userId: string, page = 1) => {
try {
const response = await postService.getUserPosts(userId, page);
const newPosts = response.list;
set(state => ({
posts: page === 1 ? newPosts : [...state.posts, ...newPosts]
}));
return newPosts;
} catch (error) {
console.error('获取用户帖子失败:', error);
return [];
}
},
// 获取帖子列表(首页)
fetchPosts: async (type: 'follow' | 'hot' | 'latest', page = 1) => {
set({ isLoadingPosts: true });
try {
let response;
switch (type) {
case 'follow':
response = await postService.getFollowingPosts(page);
break;
case 'hot':
response = await postService.getHotPosts(page);
break;
case 'latest':
default:
response = await postService.getLatestPosts(page);
break;
}
const newPosts = response.list;
set(state => ({
posts: page === 1 ? newPosts : [...state.posts, ...newPosts],
isLoadingPosts: false
}));
return response;
} catch (error) {
console.error('获取帖子列表失败:', error);
set({ isLoadingPosts: false });
return {
list: [],
total: 0,
page,
page_size: 20,
total_pages: 0,
};
}
},
// 获取通知列表
fetchNotifications: async (type?: string, page = 1) => {
set({ isLoadingNotifications: true });
try {
const response = await notificationService.getNotifications(
page,
20,
type as any
);
const newNotifications = response.list;
set(state => ({
notifications: page === 1 ? newNotifications : [...state.notifications, ...newNotifications],
isLoadingNotifications: false
}));
return newNotifications;
} catch (error) {
console.error('获取通知列表失败:', error);
set({ isLoadingNotifications: false });
return [];
}
},
// 标记通知为已读
markNotificationAsRead: async (notificationId: string) => {
try {
await notificationService.markAsRead(notificationId);
set(state => {
const notifications = state.notifications.map(n =>
n.id === notificationId ? { ...n, isRead: true } : n
);
return {
notifications,
notificationBadge: {
total: notifications.filter(n => !n.isRead).length,
likes: notifications.filter(n => n.type === 'like_post' && !n.isRead).length,
comments: notifications.filter(n => n.type === 'comment' && !n.isRead).length,
follows: notifications.filter(n => n.type === 'follow' && !n.isRead).length,
system: notifications.filter(n => n.type === 'system' && !n.isRead).length,
}
};
});
} catch (error) {
console.error('标记通知为已读失败:', error);
}
},
// 标记所有通知为已读
markAllNotificationsAsRead: async () => {
try {
await notificationService.markAllAsRead();
set(state => ({
notifications: state.notifications.map(n => ({ ...n, isRead: true })),
notificationBadge: {
total: 0,
likes: 0,
comments: 0,
follows: 0,
system: 0,
}
}));
} catch (error) {
console.error('标记所有通知为已读失败:', error);
}
},
// 获取通知角标
fetchNotificationBadge: async () => {
try {
const badge = await notificationService.getNotificationBadge();
set({ notificationBadge: badge });
} catch (error) {
console.error('获取通知角标失败:', error);
}
},
// 设置消息未读数
// 注意:未读数现在由 MessageManager 统一管理
// 此方法保留用于 TabBar 角标显示,从 MessageManager 同步
setMessageUnreadCount: (count: number) => {
set({ messageUnreadCount: count });
},
// 添加搜索历史
addSearchHistory: (keyword: string) => {
set(state => {
const history = [keyword, ...state.searchHistory.filter(k => k !== keyword)].slice(0, 20);
return { searchHistory: history };
});
},
// 清除搜索历史
clearSearchHistory: () => {
set({ searchHistory: [] });
},
// 关注用户 - authService 不管理状态,所以由 store 管理
followUser: async (userId: string) => {
const originalUsers = get().users;
const originalUserCache = get().userCache;
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: true, followersCount: u.followers_count + 1 }
: u
),
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: true, followersCount: user.followers_count + 1 }
: user
])
)
}));
try {
await authService.followUser(userId);
} catch (error) {
console.error('关注用户失败:', error);
set({ users: originalUsers, userCache: originalUserCache });
}
},
unfollowUser: async (userId: string) => {
const originalUsers = get().users;
const originalUserCache = get().userCache;
set(state => ({
users: state.users.map(u =>
u.id === userId
? { ...u, isFollowing: false, followersCount: Math.max(0, u.followers_count - 1) }
: u
),
userCache: Object.fromEntries(
Object.entries(state.userCache).map(([id, user]) => [
id,
id === userId
? { ...user, isFollowing: false, followersCount: Math.max(0, user.followers_count - 1) }
: user
])
)
}));
try {
await authService.unfollowUser(userId);
} catch (error) {
console.error('取消关注用户失败:', error);
set({ users: originalUsers, userCache: originalUserCache });
}
},
// 刷新所有数据
refreshAll: async () => {
await Promise.all([
get().fetchPosts('latest', 1),
get().fetchNotificationBadge(),
]);
},
};
});
// 导出selector hooks以优化性能
export const usePosts = () => useUserStore((state) => state.posts);
export const useNotifications = () => useUserStore((state) => state.notifications);
export const useNotificationBadge = () => useUserStore((state) => state.notificationBadge);
export const useMessageUnreadCount = () => useUserStore((state) => state.messageUnreadCount);
export const useSearchHistory = () => useUserStore((state) => state.searchHistory);
export const useIsLoadingPosts = () => useUserStore((state) => state.isLoadingPosts);