Visual_Novel_iOS/crush/Crush/Src/Modules/Chat/Util/SessionUtil.swift

566 lines
19 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// SessionUtil.swift
// LegendTeam
//
// Created by on 20/12/21.
//
import Foundation
import NIMSDK
class SessionUtil {
var topViewHeight: CGFloat = 0
let mssagePageSize = 50
//var session: NIMSession!
//var conversation: V2NIMConversation!
var conversationId : String!
let showTimeInterval: TimeInterval = 180
var cellModels: [SessionBaseModel]! = [SessionBaseModel]()
/// Key: messageClientId messageServerId,messageServerId
var msgIdDict: [String: SessionBaseModel]! = [String: SessionBaseModel]()
func getLastMessageModel() -> SessionBaseModel? {
var lastModel: SessionBaseModel?
for model in cellModels {
if let messageServerId = model.v2msg?.messageServerId, messageServerId.count > 0{
lastModel = model
}
}
return lastModel
}
}
// private
extension SessionUtil {
///
func firtTimeStamp() -> TimeInterval {
guard let model = cellModels.first else {
return 0
}
return model.timeStamp
}
///
func lastTimeInterval() -> TimeInterval {
return cellModels.last?.timeStamp ?? 0
}
///
func shouldAppendTimestamp(model: SessionBaseModel) -> Bool {
if model.cellType == .aiLoading{
return false
}
return model.timeStamp - lastTimeInterval() > showTimeInterval
}
///
func shouldInsertTimestamp(model: SessionBaseModel) -> Bool {
return firtTimeStamp() - model.timeStamp > showTimeInterval
}
/// model
func ignore(info: IMBaseRemoteInfo) -> Bool{
var ignore = false
if let customAttachment = info.customAttachment{
if let type = customAttachment.type{
if type == .HEARTBEAT_LEVEL_DOWN ||
type == .HEARTBEAT_LEVEL_UP ||
type == .INSUFFICIENT_BALANCE ||
type == .VOICE_CHAT_CONTENT
{
ignore = true
}
}else{
// type
ignore = true
}
}
return ignore
}
/// modelmodel
func needDeriveModel(message: V2NIMMessage) -> Bool {
if message.messageType == .MESSAGE_TYPE_CUSTOM{
if let attachment = message.attachment?.raw, attachment.count > 0 {
let model = CodableHelper.decode(IMCustomAttachment.self, from: attachment)
if let type = model?.type, type.rawValue.uppercased() == CLCustomAttchType.IMAGE.rawValue{
return true
}
}
}
if message.serverExtension == nil {
return false
}
let info = IMRemoteUtil.dealRemoteInfo(message: message)
if info.cellType == .keyword
|| info.cellType == .gift
{
return true
}
return false
}
func needDecriveModel(with: IMBaseRemoteInfo) -> Bool{
if let customAttachment = with.customAttachment, let type = customAttachment.type{
if type == .IMAGE{
return true
}
}
if with.cellType == .keyword || with.cellType == .gift{
return true
}
return false
}
/// model
func setIdIdctObject(model: SessionBaseModel) {
if model.isDeriveModel {
return
}
guard let messageID = model.v2msg?.messageClientId else { return }
msgIdDict[messageID] = model
}
/// messagemodel
func modelIsExist(model: SessionBaseModel) -> Bool {
guard let messageID = model.v2msg?.messageClientId else { return false }
return msgIdDict[messageID] != nil
}
func logAllCellModels(){
if APIConfig.environment == .test{
var string = "cellModels(\(cellModels.count):\n"
for per in cellModels {
string += "\(per)\n"
}
dlog(string)
}
}
}
// MARK: - Deal Models
/// Deal Models
extension SessionUtil {
/// cellModel
func createCellModelWith(type: SessionCellType) -> SessionBaseModel {
let model = SessionBaseModel()
let configType = SessionBaseModel.getContentConfigClass(type: type)
//
model.config = configType.init()
model.cellType = type
switch type {
case .image:
model.shouldShowBubble = false
case .gift:
//
model.shouldShowBubble = false
model.shouldShowCardView = true
case .timeStamp,.tips, .create, .keyword, .phonecall:
model.shouldShowBubble = false
model.shouldShowTips = true
default:
// bubble
model.shouldShowBubble = true
model.shouldShowTips = false
break
}
return model
}
func createCellModelWithCustom(attachment:String?) -> SessionBaseModel{
let model = SessionBaseModel()
guard let raw = attachment, let data = CodableHelper.decode(IMCustomAttachment.self, from: raw) else{
return model
}
// switch data.type{
// case .IMAGE: break
//
// }
//let configType = SessionBaseModel.getContentConfigClass(type: type)
return model
}
///
func dealMessages(messages: [V2NIMMessage]?, completion: @escaping ((_ messageDatas: [V2NIMMessage]?, _ error: Error?) -> Void)) {
var tempModels = modelWithV2(datas: messages!)
var tempMessages = messages ?? [V2NIMMessage]()
// while tempModels.count < mssagePageSize, messages!.count > 0 {
// tempMessages = NIMSDK.shared().conversationManager.messages(in: session, message: tempMessages.first, limit: mssagePageSize)!
// if Array.realEmpty(array: tempMessages) {
// break
// }
// let theModels = modelWith(messsages: tempMessages)
// tempModels.insert(contentsOf: theModels, at: 0)
// }
// let indexs = insertMessagesWith(models: tempModels.reversed())
DispatchQueue.main.async {
completion( messages, nil)
}
}
@discardableResult
func inserMessageWith(message: V2NIMMessage, atIndex: Int) -> [Int]{
let models = modelWithV2(datas: [message])
guard let model = models.first else { return [] }
self.cellModels.insert(model, at: atIndex)
return [atIndex]
}
/// model
func appendWith(models: [SessionBaseModel]) -> [Int] {
if Array.realEmpty(array: models) {
return []
}
let count = cellModels.count
for model in models {
if modelIsExist(model: model), !model.isDeriveModel {
continue
}
_ = appendWith(model: model)
}
var indexs = [Int]()
for i in count ..< cellModels.count {
indexs.append(i)
}
return indexs
}
/// model
@discardableResult
func appendWith(model: SessionBaseModel) -> [Int] {
var indexs = [Int]()
if shouldAppendTimestamp(model: model) {
addTimestamp(model: model, index: cellModels.count)
indexs.append(cellModels.count)
}
cellModels.insert(model, at: cellModels.count)
indexs.append(cellModels.count)
setIdIdctObject(model: model)
return indexs
}
/// model
func addTimestamp(model: SessionBaseModel, index: Int) {
let timeModel = createCellModelWith(type: .timeStamp)
timeModel.shouldShowBubble = false
timeModel.shouldShowTips = true
timeModel.timeStamp = model.v2msg?.createTime ?? model.timeStamp
cellModels.insert(timeModel, at: index)
}
/// model
func findCellModel(message: V2NIMMessage?) -> SessionBaseModel? {
guard let messageID = message?.messageClientId else { return nil }
// model
let model = msgIdDict[messageID]
return model
}
/// modelindex
func modelIndexWith(model: SessionBaseModel) -> Int? {
for i in 0 ..< cellModels.count {
let obj = cellModels[i]
//if obj.isEqual(model) {
if obj === model{
return i
}
}
return nil
}
/// message model
@discardableResult
func appendWith(messages: [V2NIMMessage]) -> [Int] {
let models = modelWithV2(datas: messages)
// modelWithV2
let indexs = appendWith(models: models.reversed())
return indexs
}
/// message model
func updateWith(message: V2NIMMessage) -> [Int]? {
guard let model = findCellModel(message: message) else { return nil }
guard let index = modelIndexWith(model: model) else { return nil }
var indexs = [Int]()
indexs.append(index)
if needDeriveModel(message: message) {
if index + 1 < cellModels.count {
indexs.append(index + 1)
}
}
return indexs
}
// MARK: V2
/// [V2NIMMessage] -> [SessionBaseModel]
func modelWithV2(datas:[V2NIMMessage]) -> [SessionBaseModel]{
var array = [SessionBaseModel]()
for i in 0 ..< datas.count {
let message = datas[i]
if let messageID = message.messageClientId, msgIdDict[messageID] != nil{
//
dlog("☁️⚠️⚠️\(message)已经存在了,应该更新,而不是新创建!⚠️⚠️")
continue
}
if let serverExtension = message.serverExtension, serverExtension.count > 0{
dlog("message___ext___\(serverExtension)")
}
// Step cellType
let info = IMRemoteUtil.dealRemoteInfo(message: message)
//
guard !ignore(info: info) else {
continue
}
// Step cellModel
if needDecriveModel(with: info) { // needDeriveModel(message: message)
//
// (👆cell//
let cellModel = createCellModelWith(type: info.cellType)
cellModel.baseRemoteInfo = info
cellModel.isDeriveModel = true
cellModel.shouldShowLeft = !message.isSelf
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
if message.messageType == .MESSAGE_TYPE_TEXT {
if message.isSelf{
let cellModel = createCellModelWith(type: .text)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = false
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}else{
let cellModel = createCellModelWith(type: .aimsg)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = true
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}
} else if message.messageType == .MESSAGE_TYPE_TIP {
let cellModel = createCellModelWith(type: .tips)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = !message.isSelf
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}else if message.messageType == .MESSAGE_TYPE_CUSTOM{
//
if message.isSelf{
let cellModel = createCellModelWith(type: .text)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = !message.isSelf
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}else{
if info.cellType != .image{
let cellModel = createCellModelWith(type: .aimsg)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = !message.isSelf
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}
}
}
} else {
// cell
let cellModel = createCellModelWith(type: info.cellType)
cellModel.baseRemoteInfo = info
cellModel.shouldShowLeft = !message.isSelf
cellModel.v2msg = message
cellModel.timeStamp = message.createTime
array.append(cellModel)
}
}
return array
}
func createAnAILoadingModel(){
let cellModel = createCellModelWith(type: .aiLoading)
cellModel.isDeriveModel = true
cellModel.shouldShowLeft = true
cellModel.v2msg = nil
cellModel.timeStamp = TimeInterval(Date().timeStampInSeconds)
appendWith(model: cellModel)
}
func clearAILoadingModel(){
cellModels.removeAll(where: {$0.cellType == .aiLoading})
}
@discardableResult
func insertMessagesWith(models: [SessionBaseModel]) -> [Int] {
if Array.realEmpty(array: models) {
return []
}
let count = cellModels.count
for i in 0 ..< models.count {
let model = models[i]
if modelIsExist(model: model), !model.isDeriveModel, !model.shouldShowTips {
continue
}
insertMessageWith(model: model, needTime: cellModels.count != count)
}
var indexs = [Int]()
for i in 0 ..< cellModels.count - count {
indexs.append(i)
}
return indexs
}
/// model
func insertMessageWith(model: SessionBaseModel, needTime: Bool) {
//
let isCreateTimestampCell = (model.cellType == .create || model.cellType == .tips)
if isCreateTimestampCell {
if let theModel = cellModels.first, theModel.cellType != .timeStamp {
addTimestamp(model: theModel, index: 0)
}
} else {
if !shouldInsertTimestamp(model: model), needTime {
let model = cellModels.first
if model?.cellType == .timeStamp {
cellModels.removeFirst()
}
}
}
cellModels.insert(model, at: 0)
setIdIdctObject(model: model)
if !isCreateTimestampCell {
addTimestamp(model: model, index: 0)
}
}
func loadMessages(completion: @escaping ((_ firstPage: Bool, _ messageDatas: [V2NIMMessage]?, _ error: V2NIMError?) -> Void)){
let option = V2NIMMessageListOption()
var loadMoreMsgFlag = false
if let v2Msg = getLastMessageModel()?.v2msg{
option.anchorMessage = v2Msg
option.beginTime = v2Msg.createTime
loadMoreMsgFlag = true
}
option.conversationId = conversationId
NIMSDK.shared().v2MessageService.getMessageList(option) {[weak self] messages in
if let models = self?.modelWithV2(datas: messages){
// self?.cellModels = models
self?.insertMessagesWith(models: models)
completion(!loadMoreMsgFlag, messages,nil)
}
} failure: { error in
dlog("Get messages error:\(error)")
completion(!loadMoreMsgFlag, nil,error)
}
}
}
// MARK: - Messages
extension SessionUtil {
///
func sendMessage(message: V2NIMMessage) {
sendMessage(message: message) { error in
print(error ?? "")
}
}
///
func sendMessage(message: V2NIMMessage, completion: @escaping ((_ error: Error?) -> Void)) {
// See more params in https://doc.yunxin.163.com/messaging2/client-apis/DAxNjk0Mzc?platform=client#V2NIMSendMessageParams
let param = V2NIMSendMessageParams()
NIMSDK.shared().v2MessageService.send(message, conversationId: conversationId, params: param) { result in
dlog("✅send message ok \(message.text ?? "")")
} failure: { error in
dlog("❌send message failed: \(error)")
if error.code == 20000{
CLPurchase.shared.showChatModelsAndIAPEntrySheet()
}
}
// NIMSDK.shared().chatManager.send(message, to: session) { error in
// completion(error)
// }
}
}
// MARK: - Test
extension SessionUtil{
func testModes(completion: @escaping (() -> Void)) {
var array = [SessionBaseModel]()
do{
let cellModel = createCellModelWith(type: .aimsg)
cellModel.shouldShowLeft = true
let msg = V2NIMMessage()
msg.text = "(Watching her parents toast you respectfully, I feel very uncomfortable. After all, she has been standing on the top of the magic capital since she was a child. She has never seen her parents like this, but should I say that he is really handsome?) Are you?"
cellModel.v2msg = msg
cellModel.timeStamp = TimeInterval(Date().timeStamp - 1000)
array.append(cellModel)
}
do{
let cellModel = createCellModelWith(type: .text)
cellModel.shouldShowLeft = false
let msg = V2NIMMessage()
msg.text = "who are you"
cellModel.v2msg = msg
cellModel.timeStamp = TimeInterval(Date().timeStamp - 500)
array.append(cellModel)
}
cellModels = array
completion()
}
}