Browse Source

aichat 的 VIP 功能开发完毕

100Years 2 months ago
parent
commit
7bf9b6debb
20 changed files with 329 additions and 311 deletions
  1. 22 0
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/Contents.json
  2. BIN
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@2x.png
  3. BIN
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@3x.png
  4. 22 0
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/Contents.json
  5. BIN
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@2x.png
  6. BIN
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@3x.png
  7. 1 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift
  8. 1 1
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController+Ex.swift
  9. 175 31
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController.swift
  10. 0 4
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift
  11. 0 140
      AIEmoji/Business/AIChat/TSChatViewController/Views/CustomMessageContentCell.swift
  12. 0 88
      AIEmoji/Business/AIChat/TSChatViewController/Views/CustomTextMessageContentCell.swift
  13. 63 27
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift
  14. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateVC.swift
  15. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift
  16. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift
  17. 1 0
      AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager+Loading.swift
  18. 37 16
      AIEmoji/Common/Purchase/TSPurchaseManager.swift
  19. 1 1
      AIEmoji/Common/TSRealmManager/TSRealmManager.swift
  20. 3 0
      AIEmoji/DataManger/Config/TSConfig.swift

+ 22 - 0
AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@2x.png


BIN
AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@2x.png


BIN
AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@3x.png


+ 1 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift

@@ -26,6 +26,7 @@ class TSChatInputBarVC: TSBaseVC, UITextViewDelegate {
             }
             
             textView.text = ""
+            textDidChange()
         }
         return sendBtn
     }()

+ 1 - 1
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController+Ex.swift

@@ -57,7 +57,7 @@ extension TSChatViewController: MessagesDisplayDelegate {
     func configureAvatarView(_ avatarView: AvatarView, for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) {
         //    let avatar = SampleData.shared.getAvatarFor(sender: message.sender)
         //设置聊天头像
-        avatarView.set(avatar: Avatar(image: UIImage(named: "App-Icon")))
+//        avatarView.set(avatar: Avatar(image: UIImage(named: "App-Icon")))
     }
     
     func configureMediaMessageImageView(

+ 175 - 31
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController.swift

@@ -45,31 +45,105 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
     // MARK: Private
     lazy var textMessageSizeCalculator = TSTextLayoutSizeCalculator(layout: self.messagesCollectionView.messagesCollectionViewFlowLayout)
     
-    
     lazy var inputBarVC: TSChatInputBarVC = {
         let inputBarVC = TSChatInputBarVC()
         inputBarVC.sendComplete = { [weak self] components in
             guard let self = self else { return }
-            sendMessages(components)
-            messagesCollectionView.scrollToLastItem(animated: true)
+            inputSendMsg(components)
         }
         return inputBarVC
     }()
     
     
-    
-    lazy var text: UILabel = {
-        let textLabel = UILabel.createLabel(text: "Remaining 3 free times")
+    let inputBarBgView:UIView = {
+        let inputBarBgView = UIView()
+        inputBarBgView.addShadow(shadowColor: "#111111".uiColor.cgColor, shadowOffset: CGSize(width: 0, height: -10), shadowRadius: 10, shadowOpacity: 1.0)
+        return inputBarBgView
+    }()
+    let inputBarTopView:UIView = UIView()
+    
+    //免费次数
+    lazy var freeText: UILabel = {
+        let textLabel = UILabel.createLabel(
+            text: "Remaining \(kPurchaseDefault.freeNum(type: .aichat)) free times",
+            font: .font(size: 12),
+            textColor: "#E83E3E".uiColor,
+            textAlignment: .center,
+            numberOfLines: 0
+        )
+        textLabel.isHidden = false
         return textLabel
     }()
     
     
+    lazy var upgradeVipBg: UIView = {
+        let upgradeVipBg = UIView()
+        
+        let imageView = UIImageView.createImageView(imageName: "vip_upgrade_bg",contentMode: .scaleToFill)
+        upgradeVipBg.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+        
+        let label = UILabel.createLabel(
+            text:"Free usage limit reached. Upgrade for unlimited chats.".localized,
+            font: .font(size: 14,weight: .bold),
+            textColor: "#111111".uiColor,
+            numberOfLines: 0
+        )
+        upgradeVipBg.addSubview(label)
+        label.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.top.equalTo(8)
+            make.bottom.equalTo(-8)
+            make.trailing.equalTo(-95)
+        }
+        
+        let upgradeBtn = UIButton.createButton(
+            title: "Upgrade".localized,
+            backgroundColor: "#111111".uiColor,
+            font:.font(size: 12,weight: .medium),
+            titleColor:.white,
+            corner: 14) { [weak self]  in
+                guard let self = self else { return }
+                TSPurchaseVC.show(target: self) { [weak self]  in
+                    guard let self = self else { return }
+                    updateVipView()
+                }
+            }
+        upgradeVipBg.addSubview(upgradeBtn)
+        upgradeBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-12)
+            make.centerY.equalToSuperview()
+            make.width.equalTo(70)
+            make.height.equalTo(28)
+        }
+        return upgradeVipBg
+    }()
+    
+    
+    
+    lazy var scrollToBottomButton: UIButton = {
+        let backBottomBtn = UIButton.createButton(image: UIImage(named: "down_arrow_line")) { [weak self]  in
+            guard let self = self else { return }
+            messagesCollectionView.scrollToLastItem(animated: false)
+        }
+        backBottomBtn.isHidden = true
+        backBottomBtn.backgroundColor = .popupColor
+        backBottomBtn.cornerRadius = 16.0
+        return backBottomBtn
+    }()
+    
+    
+    
+    
     override func viewDidLoad() {
         super.viewDidLoad()
         navigationItem.title = "MessageKit"
 
         configureMessageCollectionView()
         configureMessageInputBar()
+        configureOtherUI()
         loadFirstMessages()
         
         
@@ -127,37 +201,69 @@ class TSChatViewController: MessagesViewController, MessagesDataSource {
     }
     
     func configureMessageInputBar() {
-        let clearView = UIView()
-        clearView.backgroundColor = .clear
-
+        
+        inputBarBgView.addSubview(inputBarTopView)
+        inputBarTopView.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.trailing.equalTo(0)
+            make.top.equalTo(0)
+        }
+        
         if viewModel.uiStyle == .chat {
-            clearView.addSubview(inputBarVC.view)
+            inputBarBgView.addSubview(inputBarVC.view)
             inputBarVC.view.snp.makeConstraints { make in
                 make.leading.equalTo(0)
                 make.trailing.equalTo(0)
-                make.top.equalTo(0)
+                make.top.equalTo(inputBarTopView.snp.bottom)
                 make.bottom.equalTo(0)
             }
         }
-        inputBarType = .custom(clearView)
-        
-        
-        let redView = UIView()
-        redView.backgroundColor = .red
-        view.addSubview(redView)
-        redView.snp.makeConstraints { make in
-            make.leading.equalTo(20)
-            make.trailing.equalTo(-20)
-            make.height.equalTo(30)
-//            make.bottom.equalTo(-10)
-            make.bottom.equalTo(inputContainerView.snp.top).offset(-10)
-            
+        inputBarType = .custom(inputBarBgView)
+    }
+    
+    
+    func configureOtherUI() {
+        view.addSubview(scrollToBottomButton)
+        scrollToBottomButton.snp.makeConstraints { make in
+            make.centerX.equalToSuperview()
+            make.bottom.equalTo(inputContainerView.snp.top).offset(-14)
+            make.width.height.equalTo(32)
         }
+        self.scrollViewDidScroll(self.messagesCollectionView)
         
+        updateVipView()
+    }
+    
+    func updateVipView() {
+        inputBarTopView.subviews.forEach { $0.removeFromSuperview()}
         
-        
+        if viewModel.uiStyle == .chat ,
+           kPurchaseDefault.isVip == false
+        {
+            let freeNum = kPurchaseDefault.freeNum(type: .aichat)
+            if freeNum > 0 {
+                freeText.text = "Remaining \(freeNum) free times"
+                inputBarTopView.addSubview(freeText)
+                freeText.snp.makeConstraints { make in
+                    make.leading.equalTo(20)
+                    make.trailing.equalTo(-20)
+                    make.bottom.equalTo(-8)
+                    make.top.equalTo(8)
+                }
+            }else{
+                inputBarTopView.addSubview(upgradeVipBg)
+                upgradeVipBg.snp.makeConstraints { make in
+                    make.leading.equalTo(16)
+                    make.trailing.equalTo(-16)
+                    make.bottom.equalTo(-2)
+                    make.top.equalTo(14)
+                }
+            }
+        }
     }
     
+    
+    
     // MARK: - Helpers
     var lastIndexPath:IndexPath{
         if messageList.count == 0 {
@@ -310,9 +416,6 @@ extension TSChatViewController: InputBarAccessoryViewDelegate {
                 //保存这条消息到本地数据库
                 //发送消息后,进行AI 对话生成
                 generativeAIChat(message: message)
-            } else if let img = component as? UIImage {
-                //              let message = TSChatMessage(image: img, user: user, messageId: UUID().uuidString, date: Date())
-                //              insertMessage(message)
             }
         }
     }
@@ -347,6 +450,9 @@ extension TSChatViewController: InputBarAccessoryViewDelegate {
             if let netData = data {
                 message.sendState = .success("netData")
                 //保存这条消息到本地数据库
+                //消耗一次 AI 次数
+                kPurchaseDefault.useOnceForFree(type: .aichat)
+                
             }else {
                 message.kind = .attributedText(kMDAttributedString(text: kAIErrorString))
                 message.sendState = .failed(kAIErrorString)
@@ -369,12 +475,50 @@ extension TSChatViewController: InputBarAccessoryViewDelegate {
             }else{
                 self.messagesCollectionView.reloadData()
             }
+
+            //更新 Vip
+            if kPurchaseDefault.isVip == false{
+                self.updateVipView()
+            }
+            
+            self.messagesCollectionView.scrollToLastItem(animated: false)
             
-//            if self.isLastSectionVisible() == true {
-                self.messagesCollectionView.scrollToLastItem(animated: false)
-//            }
         }
     }
     
 }
 
+extension TSChatViewController{
+    
+    func inputSendMsg(_ data: [Any]) {
+        
+        //判断 vip
+        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: .aichat) == false, vc: self, closePageBlock: {[weak self] in
+            guard let self = self else { return }
+        }){ return }
+        
+        sendMessages(data)
+        messagesCollectionView.scrollToLastItem(animated: true)
+    }
+    
+    
+}
+
+extension TSChatViewController{
+    
+    // UICollectionViewDelegate 方法
+    override func scrollViewDidScroll(_ scrollView: UIScrollView) {
+        let offsetY = scrollView.contentOffset.y
+        let contentHeight = scrollView.contentSize.height
+        let frameHeight = scrollView.frame.size.height
+        
+        // 判断是否需要显示滚动到底部的按钮
+        if offsetY > contentHeight - frameHeight - 400 {
+            scrollToBottomButton.isHidden = true
+        } else {
+            scrollToBottomButton.isHidden = false
+        }
+    }
+    
+    
+}

+ 0 - 4
AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift

@@ -114,7 +114,3 @@ private var md: SwiftyMarkdown = {
 func kMDAttributedString(text:String) -> NSAttributedString{
     return md.attributedString(from: text)
 }
-
-
-
-

+ 0 - 140
AIEmoji/Business/AIChat/TSChatViewController/Views/CustomMessageContentCell.swift

@@ -1,140 +0,0 @@
-//
-//  CustomMessageContentCell.swift
-//  ChatExample
-//
-//  Created by Vignesh J on 01/05/21.
-//  Copyright © 2021 MessageKit. All rights reserved.
-//
-
-import MessageKit
-import UIKit
-
-class CustomMessageContentCell: MessageCollectionViewCell {
-    // MARK: Lifecycle
-    
-    override init(frame: CGRect) {
-        super.init(frame: frame)
-        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-        setupSubviews()
-    }
-    
-    required init?(coder aDecoder: NSCoder) {
-        super.init(coder: aDecoder)
-        contentView.autoresizingMask = [.flexibleWidth, .flexibleHeight]
-        setupSubviews()
-    }
-    
-    // MARK: Internal
-    
-    /// The `MessageCellDelegate` for the cell.
-    weak var delegate: MessageCellDelegate?
-    
-    /// The container used for styling and holding the message's content view.
-    var messageContainerView: UIView = {
-        let containerView = UIView()
-        containerView.cornerRadius = 16
-        return containerView
-    }()
-    
-    /// 用来显示消息框上的时间之类的
-    var cellTopLabel: UILabel = {
-        let label = UILabel()
-        label.numberOfLines = 0
-        label.textAlignment = .center
-        return label
-    }()
-    
-    var leadingAvatarImageView: UIImageView = {
-        let imageView = UIImageView.createImageView(imageName: "aichat_avatar")
-        return imageView
-    }()
-    
-    //  var cellDateLabel: UILabel = {
-    //    let label = UILabel()
-    //    label.numberOfLines = 0
-    //    label.textAlignment = .right
-    //    return label
-    //  }()
-    
-    override func prepareForReuse() {
-        super.prepareForReuse()
-        cellTopLabel.text = nil
-        cellTopLabel.attributedText = nil
-        //    cellDateLabel.text = nil
-        //    cellDateLabel.attributedText = nil
-    }
-    
-    /// Handle tap gesture on contentView and its subviews.
-    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)
-        case cellTopLabel.frame.contains(touchLocation):
-            delegate?.didTapCellTopLabel(in: self)
-            //    case cellDateLabel.frame.contains(touchLocation):
-            //      delegate?.didTapMessageBottomLabel(in: self)
-        default:
-            delegate?.didTapBackground(in: self)
-        }
-    }
-    
-    /// Handle long press gesture, return true when gestureRecognizer's touch point in `messageContainerView`'s frame
-    override func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
-        let touchPoint = gestureRecognizer.location(in: self)
-        guard gestureRecognizer.isKind(of: UILongPressGestureRecognizer.self) else { return false }
-        return messageContainerView.frame.contains(touchPoint)
-    }
-    
-    func setupSubviews() {
-        contentView.addSubview(cellTopLabel)
-        contentView.addSubview(leadingAvatarImageView)
-        contentView.addSubview(messageContainerView)
-
-    }
-    
-    func configure(
-        with message: MessageType,
-        at indexPath: IndexPath,
-        in messagesCollectionView: MessagesCollectionView,
-        dataSource: MessagesDataSource,
-        and sizeCalculator: CustomLayoutSizeCalculator)
-    {
-        guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
-            return
-        }
-        cellTopLabel.frame = sizeCalculator.cellTopLabelFrame(
-            for: message,
-            at: indexPath)
-        
-        cellTopLabel.attributedText = dataSource.cellTopLabelAttributedText(
-            for: message,
-            at: indexPath)
-        
-        
-        //    cellDateLabel.frame = sizeCalculator.cellMessageBottomLabelFrame(
-        //      for: message,
-        //      at: indexPath)
-        //      cellDateLabel.attributedText = dataSource.messageBottomLabelAttributedText(
-        //        for: message,
-        //        at: indexPath)
-        
-        messageContainerView.frame = sizeCalculator.messageContainerFrame(
-            for: message,
-            at: indexPath,
-            fromCurrentSender: dataSource
-                .isFromCurrentSender(message: message))
-        
-        messageContainerView.backgroundColor = displayDelegate.backgroundColor(
-            for: message,
-            at: indexPath,
-            in: messagesCollectionView)
-    }
-    
-    /// Handle `ContentView`'s tap gesture, return false when `ContentView` doesn't needs to handle gesture
-    func cellContentView(canHandle _: CGPoint) -> Bool {
-        false
-    }
-}

+ 0 - 88
AIEmoji/Business/AIChat/TSChatViewController/Views/CustomTextMessageContentCell.swift

@@ -1,88 +0,0 @@
-//
-//  CustomTextMessageContentCell.swift
-//  ChatExample
-//
-//  Created by Vignesh J on 01/05/21.
-//  Copyright © 2021 MessageKit. All rights reserved.
-//
-
-import MessageKit
-import UIKit
-import SwiftyMarkdown
-class CustomTextMessageContentCell: CustomMessageContentCell {
-    var messageLabel: UILabel = {
-        let label = UILabel.createLabel(font: .font(size: 16),numberOfLines: 0)
-        return label
-    }()
-    
-    override func prepareForReuse() {
-        super.prepareForReuse()
-        //        messageLabel.attributedText = nil
-        //        messageLabel.text = nil
-    }
-    
-    override func setupSubviews() {
-        super.setupSubviews()
-        messageContainerView.addSubview(messageLabel)
-    }
-    
-    override func configure(
-        with message: MessageType,
-        at indexPath: IndexPath,
-        in messagesCollectionView: MessagesCollectionView,
-        dataSource: MessagesDataSource,
-        and sizeCalculator: CustomLayoutSizeCalculator)
-    {
-        super.configure(
-            with: message,
-            at: indexPath,
-            in: messagesCollectionView,
-            dataSource: dataSource,
-            and: sizeCalculator)
-        
-        guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
-            return
-        }
-        
-        let calculator = sizeCalculator as? CustomTextLayoutSizeCalculator
-        messageLabel.frame = calculator?.messageLabelFrame(for: message,at: indexPath) ?? .zero
-        let textMessageKind = message.kind
-        switch textMessageKind {
-        case .text(let text), .emoji(let text):
-            //            let textColor = displayDelegate.textColor(for: message, at: indexPath, in: messagesCollectionView)
-            //            messageLabel.textColor = textColor
-            
-            messageLabel.text = text
-            messageLabel.attributedText = SwiftyMarkdown(string: text).attributedString()
-            //        extractAndPrintSubstring(from: messageLabel.text ?? "", to: text)
-            
-            
-        case .attributedText(let text):
-            messageLabel.attributedText = text
-        default:
-            break
-        }
-    }
-    
-    
-    func extractAndPrintSubstring(from sourceString: String, to targetString: String) {
-        // 查找 sourceString 在 targetString 中的结束位置
-        if let range = targetString.range(of: sourceString) {
-            // 获取 sourceString 在 targetString 中的结束位置
-            let startIdx = range.upperBound
-            // 提取从该位置到 targetString 结束的子字符串
-            let extractedSubstring = targetString[startIdx...]
-            
-            // 将提取的子字符串逐个字符输出
-            for character in extractedSubstring {
-                print(character)
-                messageLabel.text = (messageLabel.text ?? "") + String(character)
-                messageLabel.attributedText = SwiftyMarkdown(string: messageLabel.text ?? "").attributedString()
-            }
-        } else {
-            print("未找到 sourceString")
-            messageLabel.text = targetString
-            messageLabel.attributedText = SwiftyMarkdown(string: targetString).attributedString()
-        }
-    }
-}

+ 63 - 27
AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift

@@ -16,6 +16,13 @@ class TSTextMessageContentCell: TSMessageContentCell {
         return label
     }()
     
+    lazy var activityIndicator: UIActivityIndicatorView = {
+        activityIndicator = UIActivityIndicatorView(style: .medium)
+        activityIndicator.color = .white
+        activityIndicator.isHidden = true
+        return activityIndicator
+    }()
+    
     override func prepareForReuse() {
         super.prepareForReuse()
     }
@@ -30,6 +37,12 @@ class TSTextMessageContentCell: TSMessageContentCell {
             make.width.height.equalTo(0)
         }
         
+        messageContainerView.addSubview(activityIndicator)
+        activityIndicator.snp.makeConstraints { make in
+            make.top.equalTo(12)
+            make.left.equalTo(12)
+            make.width.height.equalTo(24.0)
+        }
         
     }
     
@@ -40,17 +53,12 @@ class TSTextMessageContentCell: TSMessageContentCell {
         dataSource: MessagesDataSource,
         and sizeCalculator: TSLayoutSizeCalculator)
     {
-        super.configure(
-            with: message,
-            at: indexPath,
-            in: messagesCollectionView,
-            dataSource: dataSource,
-            and: sizeCalculator)
-        
+        super.configure(with: message, at: indexPath, in: messagesCollectionView, dataSource: dataSource, and: sizeCalculator)
         guard let displayDelegate = messagesCollectionView.messagesDisplayDelegate else {
             return
         }
         
+        //更新frame
         let calculator = sizeCalculator as? TSTextLayoutSizeCalculator
         let labelFrame = calculator?.messageLabelSize(for: message, at: indexPath,fromCurrentSender: dataSource.isFromCurrentSender(message: message)) ?? TSTextLayoutSizeCalculator.cellMessagelabelMinSize
         messageLabel.snp.updateConstraints { make in
@@ -58,13 +66,31 @@ class TSTextMessageContentCell: TSMessageContentCell {
             make.height.equalTo(labelFrame.height)
         }
         
+
+        if let msgModel = message as? TSChatMessage {
+            //显示旋转的动画
+            switch msgModel.sendState {
+            case .start:
+                startAnimating()
+            case .progress(_):
+                stopAnimating()
+            case .success(_):
+                stopAnimating()
+            case .failed(_):
+                stopAnimating()
+            default:
+                stopAnimating()
+            }
+        }
+    
+        //给 label 赋值
         let textMessageKind = message.kind
         switch textMessageKind {
         case .text(let text), .emoji(let text):
             let textColor = displayDelegate.textColor(for: message, at: indexPath, in: messagesCollectionView)
             messageLabel.textColor = textColor
             messageLabel.text = text
-            debugPrint("text赋值")
+//            debugPrint("text赋值")
         case .attributedText(let text):
             messageLabel.attributedText = text
 //            debugPrint("attributedText赋值")
@@ -74,24 +100,34 @@ class TSTextMessageContentCell: TSMessageContentCell {
     }
     
     
-    func extractAndPrintSubstring(from sourceString: String, to targetString: String) {
-        // 查找 sourceString 在 targetString 中的结束位置
-        if let range = targetString.range(of: sourceString) {
-            // 获取 sourceString 在 targetString 中的结束位置
-            let startIdx = range.upperBound
-            // 提取从该位置到 targetString 结束的子字符串
-            let extractedSubstring = targetString[startIdx...]
-            
-            // 将提取的子字符串逐个字符输出
-            for character in extractedSubstring {
-                print(character)
-                messageLabel.text = (messageLabel.text ?? "") + String(character)
-                messageLabel.attributedText = SwiftyMarkdown(string: messageLabel.text ?? "").attributedString()
-            }
-        } else {
-            print("未找到 sourceString")
-            messageLabel.text = targetString
-            messageLabel.attributedText = SwiftyMarkdown(string: targetString).attributedString()
-        }
+//    func extractAndPrintSubstring(from sourceString: String, to targetString: String) {
+//        // 查找 sourceString 在 targetString 中的结束位置
+//        if let range = targetString.range(of: sourceString) {
+//            // 获取 sourceString 在 targetString 中的结束位置
+//            let startIdx = range.upperBound
+//            // 提取从该位置到 targetString 结束的子字符串
+//            let extractedSubstring = targetString[startIdx...]
+//            
+//            // 将提取的子字符串逐个字符输出
+//            for character in extractedSubstring {
+//                print(character)
+//                messageLabel.text = (messageLabel.text ?? "") + String(character)
+//                messageLabel.attributedText = SwiftyMarkdown(string: messageLabel.text ?? "").attributedString()
+//            }
+//        } else {
+//            print("未找到 sourceString")
+//            messageLabel.text = targetString
+//            messageLabel.attributedText = SwiftyMarkdown(string: targetString).attributedString()
+//        }
+//    }
+    
+    func startAnimating() {
+        activityIndicator.isHidden = false
+        activityIndicator.startAnimating()
+    }
+    
+    func stopAnimating() {
+        activityIndicator.isHidden = true
+        activityIndicator.stopAnimating()
     }
 }

+ 1 - 1
AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateVC.swift

@@ -170,6 +170,6 @@ extension TSGenmojiGennerateVC {
             confirmBtn.isEnabled = true
         }
         
-        kPurchaseDefault.useOnceForFree()
+        kPurchaseDefault.useOnceForFree(type: .generatePic)
     }
 }

+ 1 - 1
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift

@@ -135,7 +135,7 @@ extension TSGenmojiVC {
     func generateImage(text:String) {
         
         //判断 vip
-        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable() == false, vc: self) {[weak self] in
+        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: .generatePic) == false, vc: self) {[weak self] in
             guard let self = self else { return }
         }{ return }
         

+ 1 - 1
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift

@@ -68,7 +68,7 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
         if kPurchaseDefault.isVip {
             return "Generate"
         }
-        return "Generate (\(kPurchaseDefault.freeNum))"
+        return "Generate (\(kPurchaseDefault.freeNum(type: .generatePic)))"
     }
     
     override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {

+ 1 - 0
AIEmoji/Common/NetworkManager/TSNetWork/TSNetworkManager+Loading.swift

@@ -80,6 +80,7 @@ extension TSNetworkManager {
                 completion(nil,error)
             }
         }
+    
     }
     
 }

+ 37 - 16
AIEmoji/Common/Purchase/TSPurchaseManager.swift

@@ -15,6 +15,11 @@ public enum PremiumPeriod: String, CaseIterable {
     case lifetime       = "Lifetime"
 }
 
+public enum VipFreeNumType: String, CaseIterable {
+    case generatePic    = "kGeneratePicFreeNum"
+    case aichat         = "kAIChatFreeNum"
+}
+
 public struct PurchaseProduct {
     public let productId: String
     public let period: PremiumPeriod
@@ -84,10 +89,9 @@ public class PurchaseManager: NSObject {
     // 会员信息
     var vipInformation: [String: Any] = [:]
 
-    
-    // 免费使用会员转 livew的次数
-    var freeNum:Int = 0
-    
+    // 免费使用会员的次数
+    var freeDict:[String:Int] = [:]
+        
     //原始订单交易id dict
     var originalTransactionIdentifierDict:[String:String] = [:]
     
@@ -533,47 +537,64 @@ public extension PurchaseManager {
 }
 
 
-/// 免费次数
+/// 免费生成图片次数
 extension PurchaseManager {
     /// 使用一次免费次数
-    func useOnceForFree(){
+    func useOnceForFree(type:VipFreeNumType){
+        
+        if isVip {
+            return
+        }
+        
+        var freeNum = freeDict[type.rawValue] ?? 0
         if freeNum > 0 {
             freeNum-=1
-        }else{
-            freeNum=0
         }
+        
+        if freeNum < 0 {
+            freeNum = 0
+        }
+        
+        freeDict[type.rawValue] = freeNum
         saveForFree()
     }
     
+    func freeNum(type:VipFreeNumType) -> Int{
+        let freeNum = freeDict[type.rawValue] ?? 0
+        return freeNum
+    }
     
     func saveForFree(){
-        UserDefaults.standard.set(String(freeNum), forKey: kFreeNumKey)
+        UserDefaults.standard.set(freeDict, forKey: kFreeNumKey)
         UserDefaults.standard.synchronize()
     }
     
     func initializeForFree(){
-        if let num = UserDefaults.standard.string(forKey: kFreeNumKey) {
-            freeNum = Int(num) ?? 3
+        if let dict = UserDefaults.standard.dictionary(forKey: kFreeNumKey) as? [String:Int]{
+            freeDict = dict
         }else{
-            freeNum = 3
+            freeDict = [
+                VipFreeNumType.generatePic.rawValue:3,
+                VipFreeNumType.aichat.rawValue:3
+            ]
             saveForFree()
         }
     }
     
     /// 免费次数是否可用
-    func freeNumAvailable() -> Bool{
+    func freeNumAvailable(type:VipFreeNumType) -> Bool{
         if isVip == true {
             return true
         }else{
-            if freeNum <= 0 {
-                return false
-            }else{
+            if let freeNum = freeDict[type.rawValue],freeNum > 0 {
                 return true
             }
         }
+        return false
     }
 }
 
+
 /*
  
  首先,创建SKProductsRequest对象并使用init(productIdentifiers:)初始化,传入要查询的产品标识符。

+ 1 - 1
AIEmoji/Common/TSRealmManager/TSRealmManager.swift

@@ -25,7 +25,7 @@ class TSRealmManager {
 //        
         
         // 设置新的版本号
-        let newSchemaVersion: UInt64 = 3
+        let newSchemaVersion: UInt64 = 1
         // 获取默认配置
         var config = Realm.Configuration.defaultConfiguration
         // 设置新版本号

+ 3 - 0
AIEmoji/DataManger/Config/TSConfig.swift

@@ -66,6 +66,9 @@ extension UIColor {
     /// 主色调
     static let themeColor = "#FECB34".uiColor
     
+    /// 弹窗色
+    static let popupColor = "#222222".uiColor
+    
     static let naviMianBgColor = UIColor.clear
     static let naviMianTextColor = UIColor.white.withAlphaComponent(0.8)