harmony/chatkit_ui/src/main/ets/model/NIMMessageInfo.ets
2025-07-10 08:57:32 +08:00

358 lines
11 KiB
Plaintext
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.

/*
* Copyright (c) 2022 NetEase, Inc. All rights reserved.
* Use of this source code is governed by a MIT license that can be
* found in the LICENSE file.
*
*/
import {
V2NIMMessage,
V2NIMMessageAttachment,
V2NIMMessageImageAttachment,
V2NIMMessageType
} from '@nimsdk/base/src/main/ets/nim/sdk/V2NIMMessageService'
import {
ChatKitClient,
ChatRepo,
CustomMessageUtils,
keyReplyMsgKey,
mergedMessageCustomType,
StorageRepo
} from '@nimkit/chatkit'
import { DateUtils } from '../common/DateUtils'
import { fileUri } from '@kit.CoreFileKit'
import { ChatConst } from '../constants/ChatConst'
import { JSONUtil } from '@nimkit/common'
import { BusinessError, systemDateTime } from '@kit.BasicServicesKit'
import { V2NIMGetMediaResourceInfoResult, V2NIMMessageAIStreamStatus, V2NIMMessagePin } from '@nimsdk/base'
import { ChatKitConfig } from '../ChatKitConfig'
import { getMergedMessageContent, measureMessageHeight } from '../common/MessageHelper'
import fs from '@ohos.file.fs'
@ObservedV2
export class NIMMessageInfo {
// IM SDK 层的消息对象
@Trace message: V2NIMMessage
// 未读数量
@Trace unReadCount: number = 100
// 已读数量
@Trace readCount: number = -1
// 消息下载或上传进度
@Trace downloadProgress: number = -1
@Trace messageHeight = -1
// 消息附件
attachment: V2NIMMessageAttachment | null = null
// 自定义消息附件
customAttachment: object | null = null
// 上一条消息的发送时间,用于判断该消息展示时候是否需要展示发送时间
@Trace lastMessageTime: number = 0
// 是否是接收消息UI渲染时使用
isReceiveMsg: boolean = false
// 是否为撤回消息
isRevokeMsg: boolean = false
// 是否为撤回可编辑
@Trace revokeEditMsg: boolean = false
// 是否为PIN消息
@Trace isPinMsg: boolean = false
// 是否为合并转发消息
isMergeMsg: boolean = false
mergedContent: string = ''
// 是否为合并转发详情页中的消息
isMergeDetailMsg: boolean = false
// 是否为多选选中
@Trace isSelectedMsg: boolean = false
//是否为回复消息
@Trace
isReplyMsg: boolean = false
// 回复消息
@Trace replyMsg: NIMMessageInfo | undefined = undefined
// 撤回消息扩展
revokeInfo: RevokeInfo | undefined = undefined
//pin信息
pinInfo: V2NIMMessagePin | undefined = undefined
constructor(msg: V2NIMMessage) {
this.message = msg
this.parseMessage(msg)
}
async parseMessage(message: V2NIMMessage) {
this.isReceiveMsg = this.message.senderId != ChatKitClient.getLoginUserId()
// 解析撤回
this.revokeInfo = RevokeInfo.parseRevokeInfo(message)
if (this.revokeInfo != null) {
this.isRevokeMsg = true
this.revokeEditMsg = this.revokeInfo?.isEditMsg &&
(systemDateTime.getTime() - this.revokeInfo.revokeTime < ChatKitConfig.messageRevokeTimeLimit)
}
// 解析合并转发
if (message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_CUSTOM && message.attachment != null) {
this.customAttachment = CustomMessageUtils.dataOfCustomMessage(message.attachment)
let customType = CustomMessageUtils.typeOfCustomMessage(message.attachment)
if (customType == mergedMessageCustomType) {
this.isMergeMsg = true
this.mergedContent = getMergedMessageContent(this.customAttachment)
}
}
// 解析回复
this.parseReply(message)
this.messageHeight = -1
}
// @Computed
// get isReplyMsg():boolean{
// return this.replyMsg !== undefined
// }
setIsReply(reply: boolean) {
this.isReplyMsg = reply
}
async parseReply(message: V2NIMMessage) {
// 优先使用 thread 方案
if (message.threadReply) {
this.isReplyMsg = true
try {
const messages = await ChatRepo.getMessageListByRefers([message.threadReply])
if (messages.length > 0) {
this.replyMsg = new NIMMessageInfo(messages[0])
}
} catch (err) {
console.error(err)
}
return
}
// 非 thread 方案
const remoteExt = message.serverExtension
if (remoteExt) {
try {
const remoteDic = JSON.parse(remoteExt) as object
const msgReplyDic = remoteDic[keyReplyMsgKey] as object
if (msgReplyDic) {
try {
this.isReplyMsg = true
const messages = await ChatRepo.getMessageListByRefers([{
messageClientId: msgReplyDic['idClient'] as string,
messageServerId: msgReplyDic['idServer'] as string,
senderId: msgReplyDic['from'] as string,
createTime: msgReplyDic['time'] as number,
conversationId: msgReplyDic['to'] as string,
receiverId: msgReplyDic['receiverId'] as string,
conversationType: msgReplyDic['scene'] as number,
}]).catch((err: BusinessError) => {
console.debug('netease parseReply', err.message)
})
if (messages && messages.length > 0) {
this.replyMsg = new NIMMessageInfo(messages[0])
}
} catch (err) {
console.error(err)
}
}
} catch (err) {
console.error(err)
}
}
}
checkRevokeEdit(): boolean {
if (this.revokeInfo != null) {
this.isRevokeMsg = true
this.revokeEditMsg = this.revokeInfo?.isEditMsg &&
(systemDateTime.getTime() - this.revokeInfo.revokeTime < ChatKitConfig.messageRevokeTimeLimit)
return this.revokeEditMsg
}
return false
}
configReadReceipt(): boolean {
return this.message.messageConfig?.readReceiptEnabled ?? false
}
updateMessageStatus(message: V2NIMMessage) {
this.message = message
}
setLastMessageTime(time: number) {
this.lastMessageTime = time
}
setSelected(isSelected: boolean) {
this.isSelectedMsg = isSelected
}
getMessageHeight(context: UIContext): number {
if (this.messageHeight < 0) {
this.messageHeight = measureMessageHeight(context, this)
}
return this.messageHeight
}
setPinMessage(pinMsg: V2NIMMessagePin | undefined) {
if (pinMsg !== undefined) {
this.pinInfo = pinMsg
this.isPinMsg = true
} else {
this.isPinMsg = false
this.pinInfo = undefined
}
this.messageHeight = -1
}
setDownloadProgress(progress: number) {
this.downloadProgress = progress
}
setReadCount(readCount: number, unreadCount: number) {
this.unReadCount = unreadCount
this.readCount = readCount
}
getCreateTime(): number {
return this.message.createTime
}
// 获取消息时间
getMessageFormatTime(): string {
return DateUtils.formatTime(this.message.createTime, this.lastMessageTime)
}
// 获取消息时间(根据消息时间展示的间隔)
getMessageTime(): string {
let result = ''
if (this.message.createTime - this.lastMessageTime > ChatKitConfig.messageTimeGap) {
result = DateUtils.formatTime(this.message.createTime, this.lastMessageTime)
}
return result
}
getConversationId() {
return this.message.conversationId
}
getConversationType() {
return ChatKitClient.nim.conversationIdUtil.parseConversationType(this.getConversationId())
}
getMessageClientId(): string {
return this.message.messageClientId
}
getAvatarName(): string {
let result = ''
if (this.message != null && this.message != null) {
result = this.message.senderId.substring(this.message.senderId.length - 2, this.message.senderId.length)
}
if (this.message != null && this.message.senderId != null) {
result = this.message.senderId.substring(this.message.senderId.length - 2, this.message.senderId.length)
}
return result
}
getMessageType(): V2NIMMessageType {
return this.message.messageType
}
isAiStreamMessage(): boolean {
return this.message.aiConfig?.aiStream ?? false
}
isFinishedAiStream(): boolean {
return (this.message.aiConfig?.aiStreamStatus ??
V2NIMMessageAIStreamStatus.V2NIM_MESSAGE_AI_STREAM_STATUS_GENERATED) >
V2NIMMessageAIStreamStatus.V2NIM_MESSAGE_AI_STREAM_STATUS_PLACEHOLDER
}
isReceiveMessage(): boolean {
return this.isReceiveMsg
}
isPinMessage(): boolean {
return this.isPinMsg
}
isMergeMessage(): boolean {
return this.isMergeMsg
}
isMergeDetailMessage(): boolean {
return this.isMergeDetailMsg
}
getImageUrl(): string {
if (this.message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
let iamgeAttachment = this.message.attachment as V2NIMMessageImageAttachment
if (iamgeAttachment) {
if (iamgeAttachment.path) {
if (fs.accessSync(iamgeAttachment.path)) {
const uri = fileUri.getUriFromPath(iamgeAttachment.path)
return uri
}
}
return iamgeAttachment.url ?? ''
}
}
return ''
}
async getImageThumbUrl(): Promise<string> {
if (this.message.messageType == V2NIMMessageType.V2NIM_MESSAGE_TYPE_IMAGE) {
let iamgeAttachment = this.message.attachment as V2NIMMessageImageAttachment
if (iamgeAttachment) {
if (iamgeAttachment.path) {
if (fs.accessSync(iamgeAttachment.path)) {
const uri = fileUri.getUriFromPath(iamgeAttachment.path)
return uri
}
}
const thumbResult: V2NIMGetMediaResourceInfoResult | undefined =
await StorageRepo.getImageThumbUrl(iamgeAttachment, {
width: ChatConst.imageMessageWidth
})
if (!thumbResult) {
return iamgeAttachment.url ?? ''
} else {
return thumbResult.url ?? ''
}
}
}
return ''
}
}
export class RevokeInfo {
isLocalRevoke: boolean = false
revokeTime: number = 0
isEditMsg: boolean = false
revokeMsgText: string = ''
revokeMsgClientId: string = ''
static parseRevokeInfo(msg: V2NIMMessage): RevokeInfo | undefined {
if (msg == undefined || msg.messageType !== V2NIMMessageType.V2NIM_MESSAGE_TYPE_TEXT ||
msg.localExtension == undefined
|| !msg.localExtension?.includes(ChatConst.revokeLocalKey)) {
return undefined
}
let localExtension = msg.localExtension
if (JSONUtil.isJSONString(localExtension)) {
let localObject = JSON.parse(localExtension) as object
let revokeInfo = new RevokeInfo()
revokeInfo.revokeMsgText = localObject?.[ChatConst.revokeMsgTextKey]
revokeInfo.revokeMsgClientId = localObject?.[ChatConst.revokeMsgClientIdKey]
revokeInfo.revokeTime = localObject?.[ChatConst.revokeLocalTimeKey]
revokeInfo.isLocalRevoke = localObject?.[ChatConst.revokeLocalKey]
revokeInfo.isEditMsg = localObject?.[ChatConst.revokeMsgEditKey]
return revokeInfo
}
return undefined
}
}