// // IMMessageModels.swift // Crush // // Created by Leon on 2025/8/22. // import Foundation enum CLCustomAttchType: String, Codable { case IMAGE case IM_SEND_GIFT case HEARTBEAT_LEVEL_UP case HEARTBEAT_LEVEL_DOWN case CALL // 余额不足 raw:{"type":"INSUFFICIENT_BALANCE","content":"余额不足"} case INSUFFICIENT_BALANCE /// 过滤此消息, 后端用来保持语音和文本之间的上下文使用 case VOICE_CHAT_CONTENT /// 一些自定义消息需要滑动显示完成 var needInstantScrollDisplay: Bool{ switch self { case .IMAGE, .IM_SEND_GIFT: return true default: return false } } } struct IMCustomAttachment: Codable{ var type: CLCustomAttchType? /// 图片url var url: String? // 可能是模糊图片 var width: FlexibleCGFloat? var height: FlexibleCGFloat? var albumId: Int? // 相册id var unlockPrice: Int? // AI图片解锁需要的价格。 // - 礼物专属 var giftId: Int? var giftName: String? var giftNum: Int? var giftIcon: String? /** // HEARTBEAT_LEVEL_UP 和 HEARTBEAT_LEVEL_DOWN的title imContent */ var title : String? var icon: String? // - Call var callType: CallType? var duration: Int? // - 心动等级升降 var heartbeatLevel: HeartbeatLevel? var heartbeatLevelNum: Int? var heartbeatVal: CGFloat? var heartbeatLevelName: String? func getDisplayString() -> String{ var displayString = "" // guard let theType = type else{ // return displayString // } switch type { case .IMAGE: displayString = "[Image]" case .IM_SEND_GIFT: displayString = "[Gift]" case .CALL: displayString = dealCallAttachment() default: displayString = " " #if DEBUG // 最后一条消息,可能是自定义消息,且不含内容,比如coin不足。 displayString = "[测试环境显示]Check 类型⚠️ is \(String(describing: type))" #endif } return displayString } } // MARK: - Helper extension IMCustomAttachment{ // ⚠️不清晰,需要换! static func getCustomInfo(model: SessionBaseModel) -> IMCustomAttachment? { guard let raw = model.v2msg?.attachment?.raw else{ dlog("☁️❌ getCustom error") return nil } return CodableHelper.decode(IMCustomAttachment.self, from: raw) } private func dealCallAttachment() -> String { guard let theCallType = callType else { return "" } if theCallType == .CALL_CANCEL { return "Call Canceled" } else { let durationInMinistamp = duration ?? 0 return formatDuration(durationInMinistamp) } } fileprivate func formatDuration(_ duration: Int) -> String { // 毫秒 → 秒(向上取整) let totalSeconds = Int(ceil(Double(duration) / 1000.0)) let hours = totalSeconds / 3600 let minutes = (totalSeconds / 60) % 60 let seconds = totalSeconds % 60 if hours > 0 { return String(format: "%@ %02d:%02d:%02d", "Call Duration", hours, minutes, seconds) } else { return String(format: "%@ %02d:%02d", "Call Duration", minutes, seconds) } } } enum CallType: String, Codable{ case CALL_CANCEL case CALL_END }