2026-03-09 21:29:03 +08:00
|
|
|
/**
|
|
|
|
|
* 用户状态管理
|
|
|
|
|
* 使用Zustand进行状态管理
|
|
|
|
|
* 对接真实后端API
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
import { create } from 'zustand';
|
|
|
|
|
import { User, Post, Notification, NotificationBadge } from '../types';
|
|
|
|
|
import { PaginatedData } from '../services/api';
|
|
|
|
|
import {
|
|
|
|
|
authService,
|
|
|
|
|
postService,
|
|
|
|
|
commentService,
|
|
|
|
|
notificationService,
|
|
|
|
|
} from '../services';
|
|
|
|
|
import { userManager } from './userManager';
|
|
|
|
|
import { messageManager } from './messageManager';
|
2026-03-18 12:11:49 +08:00
|
|
|
import { createOptimisticUpdaterFactory } from '../utils/optimisticUpdate';
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
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: 'recommend' | '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;
|
|
|
|
|
fetchMessageUnreadCount: () => Promise<void>;
|
|
|
|
|
addSearchHistory: (keyword: string) => void;
|
|
|
|
|
clearSearchHistory: () => void;
|
|
|
|
|
|
|
|
|
|
// 互动操作
|
|
|
|
|
likePost: (postId: string) => Promise<void>;
|
|
|
|
|
unlikePost: (postId: string) => Promise<void>;
|
|
|
|
|
favoritePost: (postId: string) => Promise<void>;
|
|
|
|
|
unfavoritePost: (postId: string) => Promise<void>;
|
|
|
|
|
likeComment: (commentId: string) => Promise<void>;
|
|
|
|
|
unlikeComment: (commentId: string) => Promise<void>;
|
|
|
|
|
followUser: (userId: string) => Promise<void>;
|
|
|
|
|
unfollowUser: (userId: string) => Promise<void>;
|
|
|
|
|
|
|
|
|
|
// 刷新数据
|
|
|
|
|
refreshAll: () => Promise<void>;
|
|
|
|
|
}
|
|
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
export const useUserStore = create<UserState>((set, get) => {
|
|
|
|
|
// 创建乐观更新器
|
|
|
|
|
const optimisticUpdater = createOptimisticUpdaterFactory<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;
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 获取用户帖子
|
|
|
|
|
fetchUserPosts: async (userId: string, page = 1) => {
|
|
|
|
|
try {
|
|
|
|
|
const response = await postService.getUserPosts(userId, page);
|
|
|
|
|
const newPosts = response.list;
|
|
|
|
|
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
2026-03-18 12:11:49 +08:00
|
|
|
posts: page === 1 ? newPosts : [...state.posts, ...newPosts]
|
2026-03-09 21:29:03 +08:00
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
return newPosts;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取用户帖子失败:', error);
|
|
|
|
|
return [];
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 获取帖子列表(首页)
|
|
|
|
|
fetchPosts: async (type: 'recommend' | 'follow' | 'hot' | 'latest', page = 1) => {
|
|
|
|
|
set({ isLoadingPosts: true });
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
try {
|
|
|
|
|
let response;
|
|
|
|
|
|
|
|
|
|
switch (type) {
|
|
|
|
|
case 'recommend':
|
|
|
|
|
response = await postService.getRecommendedPosts(page);
|
|
|
|
|
break;
|
|
|
|
|
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,
|
|
|
|
|
};
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 获取通知列表
|
|
|
|
|
fetchNotifications: async (type?: string, page = 1) => {
|
|
|
|
|
set({ isLoadingNotifications: true });
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
try {
|
|
|
|
|
const response = await notificationService.getNotifications(
|
|
|
|
|
page,
|
|
|
|
|
20,
|
|
|
|
|
type as any
|
2026-03-09 21:29:03 +08:00
|
|
|
);
|
|
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
const newNotifications = response.list;
|
|
|
|
|
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
2026-03-18 12:11:49 +08:00
|
|
|
notifications: page === 1 ? newNotifications : [...state.notifications, ...newNotifications],
|
|
|
|
|
isLoadingNotifications: false
|
2026-03-09 21:29:03 +08:00
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
return newNotifications;
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取通知列表失败:', error);
|
|
|
|
|
set({ isLoadingNotifications: false });
|
|
|
|
|
return [];
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 标记通知为已读
|
|
|
|
|
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();
|
|
|
|
|
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
2026-03-18 12:11:49 +08:00
|
|
|
notifications: state.notifications.map(n => ({ ...n, isRead: true })),
|
|
|
|
|
notificationBadge: {
|
|
|
|
|
total: 0,
|
|
|
|
|
likes: 0,
|
|
|
|
|
comments: 0,
|
|
|
|
|
follows: 0,
|
|
|
|
|
system: 0,
|
|
|
|
|
}
|
2026-03-09 21:29:03 +08:00
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
} catch (error) {
|
|
|
|
|
console.error('标记所有通知为已读失败:', error);
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 获取通知角标
|
|
|
|
|
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 });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 从后端拉取最新消息未读数
|
|
|
|
|
// @deprecated 请使用 messageManager.fetchUnreadCount() 代替
|
|
|
|
|
// 此方法保留用于向后兼容
|
|
|
|
|
fetchMessageUnreadCount: async () => {
|
|
|
|
|
try {
|
|
|
|
|
await messageManager.fetchUnreadCount();
|
|
|
|
|
const unread = messageManager.getUnreadCount();
|
|
|
|
|
set({ messageUnreadCount: unread.total + unread.system });
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('获取消息未读数失败:', error);
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 添加搜索历史
|
|
|
|
|
addSearchHistory: (keyword: string) => {
|
|
|
|
|
set(state => {
|
|
|
|
|
const history = [keyword, ...state.searchHistory.filter(k => k !== keyword)].slice(0, 20);
|
|
|
|
|
return { searchHistory: history };
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 清除搜索历史
|
|
|
|
|
clearSearchHistory: () => {
|
|
|
|
|
set({ searchHistory: [] });
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 点赞帖子 - 使用乐观更新工具
|
|
|
|
|
likePost: async (postId: string) => {
|
|
|
|
|
const originalPosts = get().posts;
|
|
|
|
|
|
|
|
|
|
// 乐观更新
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? { ...p, is_liked: true, likes_count: p.likes_count + 1 } : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const updatedPost = await postService.likePost(postId);
|
|
|
|
|
if (updatedPost) {
|
|
|
|
|
// 使用服务器返回的数据更新
|
|
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? updatedPost : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('点赞帖子失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({ posts: originalPosts });
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 取消点赞 - 使用乐观更新工具
|
|
|
|
|
unlikePost: async (postId: string) => {
|
|
|
|
|
const originalPosts = get().posts;
|
|
|
|
|
|
|
|
|
|
// 乐观更新
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
2026-03-18 12:11:49 +08:00
|
|
|
p.id === postId ? { ...p, is_liked: false, likes_count: Math.max(0, p.likes_count - 1) } : p
|
2026-03-09 21:29:03 +08:00
|
|
|
)
|
|
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const updatedPost = await postService.unlikePost(postId);
|
|
|
|
|
if (updatedPost) {
|
|
|
|
|
// 使用服务器返回的数据更新
|
|
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? updatedPost : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('取消点赞帖子失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({ posts: originalPosts });
|
2026-03-09 21:29:03 +08:00
|
|
|
}
|
2026-03-18 12:11:49 +08:00
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 收藏帖子 - 使用乐观更新工具
|
|
|
|
|
favoritePost: async (postId: string) => {
|
|
|
|
|
const originalPosts = get().posts;
|
|
|
|
|
|
|
|
|
|
// 乐观更新
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? { ...p, is_favorited: true, favorites_count: p.favorites_count + 1 } : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const updatedPost = await postService.favoritePost(postId);
|
|
|
|
|
if (updatedPost) {
|
|
|
|
|
// 使用服务器返回的数据更新
|
|
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? updatedPost : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('收藏帖子失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({ posts: originalPosts });
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 取消收藏 - 使用乐观更新工具
|
|
|
|
|
unfavoritePost: async (postId: string) => {
|
|
|
|
|
const originalPosts = get().posts;
|
|
|
|
|
|
|
|
|
|
// 乐观更新
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
2026-03-18 12:11:49 +08:00
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? { ...p, is_favorited: false, favorites_count: Math.max(0, p.favorites_count - 1) } : p
|
|
|
|
|
)
|
2026-03-09 21:29:03 +08:00
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
const updatedPost = await postService.unfavoritePost(postId);
|
|
|
|
|
if (updatedPost) {
|
|
|
|
|
// 使用服务器返回的数据更新
|
|
|
|
|
set(state => ({
|
|
|
|
|
posts: state.posts.map(p =>
|
|
|
|
|
p.id === postId ? updatedPost : p
|
|
|
|
|
)
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('取消收藏帖子失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({ posts: originalPosts });
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 点赞评论 - 使用乐观更新工具
|
|
|
|
|
likeComment: async (commentId: string) => {
|
|
|
|
|
await optimisticUpdater({
|
|
|
|
|
key: 'posts',
|
|
|
|
|
optimisticUpdate: (posts) =>
|
|
|
|
|
posts.map(p => ({
|
|
|
|
|
...p,
|
|
|
|
|
top_comment: p.top_comment && p.top_comment.id === commentId
|
|
|
|
|
? { ...p.top_comment, is_liked: true, likes_count: p.top_comment.likes_count + 1 }
|
|
|
|
|
: p.top_comment
|
|
|
|
|
})),
|
|
|
|
|
rollbackState: (original) => original,
|
|
|
|
|
apiCall: () => commentService.likeComment(commentId),
|
|
|
|
|
errorMessage: '点赞评论失败',
|
|
|
|
|
});
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 取消点赞评论 - 使用乐观更新工具
|
|
|
|
|
unlikeComment: async (commentId: string) => {
|
|
|
|
|
await optimisticUpdater({
|
|
|
|
|
key: 'posts',
|
|
|
|
|
optimisticUpdate: (posts) =>
|
|
|
|
|
posts.map(p => ({
|
|
|
|
|
...p,
|
|
|
|
|
top_comment: p.top_comment && p.top_comment.id === commentId
|
|
|
|
|
? { ...p.top_comment, is_liked: false, likes_count: Math.max(0, p.top_comment.likes_count - 1) }
|
|
|
|
|
: p.top_comment
|
|
|
|
|
})),
|
|
|
|
|
rollbackState: (original) => original,
|
|
|
|
|
apiCall: () => commentService.unlikeComment(commentId),
|
|
|
|
|
errorMessage: '取消点赞评论失败',
|
|
|
|
|
});
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 关注用户 - 使用乐观更新工具
|
|
|
|
|
followUser: async (userId: string) => {
|
|
|
|
|
const originalUsers = get().users;
|
|
|
|
|
const originalUserCache = get().userCache;
|
|
|
|
|
|
|
|
|
|
// 乐观更新 users
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
|
|
|
|
users: state.users.map(u =>
|
|
|
|
|
u.id === userId
|
2026-03-18 12:11:49 +08:00
|
|
|
? { ...u, isFollowing: true, followersCount: u.followers_count + 1 }
|
2026-03-09 21:29:03 +08:00
|
|
|
: u
|
|
|
|
|
),
|
2026-03-18 12:11:49 +08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 乐观更新 userCache
|
|
|
|
|
set(state => ({
|
2026-03-09 21:29:03 +08:00
|
|
|
userCache: Object.fromEntries(
|
|
|
|
|
Object.entries(state.userCache).map(([id, user]) => [
|
|
|
|
|
id,
|
|
|
|
|
id === userId
|
2026-03-18 12:11:49 +08:00
|
|
|
? { ...user, isFollowing: true, followersCount: user.followers_count + 1 }
|
2026-03-09 21:29:03 +08:00
|
|
|
: user
|
|
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await authService.followUser(userId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('关注用户失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({
|
|
|
|
|
users: originalUsers,
|
|
|
|
|
userCache: originalUserCache
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-09 21:29:03 +08:00
|
|
|
|
2026-03-18 12:11:49 +08:00
|
|
|
// 取消关注 - 使用乐观更新工具
|
|
|
|
|
unfollowUser: async (userId: string) => {
|
|
|
|
|
const originalUsers = get().users;
|
|
|
|
|
const originalUserCache = get().userCache;
|
|
|
|
|
|
|
|
|
|
// 乐观更新 users
|
2026-03-09 21:29:03 +08:00
|
|
|
set(state => ({
|
|
|
|
|
users: state.users.map(u =>
|
|
|
|
|
u.id === userId
|
2026-03-18 12:11:49 +08:00
|
|
|
? { ...u, isFollowing: false, followersCount: Math.max(0, u.followers_count - 1) }
|
2026-03-09 21:29:03 +08:00
|
|
|
: u
|
|
|
|
|
),
|
2026-03-18 12:11:49 +08:00
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
// 乐观更新 userCache
|
|
|
|
|
set(state => ({
|
2026-03-09 21:29:03 +08:00
|
|
|
userCache: Object.fromEntries(
|
|
|
|
|
Object.entries(state.userCache).map(([id, user]) => [
|
|
|
|
|
id,
|
|
|
|
|
id === userId
|
2026-03-18 12:11:49 +08:00
|
|
|
? { ...user, isFollowing: false, followersCount: Math.max(0, user.followers_count - 1) }
|
2026-03-09 21:29:03 +08:00
|
|
|
: user
|
|
|
|
|
])
|
|
|
|
|
)
|
|
|
|
|
}));
|
2026-03-18 12:11:49 +08:00
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
await authService.unfollowUser(userId);
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('取消关注用户失败:', error);
|
|
|
|
|
// 回滚
|
|
|
|
|
set({
|
|
|
|
|
users: originalUsers,
|
|
|
|
|
userCache: originalUserCache
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
// 刷新所有数据
|
|
|
|
|
refreshAll: async () => {
|
|
|
|
|
await Promise.all([
|
|
|
|
|
get().fetchPosts('recommend', 1),
|
|
|
|
|
get().fetchNotificationBadge(),
|
|
|
|
|
]);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
});
|
2026-03-09 21:29:03 +08:00
|
|
|
|
|
|
|
|
// 导出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);
|