build(config): update bundle identifier and add signing plugin
feat(notification): improve JPush integration and message sync reliability - Update iOS bundle identifier to `cn.qczlit.weiyou` - Add `withSigning` Expo plugin - Enhance `withJPush` plugin with improved Swift AppDelegate injection logic - Update JPush default channel to `developer-default` - Optimize `MessageSyncService` to prevent race conditions in unread count updates - Implement promise deduplication for `fetchUnreadCount` to prevent redundant network requests - Add `setConversationsWithUnread` to `useMessageStore` for atomic state updates of conversations and unread counts
This commit is contained in:
@@ -26,7 +26,7 @@ try {
|
||||
const Constants = require('expo-constants').default;
|
||||
const extra = Constants?.expoConfig?.extra || Constants?.extra || {};
|
||||
jpushAppKey = extra.jpushAppKey || '';
|
||||
jpushChannel = extra.jpushChannel || 'withyou-default';
|
||||
jpushChannel = extra.jpushChannel || 'developer-default';
|
||||
} else {
|
||||
console.warn('[JPush] native module not linked, skipping');
|
||||
}
|
||||
|
||||
@@ -35,6 +35,8 @@ export class MessageSyncService implements IMessageSyncService {
|
||||
private loadingMoreConversations = false;
|
||||
/** 聊天页活跃期间延迟的会话列表刷新 */
|
||||
private deferredConversationRefresh = false;
|
||||
/** fetchUnreadCount 去重:复用 in-flight promise */
|
||||
private fetchUnreadCountPromise: Promise<void> | null = null;
|
||||
|
||||
constructor(
|
||||
getCurrentUserId: () => string | null,
|
||||
@@ -108,15 +110,12 @@ export class MessageSyncService implements IMessageSyncService {
|
||||
newConversations.set(normalizedConv.id, normalizedConv);
|
||||
});
|
||||
|
||||
// 使用 store.setConversations 会自动排序并更新 conversationList
|
||||
store.setConversations(newConversations);
|
||||
|
||||
// 计算并更新未读数
|
||||
// 合并更新:conversations + totalUnread 在同一次 set 中完成,消除中间状态窗口
|
||||
const totalUnread = Array.from(newConversations.values()).reduce(
|
||||
(sum, conv) => sum + (conv.unread_count || 0),
|
||||
0
|
||||
0,
|
||||
);
|
||||
store.setUnreadCount(totalUnread, store.getUnreadCount().system);
|
||||
store.setConversationsWithUnread(newConversations, totalUnread, store.getUnreadCount().system);
|
||||
|
||||
this.persistConversationListCache();
|
||||
} catch (error) {
|
||||
@@ -360,11 +359,23 @@ export class MessageSyncService implements IMessageSyncService {
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取未读数
|
||||
* 获取未读数(去重:复用 in-flight promise)
|
||||
*/
|
||||
async fetchUnreadCount(): Promise<void> {
|
||||
if (this.fetchUnreadCountPromise) {
|
||||
return this.fetchUnreadCountPromise;
|
||||
}
|
||||
this.fetchUnreadCountPromise = this.doFetchUnreadCount();
|
||||
try {
|
||||
await this.fetchUnreadCountPromise;
|
||||
} finally {
|
||||
this.fetchUnreadCountPromise = null;
|
||||
}
|
||||
}
|
||||
|
||||
private async doFetchUnreadCount(): Promise<void> {
|
||||
const store = useMessageStore.getState();
|
||||
|
||||
|
||||
try {
|
||||
const [unreadData, systemUnreadData] = await Promise.all([
|
||||
messageService.getUnreadCount(),
|
||||
@@ -388,21 +399,28 @@ export class MessageSyncService implements IMessageSyncService {
|
||||
}
|
||||
}
|
||||
|
||||
// 服务端汇总未读为 0 时,清掉内存中残留的红点
|
||||
// 服务端汇总未读为 0 时,仅在本地也一致时才清零(避免缓存竞态导致的抖动)
|
||||
if (totalUnread === 0) {
|
||||
const currentConversations = store.getState().conversations;
|
||||
let anyCleared = false;
|
||||
const newConversations = new Map(currentConversations);
|
||||
for (const [cid, conv] of newConversations) {
|
||||
if ((conv.unread_count || 0) > 0) {
|
||||
newConversations.set(cid, { ...conv, unread_count: 0 });
|
||||
anyCleared = true;
|
||||
const localSum = Array.from(store.getState().conversations.values())
|
||||
.reduce((sum, conv) => sum + (conv.unread_count || 0), 0);
|
||||
if (localSum > 0) {
|
||||
// 本地有未读但服务端返回 0 → 可能是后端缓存竞态,保留本地状态
|
||||
// 下一次 fetchConversations 会提供权威的 per-conversation 数据
|
||||
} else {
|
||||
// 本地也无未读,确认清零
|
||||
const currentConversations = store.getState().conversations;
|
||||
let anyCleared = false;
|
||||
const newConversations = new Map(currentConversations);
|
||||
for (const [cid, conv] of newConversations) {
|
||||
if ((conv.unread_count || 0) > 0) {
|
||||
newConversations.set(cid, { ...conv, unread_count: 0 });
|
||||
anyCleared = true;
|
||||
}
|
||||
}
|
||||
if (anyCleared) {
|
||||
store.setConversations(newConversations);
|
||||
this.persistConversationListCache();
|
||||
}
|
||||
}
|
||||
if (anyCleared) {
|
||||
// 使用 zustand set 函数正确更新状态
|
||||
store.setConversations(newConversations);
|
||||
this.persistConversationListCache();
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -487,15 +505,12 @@ export class MessageSyncService implements IMessageSyncService {
|
||||
newConversations.set(normalizedConv.id, normalizedConv);
|
||||
});
|
||||
|
||||
// 使用 store.setConversations 会自动排序并更新 conversationList
|
||||
store.setConversations(newConversations);
|
||||
|
||||
// 计算并更新未读数
|
||||
// 合并更新:conversations + totalUnread 在同一次 set 中完成
|
||||
const totalUnread = Array.from(newConversations.values()).reduce(
|
||||
(sum, conv) => sum + (conv.unread_count || 0),
|
||||
0
|
||||
0,
|
||||
);
|
||||
store.setUnreadCount(totalUnread, store.getUnreadCount().system);
|
||||
store.setConversationsWithUnread(newConversations, totalUnread, store.getUnreadCount().system);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -79,6 +79,11 @@ export interface MessageActions {
|
||||
|
||||
// 设置状态
|
||||
setConversations: (conversations: Map<string, ConversationResponse>) => void;
|
||||
setConversationsWithUnread: (
|
||||
conversations: Map<string, ConversationResponse>,
|
||||
totalUnread: number,
|
||||
systemUnread: number,
|
||||
) => void;
|
||||
updateConversation: (conversation: ConversationResponse) => void;
|
||||
removeConversation: (conversationId: string) => void;
|
||||
setMessages: (conversationId: string, messages: MessageResponse[]) => void;
|
||||
@@ -255,6 +260,27 @@ export const useMessageStore = create<MessageStore>((set, get) => ({
|
||||
}));
|
||||
},
|
||||
|
||||
setConversationsWithUnread: (
|
||||
conversations: Map<string, ConversationResponse>,
|
||||
totalUnread: number,
|
||||
systemUnread: number,
|
||||
) => {
|
||||
const conversationList = sortConversationList(conversations);
|
||||
const notificationMutedMap = new Map<string, boolean>();
|
||||
conversations.forEach((conv, id) => {
|
||||
if (conv.notification_muted) {
|
||||
notificationMutedMap.set(id, true);
|
||||
}
|
||||
});
|
||||
set(state => ({
|
||||
conversations,
|
||||
conversationList,
|
||||
notificationMutedMap: new Map([...state.notificationMutedMap, ...notificationMutedMap]),
|
||||
totalUnreadCount: totalUnread,
|
||||
systemUnreadCount: systemUnread,
|
||||
}));
|
||||
},
|
||||
|
||||
updateConversation: (conversation: ConversationResponse) => {
|
||||
const id = normalizeConversationId(conversation.id);
|
||||
set(state => {
|
||||
|
||||
Reference in New Issue
Block a user