feat(chat): 添加聊天界面视图模型和联系人管理功能

- 实现 ChatViewModel 处理消息收发、历史记录加载和状态管理
- 添加消息搜索、草稿保存、引用回复等功能
- 实现多媒体附件发送包括图片、视频、音频和文件
- 添加群组提及用户功能和消息撤回机制
- 实现联系人管理功能包括好友搜索、添加、删除和黑名单管理
- 添加好友请求处理和实时消息监听
- 实现会话列表管理包含未读消息统计和实时更新
- 集成 IM SDK 的连接状态管理和事件监听
- 添加消息状态跟踪和超时处理机制
- 实现数据缓存机制优化用户体验
这个提交包含在:
XuqmGroup 2026-04-28 22:32:20 +08:00
父节点 c656bdd202
当前提交 9e5fa3da03
共有 11 个文件被更改,包括 37732 次插入20367 次删除

文件差异因一行或多行过长而隐藏

文件差异因一行或多行过长而隐藏

查看文件

@ -32,20 +32,27 @@
"IS_HVIGORFILE_TYPE_CHECK": false,
"TASK_TIME": {
"923fe53966c6cd9343e11af776cd4b05be315ea4b200b02e4d5dfb0f929b73bf": {
"CreateModuleInfo": 388167,
"PreCheckSyscap": 136375,
"ProcessIntegratedHsp": 322375,
"SyscapTransform": 12371834,
"ProcessStartupConfig": 1027750,
"ConfigureCmake": 63042,
"BuildNativeWithCmake": 75791,
"BuildNativeWithNinja": 124459,
"BuildJS": 962083
"CreateModuleInfo": 314458,
"PreCheckSyscap": 126708,
"ProcessIntegratedHsp": 218542,
"SyscapTransform": 12138250,
"ProcessStartupConfig": 988208,
"ConfigureCmake": 73375,
"BuildNativeWithCmake": 72625,
"BuildNativeWithNinja": 129375,
"BuildJS": 920292,
"CompileArkTS": 2834291708,
"ProcessCompiledResources": 207917,
"PackageHap": 234451500,
"PackingCheck": 2285000,
"SignHap": 493042,
"CollectDebugSymbol": 273250,
"assembleHap": 56166
},
"77aabe6c19463543339f337db9c84e4d10fd2f56ea0aedaf85a0214d59e93ec4": {
"ConfigureCmake": 70375,
"BuildNativeWithCmake": 86667,
"BuildNativeWithNinja": 204500
"ConfigureCmake": 64000,
"BuildNativeWithCmake": 77000,
"BuildNativeWithNinja": 510500
}
},
"APIS": [
@ -56,13 +63,7 @@
"ENABLE_CPP_FUNCTION_LEVEL_INCREMENTAL": false
},
"CONFIG_PROPERTIES": {},
"BUILD_ID": "202604282057530700",
"ERROR_MESSAGE": [
{
"CODE": "00000000",
"TIMESTAMP": "1777381083000"
}
],
"TOTAL_TIME": 9931347791
"BUILD_ID": "202604282229113860",
"TOTAL_TIME": 3981273167
}
}

文件差异内容过多而无法显示 加载差异

文件差异内容过多而无法显示 加载差异

文件差异内容过多而无法显示 加载差异

文件差异内容过多而无法显示 加载差异

查看文件

@ -19,6 +19,7 @@ export interface ImEventDelegate {
onConnected?(): void
onDisconnected?(code: number, reason: string): void
onMessage?(msg: ImMessage): void
onRead?(msg: ImMessage): void
onRevoke?(data: RevokeData): void
onError?(message: string): void
}
@ -73,6 +74,10 @@ class GroupJoinRequestBody {
remark: string = ''
}
class EditMessageBody {
content: string = ''
}
class UpdateProfileBody {
appId: string = ''
nickname: string = ''
@ -141,7 +146,14 @@ export class ImClient {
}
const frame = JSON.parse(value) as WebSocketFrame
if (frame.type === 'MESSAGE') {
this.delegate?.onMessage?.(this.normalizeMessage(frame.payload as ImMessage))
const message = this.normalizeMessage(frame.payload as ImMessage)
if (message.status === 'READ') {
this.delegate?.onRead?.(message)
}
if (message.revoked || message.status === 'REVOKED' || message.msgType === 'REVOKED') {
this.delegate?.onRevoke?.({ msgId: message.id, operatorId: message.fromId })
}
this.delegate?.onMessage?.(message)
} else if (frame.type === 'REVOKE') {
this.delegate?.onRevoke?.(frame.payload as RevokeData)
}
@ -223,6 +235,46 @@ export class ImClient {
return HttpClient.get<PageResult<ImMessage>>('/api/im/messages/group-history/' + encodeURIComponent(groupId), queryString)
}
async locateHistoryPage(
toId: string,
messageId: string,
pageSize: number = 20,
maxPages: number = 20,
): Promise<ImMessage[] | null> {
const pageCount = Math.max(maxPages, 1)
for (let page = 0; page < pageCount; page += 1) {
const result = await this.fetchHistory(toId, page, pageSize)
const messages = result.content ?? []
if (messages.some(item => item.id === messageId)) {
return messages
}
if (messages.length < pageSize) {
return null
}
}
return null
}
async locateGroupHistoryPage(
groupId: string,
messageId: string,
pageSize: number = 50,
maxPages: number = 20,
): Promise<ImMessage[] | null> {
const pageCount = Math.max(maxPages, 1)
for (let page = 0; page < pageCount; page += 1) {
const result = await this.fetchGroupHistory(groupId, page, pageSize)
const messages = result.content ?? []
if (messages.some(item => item.id === messageId)) {
return messages
}
if (messages.length < pageSize) {
return null
}
}
return null
}
async listConversations(size: number = 20): Promise<ConversationData[]> {
return HttpClient.get<ConversationData[]>('/api/im/conversations', this.buildConversationQuery(size))
}
@ -298,6 +350,16 @@ export class ImClient {
)
}
async editMessage(messageId: string, content: string): Promise<ImMessage> {
const body = new EditMessageBody()
body.content = content
return HttpClient.put<ImMessage>(
'/api/im/messages/' + encodeURIComponent(messageId),
body,
this.buildAppQuery(),
)
}
async sendFriendRequest(toUserId: string, remark: string | null = null): Promise<FriendRequest> {
const params = new FriendRequestBody()
params.appId = SDKContext.getConfig().appKey