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.
This commit is contained in:
@@ -31,17 +31,23 @@ import {
|
||||
updateConversationCacheUnreadCount,
|
||||
} from './database';
|
||||
|
||||
/** 远端会话列表分页(offset / cursor)统一结果:拉取成功后均已执行本地缓存同步 */
|
||||
export interface RemoteConversationListPageResult {
|
||||
items: ConversationResponse[];
|
||||
hasMore: boolean;
|
||||
/** cursor 模式:下一页游标;offset 模式固定为 null */
|
||||
nextCursor: string | null;
|
||||
/** offset 模式:下一次 loadNext 应请求的页码;cursor 模式为 undefined */
|
||||
nextPage?: number;
|
||||
}
|
||||
|
||||
// 消息服务类
|
||||
class MessageService {
|
||||
private async refreshConversationsFromServer(
|
||||
page = 1,
|
||||
pageSize = 20
|
||||
): Promise<ConversationListResponse> {
|
||||
const response = await api.get<ConversationListResponse>('/conversations', {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
});
|
||||
const list = response.data.list || [];
|
||||
/** 与会话 offset 列表一致:将本页会话写入 SQLite,避免游标路径不落库导致冷启动未读与线上一致 */
|
||||
private async persistConversationListPageToLocalCache(
|
||||
list: ConversationResponse[]
|
||||
): Promise<void> {
|
||||
if (!list.length) return;
|
||||
await saveConversationsCache(list);
|
||||
await saveUsersCache(
|
||||
list.flatMap(conv => [
|
||||
@@ -54,9 +60,98 @@ class MessageService {
|
||||
.map(conv => conv.group)
|
||||
.filter((group): group is NonNullable<typeof group> => Boolean(group))
|
||||
);
|
||||
}
|
||||
|
||||
/** offset 会话列表单页:请求 + 落库 */
|
||||
private async requestOffsetConversationsPage(
|
||||
page: number,
|
||||
pageSize: number
|
||||
): Promise<ConversationListResponse> {
|
||||
const response = await api.get<ConversationListResponse>('/conversations', {
|
||||
page,
|
||||
page_size: pageSize,
|
||||
});
|
||||
const list = response.data.list || [];
|
||||
await this.persistConversationListPageToLocalCache(list);
|
||||
return response.data;
|
||||
}
|
||||
|
||||
/** cursor 会话列表单页:请求 + 落库 */
|
||||
private async requestCursorConversationsPage(
|
||||
params: CursorPaginationRequest
|
||||
): Promise<CursorPaginationResponse<ConversationResponse>> {
|
||||
try {
|
||||
const response = await api.get<CursorPaginationResponse<ConversationResponse>>(
|
||||
'/conversations/cursor',
|
||||
params
|
||||
);
|
||||
const raw = response.data;
|
||||
const list = Array.isArray(raw?.list) ? raw.list : [];
|
||||
await this.persistConversationListPageToLocalCache(list);
|
||||
return {
|
||||
list,
|
||||
next_cursor: raw?.next_cursor ?? null,
|
||||
prev_cursor: raw?.prev_cursor ?? null,
|
||||
has_more: raw?.has_more ?? false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取会话列表失败:', error);
|
||||
return {
|
||||
list: [],
|
||||
next_cursor: null,
|
||||
prev_cursor: null,
|
||||
has_more: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
private async refreshConversationsFromServer(
|
||||
page = 1,
|
||||
pageSize = 20
|
||||
): Promise<ConversationListResponse> {
|
||||
return this.requestOffsetConversationsPage(page, pageSize);
|
||||
}
|
||||
|
||||
/**
|
||||
* 远端会话列表单页(offset / cursor 统一入口)
|
||||
* 两种模式均先写 SQLite 再返回,行为与 NetworkRemoteConversationListPagedSource 配套。
|
||||
*/
|
||||
async fetchRemoteConversationListPage(args: {
|
||||
mode: 'offset' | 'cursor';
|
||||
pageSize: number;
|
||||
/** offset:本次页码,默认 1 */
|
||||
page?: number;
|
||||
/** cursor:本次游标;首屏不传或 null */
|
||||
cursor?: string | null;
|
||||
direction?: CursorPaginationRequest['direction'];
|
||||
}): Promise<RemoteConversationListPageResult> {
|
||||
if (args.mode === 'offset') {
|
||||
const page = args.page ?? 1;
|
||||
const data = await this.requestOffsetConversationsPage(page, args.pageSize);
|
||||
const totalPages = Math.max(0, data.total_pages ?? 0);
|
||||
const currentPage = data.page ?? page;
|
||||
const hasMore = totalPages > 0 && currentPage < totalPages;
|
||||
return {
|
||||
items: data.list || [],
|
||||
hasMore,
|
||||
nextCursor: null,
|
||||
nextPage: currentPage + 1,
|
||||
};
|
||||
}
|
||||
|
||||
const raw = await this.requestCursorConversationsPage({
|
||||
page_size: args.pageSize,
|
||||
...(args.cursor != null && args.cursor !== ''
|
||||
? { cursor: args.cursor }
|
||||
: {}),
|
||||
...(args.direction ? { direction: args.direction } : {}),
|
||||
});
|
||||
const items = raw.list || [];
|
||||
const nextCursor = raw.next_cursor ?? null;
|
||||
const hasMore = Boolean(raw.has_more && nextCursor != null);
|
||||
return { items, hasMore, nextCursor };
|
||||
}
|
||||
|
||||
private async refreshConversationDetailFromServer(id: string): Promise<ConversationDetailResponse> {
|
||||
const response = await api.get<ConversationDetailResponse>(
|
||||
`/conversations/${encodeURIComponent(id)}`
|
||||
@@ -433,8 +528,8 @@ class MessageService {
|
||||
await api.post(`/conversations/${encodeURIComponent(conversationId)}/read`, {
|
||||
last_read_seq: Number(seq),
|
||||
});
|
||||
// 立即清零本地缓存中的 unread_count,避免回到列表时仍显示旧红点
|
||||
updateConversationCacheUnreadCount(conversationId, 0).catch(error => {
|
||||
// 立即清零本地缓存中的 unread_count,并写入已读游标,避免冷启动仍显示旧红点
|
||||
updateConversationCacheUnreadCount(conversationId, 0, Number(seq)).catch(error => {
|
||||
console.error('更新本地会话未读数失败:', error);
|
||||
});
|
||||
}
|
||||
@@ -560,37 +655,6 @@ class MessageService {
|
||||
|
||||
// ==================== 游标分页方法 ====================
|
||||
|
||||
/**
|
||||
* 获取会话列表(游标分页)
|
||||
* GET /api/v1/conversations/cursor
|
||||
* @param params 游标分页请求参数
|
||||
*/
|
||||
async getConversationsCursor(
|
||||
params: CursorPaginationRequest = {}
|
||||
): Promise<CursorPaginationResponse<ConversationResponse>> {
|
||||
try {
|
||||
const response = await api.get<CursorPaginationResponse<ConversationResponse>>(
|
||||
'/conversations/cursor',
|
||||
params
|
||||
);
|
||||
const raw = response.data;
|
||||
return {
|
||||
list: Array.isArray(raw?.list) ? raw.list : [],
|
||||
next_cursor: raw?.next_cursor ?? null,
|
||||
prev_cursor: raw?.prev_cursor ?? null,
|
||||
has_more: raw?.has_more ?? false,
|
||||
};
|
||||
} catch (error) {
|
||||
console.error('获取会话列表失败:', error);
|
||||
return {
|
||||
list: [],
|
||||
next_cursor: null,
|
||||
prev_cursor: null,
|
||||
has_more: false,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取消息列表(游标分页)
|
||||
* GET /api/v1/conversations/:id/messages/cursor
|
||||
|
||||
Reference in New Issue
Block a user