Browse Source

AI 聊天支持重新生成和复制

100Years 1 month ago
parent
commit
e22af9e912
23 changed files with 604 additions and 187 deletions
  1. 10 2
      AIEmoji.xcodeproj/project.pbxproj
  2. 22 0
      AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/Contents.json
  3. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/aichat_copy@2x.png
  4. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/aichat_copy@3x.png
  5. 17 3
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatMessage.swift
  6. 4 1
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatMessageUIModel.swift
  7. 11 1
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatUser.swift
  8. 41 11
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift
  9. 16 3
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift
  10. 3 4
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift
  11. 34 84
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+ChatDelegate.swift
  12. 14 6
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+SendMsg.swift
  13. 27 4
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController.swift
  14. 5 6
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift
  15. 5 3
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSMarkDownTool.swift
  16. 41 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSChatMsgBaseView.swift
  17. 113 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSChatMsgToolView.swift
  18. 1 1
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSMSGAIDefaultHeaderView.swift
  19. 152 22
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift
  20. 8 25
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift
  21. 2 2
      AIEmoji/Business/General/TSBigIconBrowseVC/TSBigIconBrowseVC.swift
  22. 71 0
      AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateViewModel.swift
  23. 7 9
      AIEmoji/Common/TSRealmManager/TSRealmManager.swift

+ 10 - 2
AIEmoji.xcodeproj/project.pbxproj

@@ -69,6 +69,8 @@
 		A80EDD622D6C3F82003CD332 /* MarkdownLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD232D6C3F82003CD332 /* MarkdownLink.swift */; };
 		A80EDD632D6C3F82003CD332 /* MarkdownAutomaticLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD222D6C3F82003CD332 /* MarkdownAutomaticLink.swift */; };
 		A80EDD642D6C3F82003CD332 /* MarkdownStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD322D6C3F82003CD332 /* MarkdownStyle.swift */; };
+		A80EDD682D6C5098003CD332 /* TSChatMsgBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */; };
+		A80EDD6A2D6C518E003CD332 /* TSChatMsgToolView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */; };
 		A85E478F2D67115A0018D62D /* TSTextGeneralPictureVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E478E2D6711590018D62D /* TSTextGeneralPictureVC.swift */; };
 		A85E47922D6728A00018D62D /* TSTextGeneralPictureVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E47912D67289F0018D62D /* TSTextGeneralPictureVM.swift */; };
 		A85E47962D672ADA0018D62D /* TSTextPicGennerateVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A85E47952D672AD90018D62D /* TSTextPicGennerateVC.swift */; };
@@ -235,6 +237,8 @@
 		A80EDD3B2D6C3F82003CD332 /* MarkdownLink+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MarkdownLink+UIKit.swift"; sourceTree = "<group>"; };
 		A80EDD3E2D6C3F82003CD332 /* UIFont+Traits.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Traits.swift"; sourceTree = "<group>"; };
 		A80EDD402D6C3F82003CD332 /* MarkdownParser+UIKit.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "MarkdownParser+UIKit.swift"; sourceTree = "<group>"; };
+		A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatMsgBaseView.swift; sourceTree = "<group>"; };
+		A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatMsgToolView.swift; sourceTree = "<group>"; };
 		A85E478E2D6711590018D62D /* TSTextGeneralPictureVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralPictureVC.swift; sourceTree = "<group>"; };
 		A85E47912D67289F0018D62D /* TSTextGeneralPictureVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextGeneralPictureVM.swift; sourceTree = "<group>"; };
 		A85E47952D672AD90018D62D /* TSTextPicGennerateVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextPicGennerateVC.swift; sourceTree = "<group>"; };
@@ -839,7 +843,9 @@
 		A85E47C12D6964500018D62D /* TSCellView */ = {
 			isa = PBXGroup;
 			children = (
+				A80EDD672D6C507D003CD332 /* TSChatMsgBaseView.swift */,
 				A85E47C22D69646D0018D62D /* TSMSGAIDefaultHeaderView.swift */,
+				A80EDD692D6C5176003CD332 /* TSChatMsgToolView.swift */,
 			);
 			path = TSCellView;
 			sourceTree = "<group>";
@@ -1547,10 +1553,12 @@
 				A80EDD592D6C3F82003CD332 /* MarkdownLink+UIKit.swift in Sources */,
 				A80EDD5A2D6C3F82003CD332 /* MarkdownParser.swift in Sources */,
 				A80EDD5B2D6C3F82003CD332 /* MarkdownCommonElement.swift in Sources */,
+				A80EDD682D6C5098003CD332 /* TSChatMsgBaseView.swift in Sources */,
 				A80EDD5C2D6C3F82003CD332 /* MarkdownList.swift in Sources */,
 				A80EDD5D2D6C3F82003CD332 /* MarkdownCodeEscaping.swift in Sources */,
 				A80EDD5E2D6C3F82003CD332 /* MarkdownLinkElement.swift in Sources */,
 				A80EDD5F2D6C3F82003CD332 /* MarkdownHeader+UIKit.swift in Sources */,
+				A80EDD6A2D6C518E003CD332 /* TSChatMsgToolView.swift in Sources */,
 				A80EDD602D6C3F82003CD332 /* MarkdownElement.swift in Sources */,
 				A80EDD612D6C3F82003CD332 /* MarkdownLink+AppKit.swift in Sources */,
 				A80EDD622D6C3F82003CD332 /* MarkdownLink.swift in Sources */,
@@ -1672,7 +1680,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.8;
+				MARKETING_VERSION = 1.9;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -1711,7 +1719,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.8;
+				MARKETING_VERSION = 1.9;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";

+ 22 - 0
AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/Contents.json

@@ -0,0 +1,22 @@
+{
+  "images" : [
+    {
+      "idiom" : "universal",
+      "scale" : "1x"
+    },
+    {
+      "filename" : "aichat_copy@2x.png",
+      "idiom" : "universal",
+      "scale" : "2x"
+    },
+    {
+      "filename" : "aichat_copy@3x.png",
+      "idiom" : "universal",
+      "scale" : "3x"
+    }
+  ],
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

BIN
AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/aichat_copy@2x.png


BIN
AIEmoji/Assets.xcassets/AIChat/aichat_copy.imageset/aichat_copy@3x.png


+ 17 - 3
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatMessage.swift

@@ -24,7 +24,7 @@ import Foundation
 import MessageKit
 import UIKit
 // MARK: - TSChatMessage
-internal class TSChatMessage: MessageType {
+internal class TSChatMessage: MessageType{
   // MARK: Lifecycle
 
     init(kind: MessageKind, user: TSChatUser, messageId: String, date: Date) {
@@ -60,9 +60,23 @@ internal class TSChatMessage: MessageType {
     }
     var sendState: TSProgressState = .none
     var appendDict:[TSChatMessageAppendDictKey:Any] = [:]
+    
+    var messageType:TSChatMessageMsgType = .aiRobotResponse
+    var markDownText:String = ""
 }
 
 // 定义一个枚举作为字典的键
-enum TSChatMessageAppendDictKey: String {
-    case AIDefaultHeader
+enum TSChatMessageAppendDictKey: String ,Equatable{
+    case topView
+    case centerView
+    case bottomView
+    
+    case centerBottomView
+}
+
+// 定义一个枚举作为消息体
+enum TSChatMessageMsgType: String ,Equatable{
+    case aiRobotWelcome     = "aiRobotWelcome"
+    case userQuestion       = "userQuestion"
+    case aiRobotResponse    = "aiRobotResponse"
 }

+ 4 - 1
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatMessageUIModel.swift

@@ -7,6 +7,9 @@
 
 class TSChatMessageUIBaseModel: TSBaseModel {
     var text:String = ""
+    
+    var view:TSChatMsgBaseView = TSChatMsgBaseView()
     var height:CGFloat = 0.0
-    var view:UIView = UIView()
+    
 }
+

+ 11 - 1
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatUser.swift

@@ -22,13 +22,23 @@
 
 import Foundation
 import MessageKit
+
+
+// 定义一个枚举作为字典的键
+enum TSChatUserIDType: String {
+    case aiRobot = "aiRobot"
+    case user = "user"
+}
+
 struct TSChatUser: SenderType {
     var senderId: String = ""
     var displayName: String = ""
+    var idType: TSChatUserIDType = .aiRobot
     
-    init(senderId: String, displayName: String) {
+    init(senderId: String, displayName: String, idType: TSChatUserIDType = .aiRobot) {
         self.senderId = senderId
         self.displayName = displayName
+        self.idType = idType
     }
 }
 

+ 41 - 11
AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift

@@ -118,13 +118,20 @@ extension TSDBAIChatList {
     
     func getTSDBChatMessage(chatMsg:TSChatMessage) -> TSDBChatMessage {
         let keyValue = chatMsg.kind.keyValue
-        
+        let sendStateKeyValue = chatMsg.sendState.keyValue
+    
         let dbModel = TSDBChatMessage()
-        dbModel.user = TSDBChatUser(senderId: chatMsg.user.senderId, displayName: chatMsg.user.displayName)
+        dbModel.user = TSDBChatUser(chatUser: chatMsg.user)
         dbModel.messageId = chatMsg.messageId
         dbModel.sentDate = chatMsg.sentDate
         dbModel.kindKey = keyValue.0
         dbModel.kindValue = keyValue.1
+        
+        dbModel.sendStateKey = sendStateKeyValue.0
+        dbModel.sendStateValue = sendStateKeyValue.1
+        
+        dbModel.messageType = chatMsg.messageType.rawValue
+        dbModel.markDownText = chatMsg.markDownText
         return dbModel
     }
     
@@ -134,10 +141,13 @@ extension TSDBAIChatList {
 //MARK: extension
 extension MessageKind {
 
-    static func fromKeyValue(key string: String,value:String) -> MessageKind {
+    static func fromKeyValue(key string: String,value:String,chatUser:TSChatUser) -> MessageKind {
         if string == "text" {
             return .text(value)
         } else if string == "attributedText" {
+            if chatUser.idType == .user {
+                return .attributedText(kMDSendAttributedString(text: value))
+            }
             return .attributedText(kMDAttributedString(text: value))
         } else {
             return .custom(nil)
@@ -166,16 +176,32 @@ extension MessageKind {
 class TSDBChatMessage: Object {
     @objc dynamic var messageId: String = UUID().uuidString
     @objc dynamic var sentDate: Date = Date()
-    @objc dynamic var user: TSDBChatUser? = TSDBChatUser(senderId: "", displayName: "")
+    @objc dynamic var user: TSDBChatUser? = TSDBChatUser(senderId: "", displayName: "",idType: .aiRobot)
     @objc dynamic var kindKey: String = ""
     @objc dynamic var kindValue: String = ""
+    @objc dynamic var messageType: String = TSChatMessageMsgType.aiRobotResponse.rawValue
+    
+    @objc dynamic var sendStateKey: String = ""
+    @objc dynamic var sendStateValue: String = ""
+
+    @objc dynamic var markDownText: String = ""
     
     override static func primaryKey() -> String? {
         return "messageId"
     }
     
     func getTSChatMessage() -> TSChatMessage {
-        let model = TSChatMessage(kind: .fromKeyValue(key: kindKey, value: kindValue), user: user!.getTSChatUser(), messageId: messageId, date: sentDate)
+        let chatUser = user!.getTSChatUser()
+
+        var textValue = kindValue
+        if markDownText.count > 0  {
+            textValue = markDownText
+        }
+
+        let model = TSChatMessage(kind: .fromKeyValue(key: kindKey, value: textValue,chatUser: chatUser), user: chatUser, messageId: messageId, date: sentDate)
+        model.messageType = TSChatMessageMsgType(rawValue: messageType) ?? .aiRobotResponse
+        model.sendState = .fromKeyValue(key: sendStateKey, value: sendStateValue)
+        model.markDownText = markDownText
         return model
     }
     
@@ -188,21 +214,25 @@ class TSDBChatMessage: Object {
 class TSDBChatUser: Object {
     @objc dynamic var senderId: String = ""
     @objc dynamic var displayName: String = ""
-    
+    @objc dynamic var idType: String = TSChatUserIDType.aiRobot.rawValue
     func getTSChatUser() -> TSChatUser {
-        let user = TSChatUser(senderId: senderId, displayName: displayName)
+        let user = TSChatUser(senderId: senderId, displayName: displayName,idType: TSChatUserIDType(rawValue: idType) ?? .aiRobot)
         return user
     }
     
-    init(senderId: String, displayName: String) {
+    init(chatUser:TSChatUser){
+        self.senderId = chatUser.senderId
+        self.displayName = chatUser.displayName
+        self.idType = chatUser.idType.rawValue
+    }
+    
+    init(senderId: String, displayName: String,idType:TSChatUserIDType) {
         self.senderId = senderId
         self.displayName = displayName
+        self.idType = idType.rawValue
     }
     
     override init() {
         super.init()
     }
 }
-
-
-

+ 16 - 3
AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift

@@ -58,7 +58,12 @@ class TSLayoutSizeCalculator: MessageSizeCalculator {//CellSizeCalculator {
         return cellMessageContainerMaxWidth
     }()
 
-
+    
+    //消息下面的工具栏高度 30.0
+    static var cellMessageToolViewH:CGFloat = 30.0
+    
+    
+    
     var messagesDataSource: MessagesDataSource {
         self.messagesLayout.messagesDataSource
     }
@@ -78,11 +83,19 @@ class TSLayoutSizeCalculator: MessageSizeCalculator {//CellSizeCalculator {
         
         if let msgModel = message as? TSChatMessage {
             //有ai机器人头部imageview
-            if let uiBaseModel = msgModel.appendDict[.AIDefaultHeader] as? TSChatMessageUIBaseModel {
+            if let uiBaseModel = msgModel.appendDict[.topView] as? TSChatMessageUIBaseModel {
                 height+=uiBaseModel.height
             }
-        }
 
+            //消息底部工具栏,是ai 机器人,且是ai 回答问题,且回答已经结束
+            if msgModel.messageType == .aiRobotResponse,
+               msgModel.sendState.isResult
+//                ,msgModel.user.idType == .aiRobot
+            {
+                height = height + Self.cellMessageToolViewH
+            }
+        }
+        
         return height
     }
     

+ 3 - 4
AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift

@@ -17,8 +17,7 @@ class TSTextLayoutSizeCalculator: TSLayoutSizeCalculator {
     static var cellMessagelabelEdge:UIEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
     static var messageLabelFont = UIFont.font(size: 16.0)
     static var cellMessagelabelMinSize:CGSize = CGSizeMake(24, 24)
-    
-    
+
     var cellMessagelabelLeadingMaxWidth:CGFloat{
         messageContainerLeadingMaxWidth - Self.cellMessagelabelEdge.left - Self.cellMessagelabelEdge.right
     }
@@ -42,6 +41,7 @@ class TSTextLayoutSizeCalculator: TSLayoutSizeCalculator {
         let messagecontainerH = size.height + Self.cellMessagelabelEdge.top + Self.cellMessagelabelEdge.bottom
         
         height = height + messagecontainerH
+        
         let minHeight = Self.cellMessageContainerMinSize.height
         if height < minHeight {
             height = minHeight
@@ -70,8 +70,7 @@ class TSTextLayoutSizeCalculator: TSLayoutSizeCalculator {
         //用 label 计算更加精准
         var size = UILabel.getAttributedTextSize(attributedText: attributedText, maxWidth: maxWidth)
 //        debugPrint("attributedText size=\(size)")
-        
-        var size1 = attributedText.size(consideringWidth: maxWidth)
+//        var size1 = attributedText.size(consideringWidth: maxWidth)
 
         let minSize = Self.cellMessagelabelMinSize
         if size.width < minSize.width {

+ 34 - 84
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+ChatDelegate.swift

@@ -10,10 +10,6 @@ import UIKit
 import MapKit
 
 
-
-
-
-
 // MARK: MessagesDataSource
 extension TSChatViewController {
     
@@ -211,93 +207,47 @@ extension TSChatViewController: MessagesDisplayDelegate {
 // MARK: MessageCellDelegate
 
 extension TSChatViewController: MessageCellDelegate {
-    func didTapAvatar(in _: MessageCollectionViewCell) {
-        print("Avatar tapped")
-    }
-    
-    func didTapMessage(in _: MessageCollectionViewCell) {
-        print("Message tapped")
-    }
-    
-    func didTapImage(in _: MessageCollectionViewCell) {
-        print("Image tapped")
-    }
-    
-    func didTapCellTopLabel(in _: MessageCollectionViewCell) {
-        print("Top cell label tapped")
-    }
-    
-    func didTapCellBottomLabel(in _: MessageCollectionViewCell) {
-        print("Bottom cell label tapped")
-    }
-    
-    func didTapMessageTopLabel(in _: MessageCollectionViewCell) {
-        print("Top message label tapped")
-    }
-    
-    func didTapMessageBottomLabel(in _: MessageCollectionViewCell) {
-        print("Bottom label tapped")
-    }
-    
-    func didTapPlayButton(in cell: AudioMessageCell) {
-        guard
-            let indexPath = messagesCollectionView.indexPath(for: cell),
-            let message = messagesCollectionView.messagesDataSource?.messageForItem(at: indexPath, in: messagesCollectionView)
-        else {
-            print("Failed to identify message when audio cell receive tap gesture")
-            return
+    func didTapBackground(in cell: MessageCollectionViewCell) {
+        
+        guard let msgCell = cell as? TSMessageContentCell else { return }
+        
+        guard let indexPath = messagesCollectionView.indexPath(for: cell) else{ return }
+        
+        guard let cellTapType = msgCell.cellTapType else{ return }
+        
+        guard let model = messageList.safeObj(At: indexPath.item) else{ return }
+
+        switch cellTapType {
+        case .copyMsg:
+            if case let .attributedText(text) = model.kind{
+                //拷贝文字到截切板
+                UIPasteboard.general.string = text.string
+                kSavePhotoSuccesswShared.show(atView: self.view,text: "Copy Successfully".localized,showViewBtn:false)
+            }
+            
+        case .refreshMsg:
+            if case let .attributedText(text) = model.kind{
+            
+                guard let questionModel = messageList.safeObj(At: indexPath.item-1) else{ return }
+                var questionString = ""
+                if case let .attributedText(text) = questionModel.kind{
+                    questionString = text.string
+                }else if case let .text(text) = questionModel.kind{
+                    questionString = text
+                }
+                
+                if questionString.count > 0 {
+                    generativeAIChat(questionString: questionString,indexPath:indexPath)
+                }
+            }
+        
         }
     }
-    
-    func didStartAudio(in _: AudioMessageCell) {
-        print("Did start playing audio sound")
-    }
-    
-    func didPauseAudio(in _: AudioMessageCell) {
-        print("Did pause audio sound")
-    }
-    
-    func didStopAudio(in _: AudioMessageCell) {
-        print("Did stop audio sound")
-    }
-    
-    func didTapAccessoryView(in _: MessageCollectionViewCell) {
-        print("Accessory view tapped")
-    }
 }
 
-
 // MARK: MessageLabelDelegate
 
 extension TSChatViewController: MessageLabelDelegate {
-    func didSelectAddress(_ addressComponents: [String: String]) {
-        print("Address Selected: \(addressComponents)")
-    }
-    
-    func didSelectDate(_ date: Date) {
-        print("Date Selected: \(date)")
-    }
-    
-    func didSelectPhoneNumber(_ phoneNumber: String) {
-        print("Phone Number Selected: \(phoneNumber)")
-    }
-    
-    func didSelectURL(_ url: URL) {
-        print("URL Selected: \(url)")
-    }
-    
-    func didSelectTransitInformation(_ transitInformation: [String: String]) {
-        print("TransitInformation Selected: \(transitInformation)")
-    }
-    
-    func didSelectHashtag(_ hashtag: String) {
-        print("Hashtag selected: \(hashtag)")
-    }
-    
-    func didSelectMention(_ mention: String) {
-        print("Mention selected: \(mention)")
-    }
-    
     func didSelectCustom(_ pattern: String, match _: String?) {
         print("Custom data detector patter selected: \(pattern)")
     }

+ 14 - 6
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+SendMsg.swift

@@ -29,6 +29,7 @@ extension TSChatViewController {
 //                let message = TSChatMessage(text: str, user: user, messageId: UUID().uuidString, date: Date())
                 let attri = kMDSendAttributedString(text: str)
                 let message = TSChatMessage(attributedText: attri, user: user, messageId: UUID().uuidString, date: Date())
+                message.messageType = .userQuestion
                 insertMessage(message)
                 //保存这条消息到本地数据库
                 //发送消息后,进行AI 对话生成
@@ -37,7 +38,7 @@ extension TSChatViewController {
         }
     }
     
-    func generativeAIChat(message:TSChatMessage) {
+    func generativeAIChat(message:TSChatMessage,indexPath:IndexPath? = nil) {
         var messageString = ""
         switch message.kind {
         case .text(let message):
@@ -52,19 +53,26 @@ extension TSChatViewController {
             return
         }
         
+        generativeAIChat(questionString: messageString, indexPath: indexPath)
+    }
+    
+    func generativeAIChat(questionString:String,indexPath:IndexPath? = nil) {
+        
         //先插入回答的消息,转圈加载
         let message = TSChatMessage(attributedText: kMDAttributedString(text: ""), user: viewModel.kAIUser, messageId: UUID().uuidString, date: Date())
         message.sendState = .start
-        insertMessage(message)
+        message.messageType = .aiRobotResponse
+        insertMessage(message,indexPath: indexPath)
+        scrollToBottom()
         NotificationCenter.default.post(name: .kAIAnsweringNotification, object: nil, userInfo: [kIsAIAnswering: true])
 
         //每次全部输出
-        viewModel.sendChatMessage(message: messageString) {[weak self] string in
+        viewModel.sendChatMessage(message: questionString) {[weak self] string in
             guard let self = self else { return }
             debugPrint("viewModel.AiMDString=\(viewModel.AiMDString)")
             message.kind = .attributedText(kMDAttributedString(text: viewModel.AiMDString))
             message.sendState = .progress(0.5)
-
+            message.markDownText = viewModel.AiMDString
             if self.scrollToBottomButton.isHidden == true {
                 updataAIChatCellUI()
                 self.messagesCollectionView.scrollToLastItem(animated: false)
@@ -85,12 +93,12 @@ extension TSChatViewController {
             guard let self = self else { return }
             if let _ = data {
                 message.sendState = .success("netData")
+//                message.sendState = .failed(kAIErrorString)
 //                kPurchaseDefault.useOnceForFree(type: .aichat)//消耗一次 AI 次数
-                message.kind = .attributedText(kMDAttributedString(text: viewModel.AiMDString))
             }else {
-                message.kind = .attributedText(kMDAttributedString(text: kAIErrorString))
                 message.sendState = .failed(kAIErrorString)
             }
+            message.markDownText = viewModel.AiMDString
             updataAIChatCellUI()
             
             kExecuteOnMainThread {

+ 27 - 4
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController.swift

@@ -152,12 +152,22 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
         return IndexPath(item: 0, section: 0)
     }
     
-    func insertMessage(_ message: TSChatMessage) {
-        messageList.append(message)
+    func insertMessage(_ message: TSChatMessage,indexPath:IndexPath? = nil) {
+//        messageList.append(message)
+        let isReplace = replaceOrAppend(message, indexPath: indexPath)
+        var cellIndexPaht = lastIndexPath
+        if let indexPath = indexPath{
+            cellIndexPaht = indexPath
+        }
         messagesCollectionView.performBatchUpdates({
-            messagesCollectionView.insertItems(at: [lastIndexPath])
+            if isReplace == false {
+                messagesCollectionView.insertItems(at: [cellIndexPaht])
+            }
+        
             if messageList.count >= 2 {
-                messagesCollectionView.reloadItems(at: [lastIndexPath])
+                messagesCollectionView.reloadItems(at: [cellIndexPaht])
+            }else{
+                messagesCollectionView.reloadData()
             }
         }, completion: { [weak self] _ in
             if self?.isLastSectionVisible() == true {
@@ -166,6 +176,19 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
         })
     }
     
+    /// 返回值,代表是否替换
+    func replaceOrAppend(_ message: TSChatMessage,indexPath:IndexPath?) ->Bool {
+        if let indexPath = indexPath {
+            let index = indexPath.item
+            if index >= 0 && index < messageList.count {
+                messageList[index] = message// 下标存在,替换该位置的元素
+                return true
+            }
+        }
+        messageList.append(message)// 插入新元素
+        return false
+    }
+    
     
     func isLastSectionVisible() -> Bool {
         guard !messageList.isEmpty else { return false }

+ 5 - 6
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift

@@ -23,8 +23,8 @@ class TSAIChatVM {
     var uiStyle:UIStype = UIStype.chat
     var streamRequest:StreamPostRequest?
     
-    let kAIUser = TSChatUser(senderId: "000", displayName: "AI")
-    let kUserSender = TSChatUser(senderId: "001", displayName: "001")
+    let kAIUser = TSChatUser(senderId: "000", displayName: "AI",idType: .aiRobot)
+    let kUserSender = TSChatUser(senderId: "001", displayName: "001",idType: .user)
     
     
     //ai markDown 回答的string
@@ -83,16 +83,15 @@ extension TSAIChatVM {
         if uiStyle == .history {
             return self.dbAIChatList.getMessageList()
         }else {
-//            let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n\n📧 Composing high-quality emails\n\n🇺🇸 Facilitating language learning\n\n📑 Assisting in your studies\n\n💡Brainstorming ideas\n\nand much more!"
-            
             let aiString = "I can tackle your questions, my skillset includes, but is not limited to:\n📧 Composing high-quality emails\n🇺🇸 Facilitating language learning\n📑 Assisting in your studies\n💡Brainstorming ideas\nand much more!"
             let msg = TSChatMessage(kind: .attributedText(kMDAttributedString(text: aiString)), user: kAIUser, messageId: "", date: Date())
 
             let model = TSChatMessageUIBaseModel()
             model.height = 52.0
             model.view = TSMSGAIDefaultHeaderView(text: "Greetings! Curious about\nwhat I can do?".localized)
-
-            msg.appendDict = [.AIDefaultHeader:model]
+            msg.appendDict = [.topView:model]
+            msg.messageType = .aiRobotWelcome
+            
             return [msg]
         }
     }

+ 5 - 3
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSMarkDownTool.swift

@@ -25,7 +25,6 @@ func kMDSendAttributedString(text:String) -> NSAttributedString{
     ]
     
     let attributedString = NSMutableAttributedString(string: text, attributes: attributes)
-//    attributedString.addAttribute(.paragraphStyle, value: paragraphStyle, range: NSRange(location: 0, length: attributedString.length))
     return attributedString
 }
 
@@ -35,8 +34,11 @@ var md: MarkdownParser = {
     parser.enabledElements = .disabledAutomaticLink
     let code = TSCustomMarkdownCode()
     code.textBackgroundColor = .clear
-    code.color = .white.withAlphaComponent(0.8)
-    code.textHighlightColor = .white.withAlphaComponent(0.8)
+    
+    let codeColor = UIColor.white.withAlphaComponent(0.8)
+    code.color = codeColor
+    code.textHighlightColor = codeColor
+    
     parser.replaceDefaultElement(parser.code, with: code)
     return parser
 }()

+ 41 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSChatMsgBaseView.swift

@@ -0,0 +1,41 @@
+//
+//  TSChatMsgBaseView.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/23.
+//
+
+
+
+
+import MessageKit
+class TSChatMsgBaseView: TSBaseView {
+//    open weak var messageCellDelegate: MessageCellDelegate?
+//    open weak var messageCollectionViewCell:MessageCollectionViewCell?
+    var didTapCustomViewBlock:((TSChatMsgViewTapType)->Void)?
+    open var indexPath:IndexPath?
+
+    override func creatUI() {
+        
+        
+    }
+}
+// 定义一个枚举作为字典的键
+public enum TSChatMsgViewTapType {
+    case copyMsg
+    case refreshMsg
+}
+public extension MessageCellDelegate {
+
+    func didTapCustomView(msgCell: MessageCollectionViewCell?, pattern: TSChatMsgViewTapType, match: String?) {
+        debugPrint("MessageCellDelegate extension didTapCustomView")
+    }
+}
+
+
+//public protocol MessageCellDelegate: MessageLabelDelegate {
+//    
+//    
+//    
+//}
+

+ 113 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSChatMsgToolView.swift

@@ -0,0 +1,113 @@
+//
+//  TSChatMsgToolView.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/23.
+//
+
+
+//30
+class TSChatMsgToolView: TSChatMsgBaseView {
+    var viewHeight = TSLayoutSizeCalculator.cellMessageToolViewH
+
+    var isSuccess:Bool = true {
+        didSet{
+            if isSuccess {
+                successView.isHidden = false
+                failView.isHidden = true
+            }else {
+                successView.isHidden = true
+                failView.isHidden = false
+            }
+        }
+    }
+    
+    lazy var copyBtn: UIButton = {
+        let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
+            guard let self = self else { return }
+            didTapCustomViewBlock?(.copyMsg)
+        }
+        return copyBtn
+    }()
+    lazy var successView: UIView = {
+        let successView = UIView()
+        
+        let copyBtn = UIButton.createButton(image: UIImage(named: "aichat_copy")) { [weak self]  in
+            guard let self = self else { return }
+            didTapCustomViewBlock?(.copyMsg)
+        }
+    
+        successView.addSubview(copyBtn)
+        copyBtn.snp.makeConstraints { make in
+            make.leading.equalTo(12)
+            make.top.equalToSuperview()
+            make.width.height.equalTo(20)
+        }
+        
+        return successView
+    }()
+    
+    lazy var failView: UIView = {
+        let failView = UIView()
+        
+        let refreshBtn = UIButton.createButton(image: UIImage(named: "refresh")) { [weak self]  in
+            guard let self = self else { return }
+            didTapCustomViewBlock?(.refreshMsg)
+        }
+    
+        failView.addSubview(refreshBtn)
+        refreshBtn.snp.makeConstraints { make in
+            make.leading.equalTo(12)
+            make.top.equalToSuperview()
+            make.width.height.equalTo(20)
+        }
+        
+        return failView
+    }()
+    
+    
+    override func creatUI() {
+        
+        self.clipsToBounds = true
+//        contentView.backgroundColor = .green
+        contentView.addSubview(successView)
+        successView.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.trailing.equalTo(0)
+            make.top.bottom.equalToSuperview()
+            make.height.equalTo(viewHeight)
+        }
+        
+        contentView.addSubview(failView)
+        failView.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.trailing.equalTo(0)
+            make.top.bottom.equalToSuperview()
+            make.height.equalTo(viewHeight)
+        }
+        
+//        setUpSuccess()
+    }
+
+//    func setUpSuccess(){
+//        successView.addSubview(copyBtn)
+//        copyBtn.snp.makeConstraints { make in
+//            make.leading.equalTo(12)
+//            make.top.equalToSuperview()
+//            make.width.height.equalTo(20)
+//        }
+//    }
+    
+    func setHidden(isHidden:Bool) {
+        self.isHidden = isHidden
+        let h = isHidden ? 0 : viewHeight
+    
+        successView.snp.updateConstraints { make in
+            make.height.equalTo(h)
+        }
+        
+        failView.snp.updateConstraints { make in
+            make.height.equalTo(h)
+        }
+    }
+}

+ 1 - 1
AIEmoji/Business/AIChat/TSChatViewController/Views/TSCellView/TSMSGAIDefaultHeaderView.swift

@@ -5,7 +5,7 @@
 //  Created by 100Years on 2025/2/21.
 //
 
-class TSMSGAIDefaultHeaderView: TSBaseView {
+class TSMSGAIDefaultHeaderView: TSChatMsgBaseView {
     
     var text:String
     init(text:String) {

+ 152 - 22
AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift

@@ -24,7 +24,7 @@ class TSMessageContentCell: MessageCollectionViewCell {
     
     /// The `MessageCellDelegate` for the cell.
     weak var delegate: MessageCellDelegate?
-    
+    var cellTapType:TSChatMsgViewTapType?
     /// 顶部内容
     var topContainerView: UIView = {
         let containerView = UIView()
@@ -39,6 +39,7 @@ class TSMessageContentCell: MessageCollectionViewCell {
 //        containerView.backgroundColor = .yellow
         return containerView
     }()
+
     
     /// 底部内容
     var bottomContainerView: UIView = {
@@ -48,12 +49,30 @@ class TSMessageContentCell: MessageCollectionViewCell {
     }()
     
     
+    
+    
     /// 消息内容容器.frame由 子视图自动布局撑起大小
     var messageContainerView: UIView = {
         let containerView = UIView()
         containerView.cornerRadius = 16
         return containerView
     }()
+    
+    var messageTopContainerView: UIView = {
+        let containerView = UIView()
+//        containerView.backgroundColor = .orange
+        return containerView
+    }()
+    var messageCenterContainerView: UIView = {
+        let containerView = UIView()
+        return containerView
+    }()
+    var messageBottomContainerView: UIView = {
+        let containerView = UIView()
+//        containerView.backgroundColor = .purple
+        return containerView
+    }()
+    
 
     // 左头像
     var leadingAvatarImageView: UIImageView = {
@@ -70,6 +89,20 @@ class TSMessageContentCell: MessageCollectionViewCell {
     }()
     
     
+    //工具栏,复制,copy 等
+    lazy var msgToolView: TSChatMsgToolView = {
+        let msgToolView = TSChatMsgToolView()
+        msgToolView.didTapCustomViewBlock = {[weak self] tapType in
+            guard let self = self else { return }
+            if let delegate = delegate{
+                cellTapType = tapType
+                delegate.didTapBackground(in: self)
+            }
+        }
+        return msgToolView
+    }()
+ 
+    
     //初始化 cell
     func setupSubviews() {
         contentView.addSubview(topContainerView)
@@ -93,10 +126,9 @@ class TSMessageContentCell: MessageCollectionViewCell {
         setUpBottomContainerView()
     }
     
-    
-    
-    
+
     func setUpTopContainerView() {
+        
     }
     
     func setUpCenterContainerView() {
@@ -104,7 +136,6 @@ class TSMessageContentCell: MessageCollectionViewCell {
         centerContainerView.addSubview(messageContainerView)
         centerContainerView.addSubview(trailingAvatarImageView)
         
-        
         let leadingAvatarEdge = TSLayoutSizeCalculator.leadingAvatarEdge
         let leadingAvatarSize = TSLayoutSizeCalculator.leadingAvatarSize
         leadingAvatarImageView.snp.makeConstraints { make in
@@ -130,6 +161,35 @@ class TSMessageContentCell: MessageCollectionViewCell {
             make.bottom.equalTo(-cellMessageContainerEdge.bottom)
         }
         
+        
+        setUpMessageContainerView()
+    }
+    
+    func setUpMessageContainerView(){
+        messageContainerView.addSubview(messageTopContainerView)
+        messageContainerView.addSubview(messageCenterContainerView)
+        messageContainerView.addSubview(messageBottomContainerView)
+        
+        
+        messageTopContainerView.snp.makeConstraints { make in
+            make.leading.top.trailing.equalTo(0)
+        }
+        messageCenterContainerView.snp.makeConstraints { make in
+            make.top.equalTo(messageTopContainerView.snp.bottom)
+            make.leading.trailing.equalTo(0)
+        }
+        
+        messageBottomContainerView.snp.makeConstraints { make in
+            make.top.equalTo(messageCenterContainerView.snp.bottom)
+            make.leading.trailing.bottom.equalTo(0)
+        }
+        
+        //添加工具栏
+        messageBottomContainerView.addSubview(msgToolView)
+        msgToolView.snp.makeConstraints { make in
+            make.leading.trailing.top.bottom.equalTo(0)
+        }
+        
     }
     
     func setUpBottomContainerView() {
@@ -141,19 +201,18 @@ class TSMessageContentCell: MessageCollectionViewCell {
     
     // 点击触发事件
     override func handleTapGesture(_ gesture: UIGestureRecognizer) {
-        let touchLocation = gesture.location(in: self)
-        
-        switch true {
-        case messageContainerView.frame
-                .contains(touchLocation) && !cellContentView(canHandle: convert(touchLocation, to: messageContainerView)):
-            delegate?.didTapMessage(in: self)
+//        let touchLocation = gesture.location(in: self)
+//        switch true {
+//        case messageContainerView.frame
+//                .contains(touchLocation) && !cellContentView(canHandle: convert(touchLocation, to: messageContainerView)):
+//            delegate?.didTapMessage(in: self)
 //        case cellTopLabel.frame.contains(touchLocation):
 //            delegate?.didTapCellTopLabel(in: self)
 //    case cellDateLabel.frame.contains(touchLocation):
 //      delegate?.didTapMessageBottomLabel(in: self)
-        default:
-            delegate?.didTapBackground(in: self)
-        }
+//        default:
+//            delegate?.didTapBackground(in: self)
+//        }
     }
     
     /// Handle long press gesture, return true when gestureRecognizer's touch point in `messageContainerView`'s frame
@@ -174,7 +233,7 @@ class TSMessageContentCell: MessageCollectionViewCell {
         guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
             return
         }
-
+        
         //是否是当前用户发送的消息
         let fromCurrentSender = dataSource.isFromCurrentSender(message: message)
         if fromCurrentSender {
@@ -198,26 +257,97 @@ class TSMessageContentCell: MessageCollectionViewCell {
         }
         
         messageContainerView.backgroundColor = displayDelegate.backgroundColor(for: message,at: indexPath,in: messagesCollectionView)
-        handleTopContainerViewChanged(message: message)
+        handleTopContainerViewChanged(message: message,messagesCollectionView: messagesCollectionView,dataSource: dataSource)
     }
     
-    func handleTopContainerViewChanged(message: MessageType) {
+    func handleTopContainerViewChanged(
+        message: MessageType,
+        messagesCollectionView: MessagesCollectionView,
+        dataSource: MessagesDataSource
+    ) {
         topContainerView.removeAllSubviews()
+        delegate = messagesCollectionView.messageCellDelegate
+        
         if let msgModel = message as? TSChatMessage {
-            //有ai机器人头部imageview
-            if let uiBaseModel = msgModel.appendDict[.AIDefaultHeader] as? TSChatMessageUIBaseModel {
-                let aiDefaultHeaderView = uiBaseModel.view
-                topContainerView.addSubview(aiDefaultHeaderView)
-                aiDefaultHeaderView.snp.makeConstraints { make in
+            if let uiBaseModel = msgModel.appendDict[.topView] as? TSChatMessageUIBaseModel {
+                let modelView = uiBaseModel.view
+                topContainerView.addSubview(modelView)
+                modelView.snp.makeConstraints { make in
                     make.leading.trailing.top.bottom.equalTo(0)
                     make.height.equalTo(uiBaseModel.height)
                 }
             }
         }
+        
+        
+        handelMsgToolView(message: message, dataSource: dataSource)
+    }
+    
+    
+    
+    
+    
+    func handelMsgToolView(message: MessageType,dataSource: MessagesDataSource){
+        if let msgModel = message as? TSChatMessage,let chatVC = dataSource as? TSChatViewController{
+    
+            var isSuccess = msgModel.sendState.reslutSuccess
+            let isLast = isLast(message: message, dataSource: dataSource)
+            
+            //重试只在显示在最后一个 cell,所以cell 是失败了,只要不是最后一个 cell,都要显示成功
+            //历史记录中,不能显示重试
+            if isLast == false || chatVC.viewModel.uiStyle == .history {
+                isSuccess = true
+            }
+            //成功显示复制,失败显示重试
+            msgToolView.isSuccess = isSuccess
+            
+            var isHidden = true
+            if msgModel.messageType == TSChatMessageMsgType.aiRobotResponse,
+               msgModel.sendState.isResult{
+                //是机器人的回答,且回答已经结束,就可以显示工具栏了
+                isHidden = false
+            }
+
+            self.msgToolView.setHidden(isHidden: isHidden)
+        }
     }
     
+    
+    
     /// Handle `ContentView`'s tap gesture, return false when `ContentView` doesn't needs to handle gesture
     func cellContentView(canHandle _: CGPoint) -> Bool {
         false
     }
 }
+
+
+
+
+
+
+extension TSMessageContentCell {
+
+    func isLastCell(in collectionView: UICollectionView) -> Bool {
+        if let indexPath = collectionView.indexPath(for: self),
+           let dataSource = collectionView.dataSource {
+            let itemCount = dataSource.collectionView(collectionView, numberOfItemsInSection: indexPath.section)
+            return indexPath.item == itemCount - 1
+        }
+        return false
+    }
+    
+    
+    func isLast(message: MessageType,dataSource: MessagesDataSource)->Bool{
+        if let chatVC = dataSource as? TSChatViewController,
+           let msg = message as? TSChatMessage
+        {
+            let array = chatVC.messageList
+            // 判断对象是否在数组中是最后一个元素
+            if let index = array.firstIndex(where: { $0 === msg }), index == array.count - 1 {
+                return true
+            }
+        }
+        
+        return false
+    }
+}

+ 8 - 25
AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift

@@ -23,25 +23,15 @@ class TSTextMessageContentCell: TSMessageContentCell {
         return activityIndicator
     }()
     
-    
-    
     lazy var refreshBtn: UIButton = {
         let refresh = UIButton.createButton(image: UIImage(named: "aichat_refresh")) { [weak self]  in
             guard let self = self else { return }
-            
-            
+
         }
         return refresh
     }()
     
-    lazy var bottomToolView: UIView = {
-        bottomToolView = UIView()
-        bottomToolView.isHidden = true
-        return bottomToolView
-    }()
-    
-    
-    
+
     override func prepareForReuse() {
         super.prepareForReuse()
     }
@@ -50,7 +40,7 @@ class TSTextMessageContentCell: TSMessageContentCell {
         super.setupSubviews()
         
         let labelEdge = TSTextLayoutSizeCalculator.cellMessagelabelEdge
-        messageContainerView.addSubview(messageLabel)
+        messageCenterContainerView.addSubview(messageLabel)
         messageLabel.snp.makeConstraints { make in
             make.edges.equalTo(labelEdge)
             make.width.lessThanOrEqualTo(300) // 设置最大宽度为 300
@@ -58,23 +48,13 @@ class TSTextMessageContentCell: TSMessageContentCell {
             make.height.greaterThanOrEqualTo(24.0)
         }
         
-        messageContainerView.addSubview(activityIndicator)
+        messageCenterContainerView.addSubview(activityIndicator)
         activityIndicator.snp.makeConstraints { make in
             make.top.equalTo(12)
             make.left.equalTo(12)
             make.width.height.equalTo(24.0)
         }
         
-        
-        bottomToolView.addSubview(refreshBtn)
-        refreshBtn.snp.makeConstraints { make in
-            make.left.equalTo(12)
-            make.bottom.equalTo(-9)
-            make.width.height.equalTo(20)
-        }
-        
-        
-        
     }
     
     override func configure(
@@ -98,8 +78,11 @@ class TSTextMessageContentCell: TSMessageContentCell {
 //            make.height.equalTo(labelFrame.height)
 //        }
         
+        
+        let isCurrentSender = dataSource.isFromCurrentSender(message: message)
+
         //label 高度自适应
-        let maxWidth = calculator?.cellMessagelabelMaxWidth(fromCurrentSender: dataSource.isFromCurrentSender(message: message)) ?? TSTextLayoutSizeCalculator.cellMessagelabelMinSize.width
+        let maxWidth = calculator?.cellMessagelabelMaxWidth(fromCurrentSender: isCurrentSender) ?? TSTextLayoutSizeCalculator.cellMessagelabelMinSize.width
         messageLabel.snp.updateConstraints { make in
             make.width.lessThanOrEqualTo(maxWidth)
         }

+ 2 - 2
AIEmoji/Business/General/TSBigIconBrowseVC/TSBigIconBrowseVC.swift

@@ -119,9 +119,9 @@ class TSBigIconBrowseVC: TSBottomAlertVC {
             return
         }
   
-        if let image = currentImage{
+        if let model = currentModel{
             //拷贝文字到截切板
-            UIPasteboard.general.string = currentModel?.request.prompt
+            UIPasteboard.general.string = model.request.prompt
             kSavePhotoSuccesswShared.show(atView: self.view,text: "Copy Successfully".localized,showViewBtn:false)
         }else{
             kShowToastDataMissing()

+ 71 - 0
AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateViewModel.swift

@@ -15,6 +15,77 @@ enum TSProgressState  {
     case success(Any?)
     case failed(String)
 
+    static func fromKeyValue(key string: String,value:String) -> TSProgressState {
+        if string == "none" {
+            return .none
+        } else if string == "start" {
+            return .start
+        } else if string == "pending" {
+            return .pending
+        }else if string == "progress" {
+            var progress = 0.0
+            if let doubleValue = Float(value) {
+                progress = CGFloat(doubleValue)
+            }
+            return .progress(progress)
+        }else if string == "success" {
+            return .success(value)
+        }else if string == "failed" {
+            return .failed(value)
+        }else{
+            return .none
+        }
+    }
+    
+    var keyValue:(String,String){
+        switch self {
+        case .none:
+            return ("none","")
+        case .start:
+            return ("start","")
+        case .pending:
+            return ("pending","")
+        case .progress(let float):
+            return ("progress",String(Float(float)))
+        case .success(let string):
+            if let string = string as? String{
+                return ("success",string)
+            }
+            return ("success","")
+        case .failed(let string):
+            return ("failed",string)
+        }
+    }
+    
+    
+    var isResult:Bool{
+        switch self {
+        case .none:
+            return false
+        case .start:
+            return false
+        case .pending:
+            return false
+        case .progress(let float):
+            return false
+        case .success(let string):
+            return true
+        case .failed(let string):
+            return true
+        }
+    }
+    
+    
+    var reslutSuccess:Bool{
+        switch self {
+        case .success(_):
+            return true
+        case .failed(_):
+            return false
+        default:
+            return true
+        }
+    }
 }
 
 class TSGenmojiGennerateViewModel {

+ 7 - 9
AIEmoji/Common/TSRealmManager/TSRealmManager.swift

@@ -17,15 +17,13 @@ class TSRealmManager {
     var realm: Realm
 
     private init() {
-//        do {
-//            realm = try Realm()
-//        } catch {
-//            fatalError("Failed to initialize Realm: \(error)")
-//        }
-//        
-        
-        // 设置新的版本号
-        let newSchemaVersion: UInt64 = 1
+        /*设置新的版本号
+         1.0 ->1
+         1.9 ->2
+         
+         **/
+   
+        let newSchemaVersion: UInt64 = 2
         // 获取默认配置
         var config = Realm.Configuration.defaultConfiguration
         // 设置新版本号