refactor(GroupMembersScreen): enhance member list management with remote data source
- Introduced a new remote data source for managing group members using cursor pagination. - Updated member list loading logic to utilize the new data source, improving data synchronization and loading states. - Enhanced member enrichment process by integrating user profiles into the member list. - Refactored related imports and hooks for better organization and clarity.
This commit is contained in:
110
src/stores/groupMemberListSources.ts
Normal file
110
src/stores/groupMemberListSources.ts
Normal file
@@ -0,0 +1,110 @@
|
||||
import { groupService } from '../services/groupService';
|
||||
import { CursorPaginationResponse, GroupMemberResponse } from '../types/dto';
|
||||
|
||||
export const GROUP_MEMBER_LIST_PAGE_SIZE = 50;
|
||||
|
||||
export type GroupMemberListSourceKind = 'cursor' | 'offset';
|
||||
|
||||
export interface IGroupMemberListPagedSource {
|
||||
restart(): void;
|
||||
loadNext(): Promise<CursorPaginationResponse<GroupMemberResponse>>;
|
||||
readonly hasMore: boolean;
|
||||
}
|
||||
|
||||
export class NetworkCursorGroupMemberListPagedSource implements IGroupMemberListPagedSource {
|
||||
private nextCursor: string | null = null;
|
||||
private hasMoreAfterLastLoad = false;
|
||||
private loadedOnce = false;
|
||||
private readonly groupId: number | string;
|
||||
private readonly pageSize: number;
|
||||
|
||||
constructor(groupId: number | string, pageSize: number = GROUP_MEMBER_LIST_PAGE_SIZE) {
|
||||
this.groupId = groupId;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
restart(): void {
|
||||
this.nextCursor = null;
|
||||
this.hasMoreAfterLastLoad = false;
|
||||
this.loadedOnce = false;
|
||||
}
|
||||
|
||||
get hasMore(): boolean {
|
||||
return this.loadedOnce && this.hasMoreAfterLastLoad;
|
||||
}
|
||||
|
||||
async loadNext(): Promise<CursorPaginationResponse<GroupMemberResponse>> {
|
||||
const response = await groupService.getGroupMembersCursor(
|
||||
this.groupId,
|
||||
this.nextCursor == null
|
||||
? { page_size: this.pageSize }
|
||||
: { cursor: this.nextCursor, page_size: this.pageSize }
|
||||
);
|
||||
|
||||
this.nextCursor = response.next_cursor ?? null;
|
||||
this.hasMoreAfterLastLoad = Boolean(response.has_more && response.next_cursor != null);
|
||||
this.loadedOnce = true;
|
||||
|
||||
return {
|
||||
list: response.list || [],
|
||||
next_cursor: response.next_cursor ?? null,
|
||||
prev_cursor: response.prev_cursor ?? null,
|
||||
has_more: response.has_more ?? false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export class NetworkOffsetGroupMemberListPagedSource implements IGroupMemberListPagedSource {
|
||||
private nextPage = 1;
|
||||
private hasMoreAfterLastLoad = false;
|
||||
private loadedOnce = false;
|
||||
private readonly groupId: number | string;
|
||||
private readonly pageSize: number;
|
||||
|
||||
constructor(groupId: number | string, pageSize: number = GROUP_MEMBER_LIST_PAGE_SIZE) {
|
||||
this.groupId = groupId;
|
||||
this.pageSize = pageSize;
|
||||
}
|
||||
|
||||
restart(): void {
|
||||
this.nextPage = 1;
|
||||
this.hasMoreAfterLastLoad = false;
|
||||
this.loadedOnce = false;
|
||||
}
|
||||
|
||||
get hasMore(): boolean {
|
||||
return this.loadedOnce && this.hasMoreAfterLastLoad;
|
||||
}
|
||||
|
||||
async loadNext(): Promise<CursorPaginationResponse<GroupMemberResponse>> {
|
||||
const response = await groupService.getMembers(this.groupId, this.nextPage, this.pageSize);
|
||||
const list = response.list || [];
|
||||
const currentPage = response.page ?? this.nextPage;
|
||||
const totalPages = response.total_pages ?? currentPage;
|
||||
const hasMore = currentPage < totalPages;
|
||||
const nextPageCursor = hasMore ? String(currentPage + 1) : null;
|
||||
const prevPageCursor = currentPage > 1 ? String(currentPage - 1) : null;
|
||||
|
||||
this.nextPage = currentPage + 1;
|
||||
this.hasMoreAfterLastLoad = hasMore;
|
||||
this.loadedOnce = true;
|
||||
|
||||
return {
|
||||
list,
|
||||
next_cursor: nextPageCursor,
|
||||
prev_cursor: prevPageCursor,
|
||||
has_more: hasMore,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function createRemoteGroupMemberListSource(
|
||||
kind: GroupMemberListSourceKind,
|
||||
groupId: number | string,
|
||||
pageSize: number = GROUP_MEMBER_LIST_PAGE_SIZE
|
||||
): IGroupMemberListPagedSource {
|
||||
return kind === 'offset'
|
||||
? new NetworkOffsetGroupMemberListPagedSource(groupId, pageSize)
|
||||
: new NetworkCursorGroupMemberListPagedSource(groupId, pageSize);
|
||||
}
|
||||
|
||||
46
src/stores/groupMemberProfileResolver.ts
Normal file
46
src/stores/groupMemberProfileResolver.ts
Normal file
@@ -0,0 +1,46 @@
|
||||
import { GroupMemberResponse, UserDTO } from '../types/dto';
|
||||
import { getUserCache } from '../services/database';
|
||||
import { userManager } from './userManager';
|
||||
|
||||
/**
|
||||
* 为群成员列表补全用户资料(昵称、头像、用户名)。
|
||||
* cursor 接口仅返回 user_id 时,通过本地缓存和用户接口回填。
|
||||
*/
|
||||
export async function enrichGroupMembersWithUserProfiles(
|
||||
members: GroupMemberResponse[]
|
||||
): Promise<GroupMemberResponse[]> {
|
||||
if (!Array.isArray(members) || members.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const userIds = [...new Set(members.map((member) => String(member.user_id)))];
|
||||
const userMap = new Map<string, UserDTO>();
|
||||
|
||||
await Promise.all(
|
||||
userIds.map(async (userId) => {
|
||||
try {
|
||||
const cachedUser = await getUserCache(userId);
|
||||
if (cachedUser) {
|
||||
userMap.set(userId, cachedUser);
|
||||
return;
|
||||
}
|
||||
|
||||
const fetchedUser = await userManager.getUserById(userId);
|
||||
if (fetchedUser) {
|
||||
userMap.set(userId, fetchedUser);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('补全群成员用户资料失败:', error);
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
return members.map((member) => {
|
||||
if (member.user?.nickname || member.user?.username) {
|
||||
return member;
|
||||
}
|
||||
const user = userMap.get(String(member.user_id));
|
||||
return user ? { ...member, user } : member;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,6 +30,15 @@ export {
|
||||
SqliteConversationListPagedSource,
|
||||
createRemoteConversationListSource,
|
||||
} from './conversationListSources';
|
||||
export {
|
||||
GROUP_MEMBER_LIST_PAGE_SIZE,
|
||||
type GroupMemberListSourceKind,
|
||||
type IGroupMemberListPagedSource,
|
||||
NetworkCursorGroupMemberListPagedSource,
|
||||
NetworkOffsetGroupMemberListPagedSource,
|
||||
createRemoteGroupMemberListSource,
|
||||
} from './groupMemberListSources';
|
||||
export { enrichGroupMembersWithUserProfiles } from './groupMemberProfileResolver';
|
||||
export { postManager } from './postManager';
|
||||
export { groupManager } from './groupManager';
|
||||
export { userManager } from './userManager';
|
||||
|
||||
Reference in New Issue
Block a user