import { uilog } from '../const'; import { ConversationStorage } from '../db/storage'; import { ErrorCode, UIKitError } from '../error'; import { CHAT_VERSION, ChatClient, ChatContact, ChatConversation, ChatConversationType, ChatCursorResult, ChatError, ChatGroup, ChatGroupOptions, ChatGroupStyle, chatlog, ChatMessage, ChatMessageReaction, type ChatMessageStatusCallback, ChatMessageThread, ChatMessageType, ChatOptions, ChatPresence, ChatPushRemindType, ChatSearchDirection, ChatSilentModeParamType, ChatUserInfo, } from '../rename.chat'; import { Services } from '../services'; import { asyncTask, getCurTs, mergeObjects } from '../utils'; import { ChatServiceListenerImpl } from './chat.listener'; import { gGroupMemberMyRemark } from './const'; import type { DataProfileProvider } from './DataProfileProvider'; import type { MessageCacheManager } from './messageManager.types'; import type { RequestList } from './requestList.types'; import type { ChatEventType, ChatOptionsType, ChatService, ChatServiceListener, ConversationServices, DataModel, DataModelType, ResultCallback, ResultValue, UserData, UserFrom, } from './types'; import { type BlockModel, type ContactModel, type ConversationModel, type GroupModel, type GroupParticipantModel, type StateModel, type UIListener, UIListenerType, } from './types.ui'; import { PresenceUtil, setUserInfoToMessage, userInfoFromMessage, } from './utils'; export class ChatServiceImpl extends ChatServiceListenerImpl implements ChatService, ConversationServices { _user?: UserData; _dataFileProvider: DataProfileProvider; _userList: Map; _convStorage?: ConversationStorage; _convList: Map; _contactList: Map; _blockList: Map; _groupList: Map; _groupMemberList: Map>; _request: RequestList; _messageManager: MessageCacheManager; _modelState: Map>; _currentConversation?: ConversationModel; _silentModeList: Map; _pinMessageList: Map; _groupNameOnCreateGroupCallback?: (params: { selected: ContactModel[]; }) => string; constructor() { uilog.log('chat:constructor:'); chatlog.tag = '[chat]'; super(); this._userList = new Map(); this._convList = new Map(); this._contactList = new Map(); this._blockList = new Map(); this._groupList = new Map(); this._groupMemberList = new Map(); this._modelState = new Map(); this._silentModeList = new Map(); this._pinMessageList = new Map(); // ✅ 正确:使用 new 来实例化类 this._request = new (require('./requestList').RequestListImpl)(this); this._messageManager = new (require('./messageManager').MessageCacheManagerImpl)(this); this._dataFileProvider = new (require('./DataProfileProvider').DataProfileProvider)(); } // !!! warning: no need // destructor() { // } reset(): void { uilog.log('chat:reset:'); // this.clearListener(); // !!! warn: no clear. this._userList.clear(); this._convList.clear(); this._blockList.clear(); this._contactList.clear(); this._groupList.clear(); this._groupMemberList.clear(); this._modelState.clear(); this._silentModeList.clear(); this._pinMessageList.clear(); } id(): string { return this.client.options?.appKey && this.client.options?.appKey.length > 0 ? this.client.options?.appKey : (this.client.options?.appId ?? ''); } get dataFileProvider(): DataProfileProvider { return this._dataFileProvider; } async init(params: { options: ChatOptionsType; result?: (params: { isOk: boolean; error?: UIKitError }) => void; }): Promise { uilog.log('chat:init:', params.options); const { options } = params; try { const isExistedAppKey = options.hasOwnProperty('appKey'); const isExistedAppId = options.hasOwnProperty('appId'); console.log('dev:isExistedAppKey:', isExistedAppKey); console.log('dev:isExistedAppId:', isExistedAppId); if (isExistedAppKey === false && isExistedAppId === false) { params.result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.init_error, desc: 'appKey or appId is required.', }), }); return; } const appKey = (options as any).appKey; const appId = (options as any).appId; if (isExistedAppKey === true && appKey && appKey.length > 0) { await this.client.init(ChatOptions.withAppKey({ ...(options as any) })); } else if (isExistedAppId === true && appId && appId.length > 0) { await this.client.init(ChatOptions.withAppId({ ...(options as any) })); } else { params.result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.init_error, desc: 'appKey or appId is required.', }), }); return; } uilog.log('chat:opt:', this.client.options); this._convStorage = new ConversationStorage({ appKey: this.id(), }); // !!! hot-reload no pass, into catch codes // this._request = new RequestListImpl(this); // this._messageManager = new MessageCacheManagerImpl(this); this._initListener(); this._request.init(); this._messageManager.init(); this._dataFileProvider.clearDataList(); params.result?.({ isOk: true }); } catch (error) { params.result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.init_error, desc: this._fromChatError(error), }), }); } } addListener(listener: ChatServiceListener): void { super.addListener(listener); } removeListener(listener: ChatServiceListener): void { super.removeListener(listener); } clearListener(): void { super.clearListener(); } addUIListener(listener: UIListener): void { super.addUIListener(listener); } removeUIListener(listener: UIListener): void { super.removeUIListener(listener); } clearUIListener(): void { super.clearUIListener(); } sendUIEvent( type: UIListenerType, event: keyof UIListener, data?: DataModel | string, ...args: DataModel[] ): void { super.sendUIEvent(type, event, data, ...args); } _fromChatError(error: any): string | undefined { let e: string | undefined; try { e = JSON.stringify(error); } catch (ee) { if (typeof error === 'string') { e = error; } else { e = ee?.toString?.(); } } return e; } async _createUserDir(): Promise { try { const isExisted = await Services.dcs.isExistedUserDir(); if (isExisted !== true) { await Services.dcs.createUserDir(); } } catch (e) { uilog.warn('createUserDir:', e); this.sendError({ error: new UIKitError({ code: ErrorCode.chat_uikit, desc: 'createUserDir failed.', }), }); } } get client(): ChatClient { return super.client; } async login(params: { userId: string; userToken: string; userName: string; userAvatarURL?: string | undefined; usePassword?: boolean; result: (params: { isOk: boolean; error?: UIKitError }) => void; }): Promise { const { userId, userToken, userName, userAvatarURL, result, usePassword } = params; try { uilog.log('chat:login:', params); this.reset(); const version = CHAT_VERSION; const list = version.split('.'); const major = parseInt(list[0]!, 10); const minor = parseInt(list[1]!, 10); if (major <= 1 && minor < 3) { if (userToken.startsWith('00')) { await this.client.loginWithAgoraToken(userId, userToken); } else { await this.client.login(userId, userToken, usePassword ?? false); } } else { await this.client.login(userId, userToken, usePassword ?? false); } this._convStorage?.setCurrentId(userId); // !!! hot-reload no pass, into catch codes this._user = { userName: userName, remark: userName, avatarURL: userAvatarURL, userId: userId, } as UserData; Services.dcs.init(`${this.id().replace('#', '-')}/${userId}`); await this._createUserDir(); this.client.getCurrentUsername(); // this.updateSelfInfo({ self: this._user, onResult: () => {} }); uilog.log('login:finish:1', params); result?.({ isOk: true }); this.sendFinished({ event: 'login', extra: { isOk: true } }); } catch (error: any) { if (error?.code === 200) { this._convStorage?.setCurrentId(userId); // !!! for dev hot-reload this._user = { userName: userName, remark: userName, avatarURL: userAvatarURL, userId: userId, } as UserData; Services.dcs.init(`${this.id().replace('#', '-')}/${userId}`); await this._createUserDir(); this.client.getCurrentUsername(); // this.updateSelfInfo({ self: this._user, onResult: () => {} }); } uilog.log('login:finish:2', params, error); result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.login_error, desc: this._fromChatError(error), }), }); this.sendFinished({ event: 'login', extra: { isOk: false } }); } } async logout(params: { unbindDeviceToken?: boolean; result?: (params: { isOk: boolean; error?: UIKitError }) => void; }): Promise { try { uilog.log('chat:logout:'); await this.client.logout(params.unbindDeviceToken); params.result?.({ isOk: true }); this._user = undefined; this.reset(); this.sendFinished({ event: 'logout', extra: { isOk: true } }); } catch (error) { params.result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.logout_error, desc: this._fromChatError(error), }), }); this.sendFinished({ event: 'logout', extra: { isOk: false } }); } } async autoLogin(params: { userName?: string; userAvatarURL?: string; result: (params: { isOk: boolean; error?: UIKitError }) => void; }): Promise { if (this.client.options?.autoLogin !== true) { params.result?.({ isOk: false }); this.sendFinished({ event: 'autoLogin', extra: { isOk: false } }); return; } this.tryCatch({ promise: this.client.isLoginBefore(), event: 'autoLogin', onFinished: async (value) => { if (value === true) { const userId = await this.client.getCurrentUsername(); this._convStorage?.setCurrentId(userId); this._user = { userName: params.userName, remark: params.userName, avatarURL: params.userAvatarURL, userId: userId, } as UserData; Services.dcs.init(`${this.id().replace('#', '-')}/${userId}`); await this._createUserDir(); this.client.getCurrentUsername(); params.result?.({ isOk: true }); this.sendFinished({ event: 'autoLogin', extra: { isOk: true } }); } else { params.result?.({ isOk: false }); this.sendFinished({ event: 'autoLogin', extra: { isOk: false } }); } }, onError: (e) => { params.result?.({ isOk: false, error: e, }); }, }); } async loginState(): Promise<'logged' | 'noLogged'> { const r = await this.client.isLoginBefore(); return r === true ? 'logged' : 'noLogged'; } refreshToken(params: { token: string; result?: (params: { isOk: boolean; error?: UIKitError }) => void; }): void { this.tryCatch({ promise: this.client.renewAgoraToken(params.token), event: 'refreshToken', onFinished: () => { params?.result?.({ isOk: true }); }, onError: (error) => { params.result?.({ isOk: false, error: new UIKitError({ code: ErrorCode.refresh_token_error, desc: this._fromChatError(error), }), }); }, }); } checkTokenIsExpired(params: { onResult: (isExpired: boolean) => void; }): void { this.client.userManager .fetchUserInfoById(['test']) .then(() => { params.onResult?.(false); }) .catch((e: ChatError) => { if (e.code === 401) { params.onResult?.(true); } else { params.onResult?.(false); } }); } get userId(): string | undefined { return this.client.currentUserName as string | undefined; } user(userId?: string): UserData | undefined { if (this._user?.userId === userId) { return this._user; } else if (userId) { return this._userList.get(userId); } return undefined; } setUser(params: { users: UserData[] }): void { params.users.forEach((v) => { if (v.userId === this._user?.userId && v.userId) { this._user = { ...this._user, ...v }; } else { this._userList.set(v.userId, v); } }); } sendError(params: { error: UIKitError; from?: string; extra?: any }): void { this.listeners.forEach((v) => { asyncTask(() => v.onError?.(params)); }); } sendFinished(params: { event: ChatEventType; extra?: any }): void { this.listeners.forEach((v) => { asyncTask(() => v.onFinished?.(params)); }); } sendBefore(params: { event: ChatEventType; extra?: any }): void { this.listeners.forEach((v) => { asyncTask(() => v.onBefore?.(params)); }); } get requestList(): RequestList { return this._request!; } get messageManager(): MessageCacheManager { return this._messageManager!; } tryCatch(params: { promise: Promise; event: string; onFinished?: | ((value: T) => Promise | void | boolean) | undefined; onError?: ((e: UIKitError) => void | boolean) | undefined; }): void { const { promise, event, onFinished, onError } = params; this.sendBefore({ event: event }); promise .then(async (value: T) => { const ret = await onFinished?.(value); if (ret !== false) { this.sendFinished({ event: event }); } }) .catch((e) => { const isErrorObject = e instanceof UIKitError; let _e = e; if (isErrorObject === false) { _e = new UIKitError({ code: ErrorCode.chat_uikit, tag: event, desc: this._fromChatError(e), }); } const ret = onError?.(_e); if (ret !== false) { this.sendError({ error: _e, from: event, }); } }); } async tryCatchSync(params: { promise: Promise; event: string; }): Promise { const { promise, event } = params; try { return await promise; } catch (error) { throw new UIKitError({ code: ErrorCode.chat_uikit, tag: event, desc: this._fromChatError(error), }); } } async tryCatchSyncList(params: { promises: Promise[]; event: string }) { const { promises, event } = params; try { return await Promise.all(promises); } catch (error) { throw new UIKitError({ code: ErrorCode.chat_uikit, tag: event, desc: this._fromChatError(error), }); } } toUserData( user: ChatUserInfo, from?: { type: UserFrom; groupId?: string; } ): UserData { return { userId: user.userId, userName: user.nickName, remark: user.nickName, avatarURL: user.avatarUrl, from: from, }; } _getAvatarFromCache(id: string): string | undefined { return this._dataFileProvider.getDataById(id)?.avatar; } _getNameFromCache(id: string): string | undefined { return this._dataFileProvider.getDataById(id)?.name; } _getRemarkFromCache(id: string): string | undefined { return this._dataFileProvider.getDataById(id)?.remark; } _getDoNotDisturbFromCache(convId: string) { return this._silentModeList.get(convId)?.doNotDisturb; } async toUIConversation(conv: ChatConversation): Promise { const name = conv.convType === ChatConversationType.PeerChat ? (this._getRemarkFromCache(conv.convId) ?? this._getNameFromCache(conv.convId)) : this._getNameFromCache(conv.convId); return { convId: conv.convId, convType: conv.convType, isChatThread: conv.isChatThread, ext: conv.ext, isPinned: conv.isPinned, pinnedTime: conv.pinnedTime, unreadMessageCount: await this.getConversationMessageCount( conv.convId, conv.convType ), lastMessage: await this.getConversationLatestMessage( conv.convId, conv.convType ), doNotDisturb: this._getDoNotDisturbFromCache(conv.convId), convName: name, convAvatar: this._getAvatarFromCache(conv.convId), } as ConversationModel; } toUIContact(contact: ChatContact): ContactModel { const others = this._contactList.get(contact.userId); return { ...others, userId: contact.userId, remark: this._getRemarkFromCache(contact.userId) ?? contact.remark, userAvatar: this._getAvatarFromCache(contact.userId), userName: this._getNameFromCache(contact.userId), }; } toUIGroup(group: ChatGroup): GroupModel { const others = this._groupList.get(group.groupId); return { ...others, ...group, groupAvatar: this._getAvatarFromCache(group.groupId), groupName: this._getNameFromCache(group.groupId) ?? group.groupName, }; } setGroupNameOnCreateGroup( callback: (params: { selected: ContactModel[] }) => string ): void { this._groupNameOnCreateGroupCallback = callback; } getCreateGroupCustomNameCallback(): | ((params: { selected: ContactModel[] }) => string) | undefined { return this._groupNameOnCreateGroupCallback; } updateDataList(params: { dataList: Map; isUpdateNotExisted?: boolean; disableDispatch?: boolean; dispatchHandler?: (data: Map) => boolean; }): void { this._updateDataList(params); } getDataModel(id: string): DataModel | undefined { return this._dataFileProvider.getDataById(id); } getDataFileProvider(): DataProfileProvider { return this._dataFileProvider; } async _requestConvData(list: ChatConversation[]): Promise { const tmp = new Map(); list.forEach((v) => { tmp.set( v.convId, v.convType === ChatConversationType.GroupChat ? 'group' : 'user' ); }); await this._dataFileProvider.requestDataList({ dataList: tmp, disableDispatch: true, requestHasData: false, isUpdateNotExisted: true, }); } async _requestData(params: { list: string[]; type: DataModelType; disableDispatch?: boolean; requestHasData: boolean; isUpdateNotExisted?: boolean; }): Promise { const { list, type = 'user', requestHasData = false, disableDispatch = true, isUpdateNotExisted = false, } = params; const tmp = new Map(); list.forEach((v) => { tmp.set(v, type); }); await this._dataFileProvider.requestDataList({ dataList: tmp, disableDispatch: disableDispatch, requestHasData: requestHasData, isUpdateNotExisted: isUpdateNotExisted, }); } _updateDataList(params: { dataList: Map; isUpdateNotExisted?: boolean | undefined; disableDispatch?: boolean | undefined; dispatchHandler?: (data: Map) => boolean; }): void { this._dataFileProvider.updateDataList(params); } setCurrentConversation(params: { conv?: ConversationModel }): void { if (params.conv && params.conv.convId) { const conv = this._convList.get(params.conv.convId); if (conv) { this._currentConversation = conv; } else { this._currentConversation = params.conv; } } else { this._currentConversation = params.conv; } } getCurrentConversation(): ConversationModel | undefined { return this._currentConversation; } /** * @description Get the current user all conversation list. * @params params * - onResult: The callback function of the result. */ async getAllConversations(params: { onResult: ResultCallback; }): Promise { const { onResult } = params; try { const map = new Map(); const isFinished = await this._convStorage?.isFinishedForFetchList(); if (isFinished === true) { const list = await this.client.chatManager.getAllConversations(); const list2 = await this._convStorage?.getAllConversation(); if (list2 && list2?.length > 0) { list2?.forEach((v) => { this._silentModeList.set(v.convId, { convId: v.convId, doNotDisturb: v.doNotDisturb, }); }); } try { await this._requestConvData(list); } catch (e) { uilog.warn('dev:getAllConversations:requestConvData:', e); } this._convList.clear(); const ret = list.map(async (v) => { const conv = await this.toUIConversation(v); this._convList.set(v.convId, conv); }); await Promise.all(ret); } else { let cursor = ''; const pageSize = 50; const pageSize2 = 20; const pinList = await this.client.chatManager.fetchPinnedConversationsFromServerWithCursor( cursor, pageSize ); pinList.list?.forEach((v) => { map.set(v.convId, { ...v, } as ChatConversation); }); cursor = ''; for (;;) { const list = await this.client.chatManager.fetchConversationsFromServerWithCursor( cursor, pageSize2 ); list.list?.forEach((v) => { const item = map.get(v.convId); if (item) { map.set(v.convId, { ...item, ...v, } as ChatConversation); } else { map.set(v.convId, { ...v, } as ChatConversation); } }); if ( list.cursor.length === 0 || (list.list && list.list?.length < pageSize) || list.list === undefined ) { break; } cursor = list.cursor; } if (map.size > 0) { const reqList = Array.from(map.values()).map((v) => { return { convId: v.convId, convType: v.convType, } as any; }); let index = 0; while (index < reqList.length) { const subReqList = reqList.slice(index, index + pageSize2); index += pageSize2; const silentList = await this.client.pushManager.fetchSilentModeForConversations( subReqList ); silentList.forEach((v) => { this._silentModeList.set(v.conversationId, { convId: v.conversationId, doNotDisturb: v.remindType === ChatPushRemindType.MENTION_ONLY || v.remindType === ChatPushRemindType.NONE, }); }); } if (this._silentModeList.size > 0) { await this._convStorage?.setAllConversation( Array.from(this._silentModeList.values()).map((item) => { return { convId: item.convId, doNotDisturb: item.doNotDisturb, } as ConversationModel; }) ); } } await this._convStorage?.setFinishedForFetchList(true); try { await this._requestConvData(Array.from(map.values())); } catch (e) { uilog.warn('dev:getAllConversations:requestConvData:', e); } const ret = Array.from(map.values()).map(async (v) => { const conv = await this.toUIConversation(v); this._convList.set(conv.convId, conv); return conv; }); await Promise.all(ret); } this.sendFinished({ event: 'getAllConversations', extra: { isOk: true }, }); onResult({ isOk: true, value: Array.from(this._convList.values()), }); } catch (e) { this.sendError({ error: new UIKitError({ code: ErrorCode.chat_uikit, desc: 'getAllConversations failed.', }), }); onResult({ isOk: false, error: new UIKitError({ code: ErrorCode.get_all_conversations_error, desc: this._fromChatError(e), }), }); } } async getConversation(params: { convId: string; convType: ChatConversationType; createIfNotExist?: boolean; fromNative?: boolean; isChatThread?: boolean; }): Promise { const { fromNative = false, createIfNotExist } = params; if (fromNative === true) { const ret = await this.tryCatchSync({ promise: this.client.chatManager.getConversation( params.convId, params.convType, params.createIfNotExist ?? true, params.isChatThread ?? false ), event: 'getConversation', }); if (ret) { try { await this._requestConvData([ { convId: params.convId, convType: params.convType, } as ChatConversation, ]); } catch (e) { uilog.warn('dev:getConversation:', e); } const c1 = await this.toUIConversation(ret); const c2 = this._convList.get(params.convId); if (c2) { const conv = mergeObjects(c1, c2); this._convList.set(conv.convId, conv); return conv; } else { this._convList.set(c1.convId, c1); if (params.isChatThread !== true) { this.sendUIEvent(UIListenerType.Conversation, 'onAddedEvent', c1); } } return c1; } } else { if (createIfNotExist === true) { const isExisted = this._convList.get(params.convId); if (isExisted) { return isExisted; } else { try { await this._requestConvData([ { convId: params.convId, convType: params.convType, } as ChatConversation, ]); } catch (e) { uilog.warn('dev:getConversation:', e); } const conv = await this.toUIConversation({ convId: params.convId, convType: params.convType, } as ChatConversation); this._convList.set(conv.convId, conv); if (params.isChatThread !== true) { this.sendUIEvent(UIListenerType.Conversation, 'onAddedEvent', conv); } return conv; } } else { const conv = this._convList.get(params.convId); return conv; } } return undefined; } async removeConversationAllMessages(params: { convId: string; convType: ChatConversationType; }): Promise { const ret = this.tryCatchSync({ promise: this.client.chatManager.deleteConversationAllMessages( params.convId, params.convType ), event: 'removeConversationAllMessages', }); const conv = this._convList.get(params.convId); if (conv) { this.sendUIEvent( UIListenerType.Conversation, 'onRequestReloadEvent', conv.convId ); } Services.dcs .deleteConversationDir(params.convId) .then() .catch((e) => { uilog.warn('remove:', e); }); return ret; } async removeConversation(params: { convId: string; removeMessage?: boolean; }): Promise { const { convId, removeMessage = true } = params; const ret = await this.tryCatchSync({ promise: this.client.chatManager.deleteConversation( convId, removeMessage ?? true ), event: 'removeConversation', }); const conv = this._convList.get(convId); if (conv) { this._convList.delete(convId); this.sendUIEvent(UIListenerType.Conversation, 'onDeletedEvent', conv); if (removeMessage === true) { Services.dcs .deleteConversationDir(params.convId) .then() .catch((e) => { uilog.warn('remove:', e); }); } } return ret; } async clearAllConversations(): Promise { const ret = Array.from(this._convList.values()).map(async (v) => { await this.tryCatchSync({ promise: this.client.chatManager.deleteConversation(v.convId, true), event: 'clearAllConversations', }); this.sendUIEvent(UIListenerType.Conversation, 'onDeletedEvent', v); }); await Promise.all(ret); this._convList.clear(); } async setConversationPin(params: { convId: string; convType: ChatConversationType; isPin: boolean; }): Promise { const ret = await this.tryCatchSync({ promise: this.client.chatManager.pinConversation( params.convId, params.isPin ), event: 'setConversationPin', }); // const conv = this._convList.get(params.convId); const conv = await this.getConversation({ convId: params.convId, convType: params.convType, fromNative: true, }); if (conv) { conv.isPinned = params.isPin; this.sendUIEvent(UIListenerType.Conversation, 'onUpdatedEvent', conv); } return ret; } async setConversationSilentMode(params: { convId: string; convType: ChatConversationType; doNotDisturb: boolean; }): Promise { const ret = await this.tryCatchSync({ promise: this.client.pushManager.setSilentModeForConversation({ convId: params.convId, convType: params.convType, option: { remindType: params.doNotDisturb === true ? ChatPushRemindType.NONE : ChatPushRemindType.ALL, paramType: ChatSilentModeParamType.REMIND_TYPE, }, }), event: 'setConversationSilentMode', }); // const conv = this._convList.get(params.convId); const conv = await this.getConversation({ convId: params.convId, convType: params.convType, fromNative: true, }); if (conv) { conv.doNotDisturb = params.doNotDisturb; this._silentModeList.set(conv.convId, { convId: conv.convId, doNotDisturb: params.doNotDisturb, }); await this._convStorage?.setAllConversation( Array.from(this._silentModeList.values()).map((item) => { return { convId: item.convId, doNotDisturb: item.doNotDisturb, } as ConversationModel; }) ); this.sendUIEvent(UIListenerType.Conversation, 'onUpdatedEvent', conv); } return ret; } async setConversationRead(params: { convId: string; convType: ChatConversationType; }): Promise { const ret = await this.tryCatchSync({ promise: this.client.chatManager.markAllMessagesAsRead( params.convId, params.convType ), event: 'setConversationRead', }); // const conv = this._convList.get(params.convId); const conv = await this.getConversation({ convId: params.convId, convType: params.convType, fromNative: true, }); if (conv) { conv.unreadMessageCount = 0; this.messageManager.emitConversationUnreadCountChanged(); this.sendUIEvent(UIListenerType.Conversation, 'onUpdatedEvent', conv); } return ret; } async setConversationExt(params: { convId: string; convType: ChatConversationType; ext: Record; }): Promise { const ret = await this.tryCatchSync({ promise: this.client.chatManager.setConversationExtension( params.convId, params.convType, params.ext ), event: 'setConversationExt', }); // const conv = this._convList.get(params.convId); const conv = await this.getConversation({ convId: params.convId, convType: params.convType, fromNative: true, }); if (conv) { conv.ext = params.ext; this.sendUIEvent(UIListenerType.Conversation, 'onUpdatedEvent', conv); } return ret; } getConversationMessageCount( convId: string, convType: ChatConversationType ): Promise { return this.tryCatchSync({ promise: this.client.chatManager.getConversationUnreadCount( convId, convType ), event: 'getConversationMessageCount', }); } getConversationLatestMessage( convId: string, convType: ChatConversationType ): Promise { return this.tryCatchSync({ promise: this.client.chatManager.getLatestMessage(convId, convType), event: 'getConversationLatestMessage', }); } async getDoNotDisturb( convId: string, convType: ChatConversationType ): Promise { const ret = await this.tryCatchSync({ promise: this.client.pushManager.fetchSilentModeForConversation({ convId, convType, }), event: 'getDoNotDisturb', }); if (ret) { return ( ret?.remindType === ChatPushRemindType.MENTION_ONLY || ret?.remindType === ChatPushRemindType.NONE ); } return false; } isContact(params: { userId: string }): boolean { return ( this._contactList.has(params.userId) && this.userId !== params.userId ); } getAllContacts(params: { requestServer?: boolean; onResult: ResultCallback; }): void { const { requestServer = false } = params; if (this._contactList.size > 0 && requestServer === false) { this.tryCatch({ promise: this.client.contactManager.getAllContacts(), event: 'getAllContacts', onFinished: async (value) => { const list = new Map() as Map; const tmp = new Map(); value.forEach((v) => { tmp.set(v.userId, { id: v.userId, type: 'user', remark: v.remark && v.remark.length > 0 ? v.remark : undefined, } as DataModel); }); this._updateDataList({ dataList: tmp, disableDispatch: true, isUpdateNotExisted: true, }); try { await this._requestData({ list: Array.from(tmp.keys()), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getAllContacts', error); } const tmp2 = this._dataFileProvider.getUserList( Array.from(tmp.keys()) ); tmp2.forEach((v) => { list.set(v.id, { userId: v.id, userName: v.name, userAvatar: v.avatar, remark: v.remark, } as ContactModel); }); this._contactList = list; params.onResult({ isOk: true, value: Array.from(this._contactList.values()), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); return; } this.tryCatch({ promise: this.client.contactManager.fetchAllContacts(), event: 'fetchAllContacts', onFinished: async (value) => { const list = new Map() as Map; const tmp = new Map(); value.forEach((v) => { tmp.set(v.userId, { id: v.userId, type: 'user', remark: v.remark && v.remark.length > 0 ? v.remark : undefined, } as DataModel); }); this._updateDataList({ dataList: tmp, disableDispatch: true, isUpdateNotExisted: true, }); try { await this._requestData({ list: Array.from(tmp.keys()), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getAllContacts', error); } const tmp2 = this._dataFileProvider.getUserList(Array.from(tmp.keys())); tmp2.forEach((v) => { list.set(v.id, { userId: v.id, userName: v.name, userAvatar: v.avatar, remark: v.remark, } as ContactModel); }); this._contactList = list; params.onResult({ isOk: true, value: Array.from(this._contactList.values()), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } async getContactSync(params: { userId: string; }): Promise> { const ret = await this.tryCatchSync({ promise: this.client.contactManager.getContact(params.userId), event: 'getContactSync', }); if (ret) { const contact = this.toUIContact(ret); return { isOk: true, value: contact, }; } return { isOk: true, value: undefined, }; } getContact(params: { userId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.getContactSync(params), event: 'getContact', onFinished: async (value) => { params.onResult(value); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getAllContacts2(params: { requestServer?: boolean; onResult: ResultCallback; }): void { const { requestServer = false } = params; if (this._contactList.size > 0 && requestServer === false) { this.tryCatch({ promise: this.client.contactManager.getAllContactsFromDB(), event: 'getAllContactsFromDB', onFinished: async (value) => { const list = new Map() as Map; const tmp = new Map(); value.forEach((v: string) => { tmp.set(v, { id: v, type: 'user', remark: undefined, } as DataModel); }); this._updateDataList({ dataList: tmp, disableDispatch: true, isUpdateNotExisted: true, }); try { await this._requestData({ list: Array.from(tmp.keys()), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getAllContacts2', error); } const tmp2 = this._dataFileProvider.getUserList( Array.from(tmp.keys()) ); tmp2.forEach((v) => { list.set(v.id, { userId: v.id, userName: v.name, userAvatar: v.avatar, remark: v.remark, } as ContactModel); }); this._contactList = list; params.onResult({ isOk: true, value: Array.from(this._contactList.values()), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); return; } this.tryCatch({ promise: this.client.contactManager.getAllContactsFromServer(), event: 'getAllContactsFromServer', onFinished: async (value) => { const list = new Map() as Map; const tmp = new Map(); value.forEach((v: string) => { tmp.set(v, { id: v, type: 'user', remark: undefined, } as DataModel); }); this._updateDataList({ dataList: tmp, disableDispatch: true, isUpdateNotExisted: true, }); try { await this._requestData({ list: Array.from(tmp.keys()), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getAllContacts2', error); } const tmp2 = this._dataFileProvider.getUserList(Array.from(tmp.keys())); tmp2.forEach((v) => { list.set(v.id, { userId: v.id, userName: v.name, userAvatar: v.avatar, remark: v.remark, } as ContactModel); }); this._contactList = list; params.onResult({ isOk: true, value: Array.from(this._contactList.values()), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } addNewContact(params: { userId: string; reason?: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.contactManager.addContact( params.userId, params.reason ), event: 'addNewContact', onFinished: async () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e, }); }, }); } removeContact(params: { userId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.contactManager.deleteContact(params.userId), event: 'removeContact', onFinished: async () => { const contact = this._contactList.get(params.userId); this._contactList.delete(params.userId); this.sendUIEvent(UIListenerType.Contact, 'onDeletedEvent', contact); params.onResult?.({ isOk: true, }); }, }); } setContactRemark(params: { userId: string; remark: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.contactManager.setContactRemark({ userId: params.userId, remark: params.remark, }), event: 'setContactRemark', onFinished: () => { this._updateDataList({ dataList: this._dataFileProvider.toMap([ { id: params.userId, remark: params.remark, type: 'user', } as DataModel, ]), dispatchHandler: () => { const contact = this._contactList.get(params.userId); if (contact) { contact.remark = params.remark; contact.userName = this._getNameFromCache(params.userId); contact.remark = this._getRemarkFromCache(params.userId); contact.userAvatar = this._getAvatarFromCache(params.userId); this.sendUIEvent( UIListenerType.Contact, 'onUpdatedEvent', contact ); } return false; }, }); params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } acceptInvitation(params: { userId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.contactManager.acceptInvitation(params.userId), event: 'acceptInvitation', onFinished: async () => { params.onResult({ isOk: true, }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } declineInvitation(params: { userId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.contactManager.declineInvitation(params.userId), event: 'declineInvitation', onFinished: async () => { params.onResult({ isOk: true, }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } setModelState(params: { tag: string; id: string; state: StateModel }): void { const list = this._modelState.get(params.tag); if (list) { list.set(params.id, params.state); } else { this._modelState.set(params.tag, new Map([[params.id, params.state]])); } } getModelState(params: { tag: string; id: string }): StateModel | undefined { const list = this._modelState.get(params.tag); return list?.get(params.id); } clearModelState(params: { tag: string }): void { this._modelState.delete(params.tag); } getJoinedGroups(params: { onResult: ResultCallback }): void { this.tryCatch({ promise: this.client.groupManager.getJoinedGroups(), event: 'getJoinedGroups', onFinished: async (groups) => { let list: GroupModel[] = []; for (const group of groups) { list.push(this.toUIGroup(group)); } params.onResult({ isOk: true, value: list }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getPageGroups(params: { pageSize: number; pageNum: number; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.fetchJoinedGroupsFromServer( params.pageSize, params.pageNum ), event: 'getPageGroups', onFinished: async (value) => { const list = new Map(); value.forEach((v) => { list.set(v.groupId, { id: v.groupId, name: v.groupName, type: 'group', } as DataModel); }); this._updateDataList({ dataList: list, isUpdateNotExisted: true, disableDispatch: true, }); try { await this._requestData({ list: value.map((v) => v.groupId), type: 'group', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getPageGroups', error); } value.forEach(async (v) => { const group = this.toUIGroup(v); this._groupList.set(v.groupId, group); this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); }); params.onResult({ isOk: true, value: Array.from(value).map((v) => this.toUIGroup(v)), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getGroupAllMembers(params: { groupId: string; isReset?: boolean; owner?: GroupParticipantModel; onResult: ResultCallback; }): void { const { isReset = false, owner } = params; const memberList = this._groupMemberList.get(params.groupId); if (memberList && memberList.size > 1 && isReset === false) { params.onResult({ isOk: true, value: Array.from(memberList.values()), }); return; } const pageSize = 200; this.tryCatch({ promise: this.client.groupManager.fetchMemberListFromServer( params.groupId, pageSize, '' ), event: 'getGroupAllMembers', onFinished: async (value) => { const memberList = new Map(); value.list?.forEach(async (v) => { memberList.set(v, { memberId: v }); }); let cursor = value.cursor; if ( cursor.length === 0 || (value.list && value.list.length < pageSize) || value.list === undefined ) { } else { for (;;) { const list = await this.client.groupManager.fetchMemberListFromServer( params.groupId, pageSize, cursor ); list.list?.forEach((v) => { memberList.set(v, { memberId: v }); }); if ( list.cursor.length === 0 || (list.list && list.list.length < pageSize) || list.list === undefined ) { break; } cursor = list.cursor; } } if (owner) { memberList.set(owner.memberId, owner); } try { await this._requestData({ list: Array.from(memberList.keys()), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getGroupAllMembers', error); } memberList.forEach((v) => { v.memberAvatar = this._getAvatarFromCache(v.memberId); v.memberName = this._getNameFromCache(v.memberId); }); this._groupMemberList.set(params.groupId, memberList); params.onResult({ isOk: true, value: Array.from(memberList.values()), }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } async getGroupOwner(params: { groupId: string; }): Promise { const ret = await this.tryCatchSync({ promise: this.client.groupManager.getGroupWithId(params.groupId), event: 'getGroupOwner', }); if (ret) { let groupMember = this._groupMemberList.get(params.groupId); if (groupMember) { const member = groupMember.get(ret.owner); if (member && member.memberName && member.memberAvatar) { return member; } try { await this._requestData({ list: [ret.owner], type: 'user', requestHasData: true, }); } catch (error) { uilog.warn('dev:getGroupOwner', error); } groupMember.set(ret.owner, { memberId: ret.owner, ...member, memberAvatar: this._getAvatarFromCache(ret.owner), memberName: this._getNameFromCache(ret.owner), } as GroupParticipantModel); } else { groupMember = new Map([ [ret.owner, { memberId: ret.owner } as GroupParticipantModel], ]); try { await this._requestData({ list: [ret.owner], type: 'user', requestHasData: true, }); } catch (error) { uilog.warn('dev:getGroupOwner', error); } groupMember.set(ret.owner, { memberId: ret.owner, memberAvatar: this._getAvatarFromCache(ret.owner), memberName: this._getNameFromCache(ret.owner), } as GroupParticipantModel); this._groupMemberList.set(params.groupId, groupMember); } return this._groupMemberList.get(params.groupId)?.get(ret.owner); } return undefined; } getGroupMember(params: { groupId: string; userId: string; }): GroupParticipantModel | undefined { const memberList = this._groupMemberList.get(params.groupId); if (memberList) { return memberList.get(params.userId); } return undefined; } fetchJoinedGroupCount(params: { onResult: ResultCallback }): void { this.tryCatch({ promise: this.client.groupManager.fetchJoinedGroupCount(), event: 'fetchJoinedGroupCount', onFinished: async (value) => { params.onResult({ isOk: true, value: value }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getGroupInfoFromServer(params: { groupId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.fetchGroupInfoFromServer( params.groupId ), event: 'getGroupInfoFromServer', onFinished: async (value) => { if (value) { this._updateDataList({ dataList: this._dataFileProvider.toMap([ { id: value.groupId, name: value.groupName, type: 'group', } as DataModel, ]), dispatchHandler: () => { return false; }, }); try { await this._requestData({ list: [value.groupId], type: 'group', requestHasData: true, }); } catch (error) { uilog.warn('dev:getGroupInfoFromServer', error); } const localGroup = this._groupList.get(params.groupId); const group = this.toUIGroup(value); if (localGroup) { this._groupList.set(group.groupId, mergeObjects(group, localGroup)); } else { this._groupList.set(group.groupId, group); } this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); params.onResult({ isOk: true, value: group, }); } else { params.onResult({ isOk: false, }); } }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } async getGroupInfoSync(params: { groupId: string; }): Promise> { const ret = await this.tryCatchSync({ promise: this.client.groupManager.getGroupWithId(params.groupId), event: 'getGroupInfoSync', }); // !!! fix bug: if group is not exist, return error object. if (ret && ret.groupName.length > 0) { const group = this.toUIGroup(ret); return { isOk: true, value: group, }; } else { const ret2 = await this.tryCatchSync({ promise: this.client.groupManager.fetchGroupInfoFromServer( params.groupId ), event: 'getGroupInfoSync', }); if (ret2) { this._updateDataList({ dataList: this._dataFileProvider.toMap([ { id: ret2.groupId, name: ret2.groupName, type: 'group', } as DataModel, ]), dispatchHandler: () => { return false; }, }); try { await this._requestData({ list: [ret2.groupId], type: 'group', requestHasData: true, }); } catch (error) { uilog.warn('dev:getGroupInfoSync', error); } const localGroup = this._groupList.get(params.groupId); const group = this.toUIGroup(ret2); if (localGroup) { this._groupList.set(group.groupId, mergeObjects(group, localGroup)); } else { this._groupList.set(group.groupId, group); } this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); return { isOk: true, value: group, }; } } return { isOk: false, }; } getGroupInfo(params: { groupId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.getGroupInfoSync(params), event: 'getGroupInfo', onFinished: (value) => { params.onResult(value); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } createGroup(params: { groupName: string; groupDescription?: string; inviteMembers: string[]; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.createGroup( new ChatGroupOptions({ style: ChatGroupStyle.PrivateMemberCanInvite, maxCount: 1000, inviteNeedConfirm: false, }), params.groupName, params.groupDescription, params.inviteMembers ), event: 'createGroup', onFinished: async (value) => { this._updateDataList({ dataList: this._dataFileProvider.toMap([ { id: value.groupId, name: value.groupName, type: 'group', } as DataModel, ]), isUpdateNotExisted: true, dispatchHandler: () => { const group = this.toUIGroup(value); this._groupList.set(group.groupId, group); this.sendUIEvent(UIListenerType.Group, 'onAddedEvent', group); return false; }, }); params.onResult?.({ isOk: true, value: value, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } quitGroup(params: { groupId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.leaveGroup(params.groupId), event: 'quitGroup', onFinished: async () => { const group = this._groupList.get(params.groupId); if (group) { this._groupList.delete(params.groupId); this.sendUIEvent(UIListenerType.Group, 'onDeletedEvent', group); } params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } destroyGroup(params: { groupId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.destroyGroup(params.groupId), event: 'destroyGroup', onFinished: async () => { const group = this._groupList.get(params.groupId); if (group) { this._groupList.delete(params.groupId); this.sendUIEvent(UIListenerType.Group, 'onDeletedEvent', group); } params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } setGroupName(params: { groupId: string; groupNewName: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.changeGroupName( params.groupId, params.groupNewName ), event: 'setGroupName', onFinished: async () => { const group = this._groupList.get(params.groupId); if (group) { group.groupName = params.groupNewName; this._updateDataList({ dataList: this._dataFileProvider.toMap([ { id: group.groupId, name: group.groupName, type: 'group', } as DataModel, ]), dispatchHandler: () => { this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); return false; }, }); } params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } setGroupDescription(params: { groupId: string; groupDescription: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.changeGroupDescription( params.groupId, params.groupDescription ), event: 'setGroupDescription', onFinished: async () => { const group = this._groupList.get(params.groupId); if (group) { group.description = params.groupDescription; this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); } params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } setGroupMyRemark(params: { groupId: string; memberId: string; groupMyRemark: string; ext?: Record; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.setMemberAttribute( params.groupId, params.memberId, { [gGroupMemberMyRemark]: params.groupMyRemark } ), event: 'setGroupMyRemark', onFinished: async () => { const group = this._groupList.get(params.groupId); if (group) { group.myRemark = params.groupMyRemark; this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); } params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } getGroupMyRemark(params: { groupId: string; memberId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.fetchMemberAttributes( params.groupId, params.memberId ), event: 'getGroupMyRemark', onFinished: (result) => { const group = this._groupList.get(params.groupId); if (group) { group.myRemark = result?.[gGroupMemberMyRemark]; } params.onResult({ isOk: true, value: result?.[gGroupMemberMyRemark] }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } addGroupMembers(params: { groupId: string; members: GroupParticipantModel[]; welcomeMessage?: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.addMembers( params.groupId, params.members.map((item) => item.memberId), params.welcomeMessage ), event: 'addGroupMembers', onFinished: async () => { const groupMembers = this._groupMemberList.get(params.groupId); if (groupMembers) { try { await this._requestData({ list: params.members.map((item) => item.memberId), type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:addGroupMembers', error); } for (const member of params.members) { groupMembers.set(member.memberId, { ...member, memberName: this._getNameFromCache(member.memberId), memberAvatar: this._getAvatarFromCache(member.memberId), }); } for (const member of params.members) { this.sendUIEvent( UIListenerType.GroupParticipant, 'onAddedEvent', member ); } params.onResult({ isOk: true, }); } else { params.onResult({ isOk: false, }); } }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } removeGroupMembers(params: { groupId: string; members: string[]; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.removeMembers( params.groupId, params.members ), event: 'removeGroupMembers', onFinished: async () => { for (const memberId of params.members) { const groupMember = this._groupMemberList.get(params.groupId); if (groupMember) { const member = groupMember.get(memberId); if (member) { groupMember.delete(memberId); this.sendUIEvent( UIListenerType.GroupParticipant, 'onDeletedEvent', member ); } } } params.onResult({ isOk: true, }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } changeGroupOwner(params: { groupId: string; newOwnerId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.groupManager.changeOwner( params.groupId, params.newOwnerId ), event: 'changeGroupOwner', onFinished: () => { const group = this._groupList.get(params.groupId); if (group) { group.owner = params.newOwnerId; this.sendUIEvent(UIListenerType.Group, 'onUpdatedEvent', group); } params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } async getUserInfoSync(params: { userId: string; }): Promise> { const ret = await this.tryCatchSync({ promise: this.client.userManager.fetchUserInfoById([params.userId]), event: 'getUserInfoSync', }); const list = Array.from(ret.values()).map((v) => this.toUserData(v)); if (list.length > 0) { const first = list[0]!; this.setUser({ users: [first] }); return { isOk: true, value: first, }; } return { isOk: true, }; } getUserInfo(params: { userId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.getUserInfoSync(params), event: 'getUserInfo', onFinished: (value) => { params.onResult?.(value); }, onError: (error) => { params.onResult?.({ isOk: false, error }); }, }); } getUsersInfo(params: { userIds: string[]; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.userManager.fetchUserInfoById(params.userIds), event: 'getUsersInfo', onFinished: (value) => { if (value) { const list = Array.from(value.values()).map((v) => { return this.toUserData(v); }); this.setUser({ users: list }); params.onResult({ isOk: true, value: list, }); } else { params.onResult({ isOk: true, value: [], }); } }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } updateSelfInfo(params: { self: UserData; onResult: ResultCallback; }): void { const { self } = params; const p = { userId: self.userId, nickName: self.userName, avatarUrl: self.avatarURL, }; this.tryCatch({ promise: this.client.userManager.updateOwnUserInfo(p), event: 'updateSelfInfo', onFinished: () => { this.setUser({ users: [{ ...this.user, ...self }] }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getMessage(params: { messageId: string }): Promise { return this.tryCatchSync({ promise: this.client.chatManager.getMessage(params.messageId), event: 'getMessage', }); } resendMessage(params: { message: ChatMessage; callback: ChatMessageStatusCallback; }): void { this.tryCatch({ promise: this.client.chatManager.resendMessage( params.message, params.callback ), event: 'resendMessage', }); } recallMessage(params: { message: ChatMessage; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.recallMessage(params.message.msgId), event: 'recallMessage', onFinished: async () => { params.onResult({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } insertMessage(params: { message: ChatMessage; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.insertMessage(params.message), event: 'insertMessage', onFinished: async () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } updateMessage(params: { message: ChatMessage; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.updateMessage(params.message), event: 'updateMessage', onFinished: async () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } removeMessage(params: { message: ChatMessage; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.deleteMessage( params.message.conversationId, params.message.chatType as number as ChatConversationType, params.message.msgId, params.message.isChatThread ), event: 'removeMessage', onFinished: async () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } removeMessages(params: { message: ChatMessage[] }): Promise { return this.tryCatchSyncList({ promises: params.message.map((item) => this.client.chatManager.deleteMessage( item.conversationId, item.chatType as number as ChatConversationType, item.msgId, item.isChatThread ) ), event: 'removeMessages', }); } editMessage(params: { message: ChatMessage; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.modifyMessageBody( params.message.msgId, params.message.body ), event: 'editMessage', onFinished: async (msg) => { params.onResult({ isOk: true, value: msg, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } translateMessage(params: { message: ChatMessage; languages: string[]; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.translateMessage( params.message, params.languages ), event: 'translateMessage', onFinished: async (msg) => { params.onResult({ isOk: true, value: msg, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } getNewRequestList(params: { convId: string; convType: ChatConversationType; timestamp?: number; pageSize?: number; direction?: ChatSearchDirection; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.getMsgsWithMsgType({ convId: params.convId, convType: params.convType, msgType: ChatMessageType.CUSTOM, direction: params.direction, timestamp: params.timestamp, count: params.pageSize, }), event: 'getNewRequestList', onFinished: async (value) => { params.onResult({ isOk: true, value: value, }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } sendMessage(params: { message: ChatMessage; callback?: ChatMessageStatusCallback; }): void { const { message, callback } = params; this.setUserInfoToMessage({ msg: message, user: this._user }); this.tryCatch({ promise: this.client.chatManager.sendMessage(message, callback), event: 'sendMessage', }); } downloadMessageAttachment(params: { message: ChatMessage; callback?: ChatMessageStatusCallback; }): void { this.tryCatchSync({ promise: this.client.chatManager.downloadAttachment( params.message, params.callback ), event: 'downloadMessageAttachment', }); } downloadMessageAttachmentForThread(params: { message: ChatMessage; callback?: ChatMessageStatusCallback; }): void { this.tryCatchSync({ promise: this.client.chatManager.downloadAttachmentInCombine( params.message, params.callback ), event: 'downloadMessageAttachmentForThread', }); } getHistoryMessage(params: { convId: string; convType: ChatConversationType; startMsgId: string; direction: ChatSearchDirection; loadCount: number; isChatThread?: boolean; }): Promise { const { convId, convType, startMsgId, direction, loadCount, isChatThread } = params; return this.tryCatchSync({ promise: this.client.chatManager.getMsgs({ convId, convType, startMsgId, direction, loadCount, isChatThread, }), event: 'getHistoryMessage', }); } fetchHistoryMessages(params: { convId: string; convType: ChatConversationType; startMsgId: string; direction: ChatSearchDirection; pageSize: number; }): Promise> { const { convId, convType, startMsgId, direction, pageSize } = params; return this.tryCatchSync({ promise: this.client.chatManager.fetchHistoryMessagesByOptions( convId, convType, { options: { direction, startTs: -1, endTs: -1, needSave: false, }, cursor: startMsgId, pageSize: pageSize, } ), event: 'fetchHistoryMessages', }); } userInfoFromMessage(msg?: ChatMessage): UserData | undefined { return userInfoFromMessage(msg); } setUserInfoToMessage(params: { msg: ChatMessage; user?: UserData }): void { return setUserInfoToMessage(params); } setMessageRead(params: { convId: string; convType: ChatConversationType; msgId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.markMessageAsRead( params.convId, params.convType, params.msgId ), event: 'setMessageRead', onFinished: () => { params.onResult({ isOk: true }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } sendMessageReadAck(params: { message: ChatMessage; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.sendMessageReadAck(params.message), event: 'sendMessageReadAck', onFinished: async () => { params.onResult({ isOk: true, }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } reportMessage(params: { messageId: string; tag: string; reason: string; onResult: ResultCallback; }): void { const { messageId, tag, reason } = params; this.tryCatch({ promise: this.client.chatManager.reportMessage(messageId, tag, reason), event: 'reportMessage', onFinished: async () => { params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchCombineMessageDetail(params: { msg: ChatMessage; }): Promise { return this.tryCatchSync({ promise: this.client.chatManager.fetchCombineMessageDetail(params.msg), event: 'fetchCombineMessageDetail', }); } getMessagesByKeyword(params: { keyword: string; convId: string; convType: ChatConversationType; direction?: ChatSearchDirection; timestamp?: number; maxCount?: number; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.getConvMsgsWithKeyword({ convId: params.convId, convType: params.convType, keywords: params.keyword, direction: params.direction, timestamp: params.timestamp, count: params.maxCount ?? 200, }), event: 'getMessagesByKeyword', onFinished: (value) => { params.onResult({ isOk: true, value: value }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } pinMessage(params: { msgId: string; onResult?: ResultCallback }): void { this.tryCatch({ promise: this.client.chatManager.pinMessage(params.msgId), event: 'pinMessage', onFinished: () => { params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } unPinMessage(params: { msgId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.unpinMessage(params.msgId), event: 'unPinMessage', onFinished: () => { params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchPinnedMessages(params: { convId: string; convType: ChatConversationType; forceRequest?: boolean; onResult: ResultCallback; }): void { const { forceRequest = false } = params; if (this._pinMessageList.has(params.convId) && forceRequest === false) { this.getPinnedMessages(params); return; } this.tryCatch({ promise: this.client.chatManager.fetchPinnedMessages( params.convId, params.convType ), event: 'fetchPinnedMessages', onFinished: (value) => { this._pinMessageList.set(params.convId, true); params.onResult({ isOk: true, value: value }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getPinnedMessages(params: { convId: string; convType: ChatConversationType; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.getPinnedMessages( params.convId, params.convType ), event: 'getPinnedMessages', onFinished: (value) => { params.onResult({ isOk: true, value: value }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } addReactionToMessage(params: { msgId: string; reaction: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.addReaction( params.reaction, params.msgId ), event: 'addReactionToMessage', onFinished: () => { params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } removeReactionFromMessage(params: { msgId: string; reaction: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.removeReaction( params.reaction, params.msgId ), event: 'removeReactionFromMessage', onFinished: () => { params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } getMessageReactionsList(params: { msgId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.getReactionList(params.msgId), event: 'getMessageReactionsList', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } getMessageReactionsDetail(params: { msgId: string; reaction: string; cursor?: string; pageSize?: number; onResult: ResultCallback>; }): void { this.tryCatch({ promise: this.client.chatManager.fetchReactionDetail( params.msgId, params.reaction, params.cursor, params.pageSize ), event: 'getMessageReactionsDetail', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); return false; }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } subPresence(params: { userIds: string[]; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.presenceManager.subscribe( params.userIds, 60 * 60 * 24 * 3 ), event: 'subPresence', onFinished: (res) => { params.onResult?.({ isOk: true, value: res, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } unSubPresence(params: { userIds: string[]; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.presenceManager.unsubscribe(params.userIds), event: 'unSubPresence', onFinished: () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } publishPresence(params: { status: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.presenceManager.publishPresence( PresenceUtil.convertToProtocol(params.status) ), event: 'publishPresence', onFinished: () => { const userId = this.userId; const status = params.status; if (userId) { this.listeners.forEach((v) => { v.onPresenceStatusChanged?.([ new ChatPresence({ publisher: userId, statusDescription: status, lastTime: getCurTs('s').toString(), expiryTime: (60 * 60 * 24 * 3).toString(), statusDetails: new Map(), }), ]); }); } params.onResult?.({ isOk: true }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchPresence(params: { userIds: string[]; onResult: ResultCallback>; }): void { this.tryCatch({ promise: this.client.presenceManager.fetchPresenceStatus(params.userIds), event: 'fetchPresence', onFinished: (result) => { const map = new Map(); for (const item of result) { map.set(item.publisher, PresenceUtil.convertFromProtocol(item)); } params.onResult?.({ isOk: true, value: map, }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } createThread(params: { name: string; msgId: string; parentId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.createChatThread( params.name, params.msgId, params.parentId ), event: 'createThread', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult({ isOk: false, error: e }); }, }); } joinThread(params: { threadId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.joinChatThread(params.threadId), event: 'joinThread', onFinished: (value) => { params.onResult?.({ isOk: true, value: value, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } leaveThread(params: { threadId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.leaveChatThread(params.threadId), event: 'leaveThread', onFinished: () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } destroyThread(params: { threadId: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.destroyChatThread(params.threadId), event: 'destroyThread', onFinished: () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } updateThreadName(params: { threadId: string; name: string; onResult?: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.updateChatThreadName( params.threadId, params.name ), event: 'updateThreadName', onFinished: () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } removeMemberFromThread(params: { threadId: string; userId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.removeMemberWithChatThread( params.threadId, params.userId ), event: 'removeMemberFromThread', onFinished: () => { params.onResult?.({ isOk: true, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchMembersFromThread(params: { threadId: string; cursor: string; pageSize: number; onResult: ResultCallback>; }): void { this.tryCatch({ promise: this.client.chatManager.fetchMembersWithChatThreadFromServer( params.threadId, params.cursor, params.pageSize ), event: 'fetchMembersFromThread', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchThreadsFromGroup(params: { parentId: string; cursor: string; pageSize: number; onResult: ResultCallback>; }): void { this.tryCatch({ promise: this.client.chatManager.fetchChatThreadWithParentFromServer( params.parentId, params.cursor, params.pageSize ), event: 'fetchThreadsFromGroup', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } fetchThreadsLastMessage(params: { threadId: string[]; onResult: ResultCallback>; }): void { this.tryCatch({ promise: this.client.chatManager.fetchLastMessageWithChatThread( params.threadId ), event: 'fetchThreadsLastMessage', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult?.({ isOk: false, error: e }); }, }); } async fetchThreadsLastMessageSync(params: { threadId: string[]; }): Promise>> { const ret = await this.tryCatchSync({ promise: this.client.chatManager.fetchLastMessageWithChatThread( params.threadId ), event: 'fetchThreadsLastMessageSync', }); return { isOk: true, value: ret, }; } fetchThread(params: { threadId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.fetchChatThreadFromServer( params.threadId ), event: 'fetchThread', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult({ isOk: false, error: e, }); }, }); } getThread(params: { threadId: string; onResult: ResultCallback; }): void { this.tryCatch({ promise: this.client.chatManager.getMessageThread(params.threadId), event: 'getThread', onFinished: (value) => { params.onResult({ isOk: true, value: value, }); }, onError: (e) => { params.onResult({ isOk: false, error: e, }); }, }); } async getAllBlockList(params: { isForce?: boolean; onResult: ResultCallback; }): Promise { try { let ret: string[]; if (this._blockList.size === 0 || params.isForce === true) { ret = await this.client.contactManager.getBlockListFromServer(); } else { ret = await this.client.contactManager.getBlockListFromDB(); } if (ret.length > 0) { try { await this._requestData({ list: ret, type: 'user', requestHasData: true, isUpdateNotExisted: true, }); } catch (error) { uilog.warn('dev:getAllBlockList', error); } this._blockList.clear(); for (const item of ret) { this._blockList.set(item, { userId: item, userAvatar: this._getAvatarFromCache(item), userName: this._getNameFromCache(item), remark: this._getRemarkFromCache(item), } as BlockModel); } } else { this._blockList.clear(); } params.onResult({ isOk: true, value: Array.from(this._blockList.values()), }); } catch (e) { params.onResult({ isOk: false, error: new UIKitError({ code: ErrorCode.ui_error, desc: this._fromChatError(e), }), }); } } isBlockUser(params: { userId: string }): boolean { return this._blockList.has(params.userId); } addUserToBlock(params: { userId: string; onResult?: ResultCallback; }): void { const { userId, onResult } = params; this.tryCatch({ promise: this.client.contactManager.addUserToBlockList(userId), event: 'addUserToBlock', onFinished: () => { const user = this.getDataModel(userId); if (user) { const item = { userId: userId, userAvatar: user.avatar, userName: user.name, remark: user.remark, } as BlockModel; this._blockList.set(userId, item); this.sendUIEvent(UIListenerType.Block, 'onAddedEvent', item); } else { this._blockList.set(userId, { userId: userId, } as BlockModel); this.sendUIEvent(UIListenerType.Block, 'onAddedEvent', { userId: userId, } as BlockModel); } onResult?.({ isOk: true, }); }, onError: (e) => { onResult?.({ isOk: false, error: e, }); }, }); } removeUserFromBlock(params: { userId: string; onResult?: ResultCallback; }): void { const { userId, onResult } = params; this.tryCatch({ promise: this.client.contactManager.removeUserFromBlockList(userId), event: 'removeUserFromBlock', onFinished: () => { this._blockList.delete(userId); this.sendUIEvent(UIListenerType.Block, 'onDeletedEvent', { userId: userId, } as BlockModel); onResult?.({ isOk: true, }); }, onError: (e) => { onResult?.({ isOk: false, error: e, }); }, }); } }