Files
frontend/src/stores/conversationListSources.ts
lafay 90d834695f
All checks were successful
Frontend CI / build-and-push-web (push) Successful in 2m49s
Frontend CI / ota-android (push) Successful in 15m30s
Frontend CI / build-android-apk (push) Successful in 59m14s
refactor(ConversationList): streamline conversation list handling and enhance unread count management
- Removed debug display for unread count in ConversationListRow to clean up UI.
- Updated updateConversationCacheUnreadCount to optionally handle myLastReadSeq for better synchronization of read status.
- Refactored message service to unify offset and cursor pagination methods, improving data fetching consistency.
- Introduced a new method to normalize unread counts based on read cursor, ensuring accurate display of unread messages.
- Consolidated conversation list source implementations to simplify remote data fetching logic.
2026-03-25 04:07:48 +08:00

115 lines
3.4 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 会话列表数据源抽象游标、常规页码、SQLite 等实现同一契约MessageManager 只依赖接口。
*/
import { ConversationResponse } from '../types/dto';
import { messageService } from '../services/messageService';
import { getConversationListCache } from '../services/database';
export const CONVERSATION_LIST_PAGE_SIZE = 20;
/** 单次拉取结果(一页或一批) */
export interface ConversationListPage {
items: ConversationResponse[];
/** 在当前源上是否还能再 loadNext 一页 */
hasMore: boolean;
}
/**
* 分页式会话列表源restart 后开始新序列;首次 loadNext 为第一页,之后为后续页。
*/
export interface IConversationListPagedSource {
restart(): void;
loadNext(): Promise<ConversationListPage>;
/** 至少成功 loadNext 过一次且仍有下一页时为 true */
readonly hasMore: boolean;
}
/**
* 远端会话列表offset 与 cursor 共用实现)
* 均通过 messageService.fetchRemoteConversationListPage 拉取并落库,行为一致。
*/
export class NetworkRemoteConversationListPagedSource implements IConversationListPagedSource {
private nextPage = 1;
private nextCursor: string | null = null;
private hasMoreAfterLastLoad = false;
private loadedOnce = false;
private readonly pageSize: number;
private readonly mode: RemoteConversationListSourceKind;
constructor(
mode: RemoteConversationListSourceKind,
pageSize: number = CONVERSATION_LIST_PAGE_SIZE
) {
this.mode = mode;
this.pageSize = pageSize;
}
restart(): void {
this.nextPage = 1;
this.nextCursor = null;
this.hasMoreAfterLastLoad = false;
this.loadedOnce = false;
}
get hasMore(): boolean {
return this.loadedOnce && this.hasMoreAfterLastLoad;
}
async loadNext(): Promise<ConversationListPage> {
const result = await messageService.fetchRemoteConversationListPage({
mode: this.mode,
pageSize: this.pageSize,
...(this.mode === 'offset'
? { page: this.nextPage }
: { cursor: this.nextCursor }),
});
this.hasMoreAfterLastLoad = result.hasMore;
if (this.mode === 'offset') {
this.nextPage = result.nextPage ?? this.nextPage + 1;
} else {
this.nextCursor = result.nextCursor ?? null;
}
this.loadedOnce = true;
return { items: result.items, hasMore: result.hasMore };
}
}
/** SQLite 会话列表缓存(单批,无后续页) */
export class SqliteConversationListPagedSource implements IConversationListPagedSource {
private consumed = false;
restart(): void {
this.consumed = false;
}
get hasMore(): boolean {
return false;
}
async loadNext(): Promise<ConversationListPage> {
if (this.consumed) {
return { items: [], hasMore: false };
}
this.consumed = true;
try {
const items = await getConversationListCache();
return { items, hasMore: false };
} catch (e) {
console.warn('[SqliteConversationListPagedSource] 读取会话列表缓存失败:', e);
return { items: [], hasMore: false };
}
}
}
/** 远端会话列表分页策略 */
export type RemoteConversationListSourceKind = 'cursor' | 'offset';
export function createRemoteConversationListSource(
kind: RemoteConversationListSourceKind,
pageSize: number = CONVERSATION_LIST_PAGE_SIZE
): IConversationListPagedSource {
return new NetworkRemoteConversationListPagedSource(kind, pageSize);
}