Files
frontend/src/stores/groupListSources.ts

302 lines
7.8 KiB
TypeScript
Raw Normal View History

/**
* GroupManager
*/
import {
GroupResponse,
GroupMemberResponse,
GroupListResponse,
GroupMemberListResponse,
CursorPaginationRequest,
} from '../types/dto';
import { groupService } from '../services/groupService';
import { groupCacheRepository } from '@/database';
export const GROUP_LIST_PAGE_SIZE = 20;
export const GROUP_MEMBER_LIST_PAGE_SIZE = 50;
// ==================== 群组列表数据源 ====================
/** 单次拉取结果(一页或一批) */
export interface GroupListPage {
items: GroupResponse[];
/** 在当前源上是否还能再 loadNext 一页 */
hasMore: boolean;
}
/**
* restart loadNext
*/
export interface IGroupListPagedSource {
restart(): void;
loadNext(): Promise<GroupListPage>;
/** 至少成功 loadNext 过一次且仍有下一页时为 true */
readonly hasMore: boolean;
}
/**
*
*/
export class NetworkCursorGroupListPagedSource implements IGroupListPagedSource {
private nextCursor: string | null = null;
private hasMoreAfterLastLoad = false;
private loadedOnce = false;
private readonly pageSize: number;
constructor(pageSize: number = GROUP_LIST_PAGE_SIZE) {
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<GroupListPage> {
const params: CursorPaginationRequest = {
cursor: this.nextCursor ?? '',
page_size: this.pageSize,
};
const result = await groupService.getGroupsCursor(params);
this.hasMoreAfterLastLoad = result.has_more ?? false;
this.nextCursor = result.next_cursor ?? null;
this.loadedOnce = true;
return { items: result.list ?? [], hasMore: this.hasMoreAfterLastLoad };
}
}
/**
*
*/
export class NetworkOffsetGroupListPagedSource implements IGroupListPagedSource {
private nextPage = 1;
private hasMoreAfterLastLoad = false;
private loadedOnce = false;
private readonly pageSize: number;
constructor(pageSize: number = GROUP_LIST_PAGE_SIZE) {
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<GroupListPage> {
const result = await groupService.getGroups(this.nextPage, this.pageSize);
const items = result.list ?? [];
const totalPages = result.total_pages ?? 1;
this.hasMoreAfterLastLoad = this.nextPage < totalPages;
this.nextPage++;
this.loadedOnce = true;
return { items, hasMore: this.hasMoreAfterLastLoad };
}
}
/**
* SQLite
*/
export class SqliteGroupListPagedSource implements IGroupListPagedSource {
private consumed = false;
restart(): void {
this.consumed = false;
}
get hasMore(): boolean {
return false;
}
async loadNext(): Promise<GroupListPage> {
if (this.consumed) {
return { items: [], hasMore: false };
}
this.consumed = true;
try {
const items = await groupCacheRepository.getAll();
return { items, hasMore: false };
} catch (e) {
console.warn('[SqliteGroupListPagedSource] 读取群组列表缓存失败:', e);
return { items: [], hasMore: false };
}
}
}
// ==================== 群组成员列表数据源 ====================
/** 成员列表单次拉取结果 */
export interface GroupMemberListPage {
items: GroupMemberResponse[];
hasMore: boolean;
}
/**
*
*/
export interface IGroupMemberListPagedSource {
restart(): void;
loadNext(): Promise<GroupMemberListPage>;
readonly hasMore: boolean;
}
/**
*
*/
export class NetworkCursorGroupMemberListPagedSource implements IGroupMemberListPagedSource {
private nextCursor: string | null = null;
private hasMoreAfterLastLoad = false;
private loadedOnce = false;
private readonly pageSize: number;
private readonly groupId: string;
constructor(groupId: 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<GroupMemberListPage> {
const params: CursorPaginationRequest = {
cursor: this.nextCursor ?? '',
page_size: this.pageSize,
};
const result = await groupService.getGroupMembersCursor(this.groupId, params);
this.hasMoreAfterLastLoad = result.has_more ?? false;
this.nextCursor = result.next_cursor ?? null;
this.loadedOnce = true;
return { items: result.list ?? [], hasMore: this.hasMoreAfterLastLoad };
}
}
/**
*
*/
export class NetworkOffsetGroupMemberListPagedSource implements IGroupMemberListPagedSource {
private nextPage = 1;
private hasMoreAfterLastLoad = false;
private loadedOnce = false;
private readonly pageSize: number;
private readonly groupId: string;
constructor(groupId: 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<GroupMemberListPage> {
const result = await groupService.getMembers(
this.groupId,
this.nextPage,
this.pageSize
);
const items = result.list ?? [];
const totalPages = result.total_pages ?? 1;
this.hasMoreAfterLastLoad = this.nextPage < totalPages;
this.nextPage++;
this.loadedOnce = true;
return { items, hasMore: this.hasMoreAfterLastLoad };
}
}
/**
* SQLite
*/
export class SqliteGroupMemberListPagedSource implements IGroupMemberListPagedSource {
private consumed = false;
private readonly groupId: string;
constructor(groupId: string) {
this.groupId = groupId;
}
restart(): void {
this.consumed = false;
}
get hasMore(): boolean {
return false;
}
async loadNext(): Promise<GroupMemberListPage> {
if (this.consumed) {
return { items: [], hasMore: false };
}
this.consumed = true;
try {
const items = await groupCacheRepository.getMembers(this.groupId);
return { items, hasMore: false };
} catch (e) {
console.warn('[SqliteGroupMemberListPagedSource] 读取成员列表缓存失败:', e);
return { items: [], hasMore: false };
}
}
}
// ==================== 工厂函数 ====================
/** 远端群组列表分页策略 */
export type RemoteGroupListSourceKind = 'cursor' | 'offset';
export function createRemoteGroupListSource(
kind: RemoteGroupListSourceKind = 'cursor',
pageSize: number = GROUP_LIST_PAGE_SIZE
): IGroupListPagedSource {
if (kind === 'offset') {
return new NetworkOffsetGroupListPagedSource(pageSize);
}
return new NetworkCursorGroupListPagedSource(pageSize);
}
export function createRemoteGroupMemberListSource(
groupId: string,
kind: RemoteGroupListSourceKind = 'cursor',
pageSize: number = GROUP_MEMBER_LIST_PAGE_SIZE
): IGroupMemberListPagedSource {
if (kind === 'offset') {
return new NetworkOffsetGroupMemberListPagedSource(groupId, pageSize);
}
return new NetworkCursorGroupMemberListPagedSource(groupId, pageSize);
}