|
@@ -1,609 +0,0 @@
|
|
|
-//
|
|
|
-// MIT License
|
|
|
-//
|
|
|
-// Copyright (c) 2017-2020 MessageKit
|
|
|
-//
|
|
|
-// Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
|
-// of this software and associated documentation files (the "Software"), to deal
|
|
|
-// in the Software without restriction, including without limitation the rights
|
|
|
-// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
|
-// copies of the Software, and to permit persons to whom the Software is
|
|
|
-// furnished to do so, subject to the following conditions:
|
|
|
-//
|
|
|
-// The above copyright notice and this permission notice shall be included in all
|
|
|
-// copies or substantial portions of the Software.
|
|
|
-//
|
|
|
-// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
|
-// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
|
-// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
|
-// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
|
-// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
|
-// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
|
-// SOFTWARE.
|
|
|
-
|
|
|
-import InputBarAccessoryView
|
|
|
-import MessageKit
|
|
|
-import UIKit
|
|
|
-
|
|
|
-class TSChatViewController: MessagesViewController, MessagesDataSource {
|
|
|
-
|
|
|
- var viewModel:TSAIChatVM = TSAIChatVM()
|
|
|
-
|
|
|
-
|
|
|
- public lazy var navBarContentView: UIView = creatNavBarContentView
|
|
|
-
|
|
|
- public lazy var normalNavBarView: TSNormalNavigationBarView = creatNormalNavBarView
|
|
|
-
|
|
|
-
|
|
|
- // MARK: - Public properties
|
|
|
- lazy var messageList: [TSChatMessage] = []
|
|
|
-
|
|
|
- private(set) lazy var refreshControl: UIRefreshControl = {
|
|
|
- let control = UIRefreshControl()
|
|
|
- control.addTarget(self, action: #selector(loadMoreMessages), for: .valueChanged)
|
|
|
- return control
|
|
|
- }()
|
|
|
-
|
|
|
- // 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 }
|
|
|
- inputSendMsg(components)
|
|
|
- }
|
|
|
- return inputBarVC
|
|
|
- }()
|
|
|
-
|
|
|
-
|
|
|
- lazy var vipBtn: UIButton = creatVipBtn
|
|
|
- lazy var navBarView: TSBaseNavContentBarView = creatNavBarView
|
|
|
-
|
|
|
- var deleteBlock:(()->Void)?
|
|
|
-
|
|
|
- lazy var inputBarBgView:UIView = creatInputBarBgView
|
|
|
- let inputBarTopView:UIView = UIView()
|
|
|
-
|
|
|
- //免费次数label
|
|
|
- lazy var freeText: UILabel = creatFreeText
|
|
|
- lazy var upgradeVipBg: UIView = creatUpgradeVipBg
|
|
|
- lazy var scrollToBottomButton: UIButton = creatScrollToBottomButton
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- override func viewDidLoad() {
|
|
|
- super.viewDidLoad()
|
|
|
- navigationItem.title = "MessageKit"
|
|
|
- edgesForExtendedLayout = [.top]
|
|
|
-
|
|
|
- // 创建轻击手势识别器
|
|
|
- let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickView))
|
|
|
- // 设置轻击手势识别器取消默认的视图触摸响应,避免影响子视图交互
|
|
|
- tapGesture.cancelsTouchesInView = false
|
|
|
- // 将轻击手势识别器添加到视图上
|
|
|
- messagesCollectionView.addGestureRecognizer(tapGesture)
|
|
|
- configureNaviBarView()
|
|
|
- configureMessageCollectionView()
|
|
|
- configureMessageInputBar()
|
|
|
- configureOtherUI()
|
|
|
- loadFirstMessages()
|
|
|
-
|
|
|
- vipBtn.isHidden = PurchaseManager.default.isVip
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(vipInfoChanged), name: .kPurchaseDidChanged, object: nil)
|
|
|
- if viewModel.uiStyle == .chat {
|
|
|
- // 注册通知监听,App死的时候,保存本次聊天记录到本地
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(saveChatList), name: .kApplicationWillTerminate, object: nil)
|
|
|
- // 监听键盘事件
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
|
|
- NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @objc func vipInfoChanged() {
|
|
|
- kExecuteOnMainThread {
|
|
|
- self.vipBtn.isHidden = PurchaseManager.default.isVip
|
|
|
- self.updateVipView()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @objc func clickView(){
|
|
|
- view.endEditing(true)
|
|
|
- }
|
|
|
-
|
|
|
- @objc func keyboardWillShow(_ notification: Notification) {
|
|
|
-
|
|
|
- guard let keyboardFrame = notification.userInfo?[UIResponder.keyboardFrameEndUserInfoKey] as? CGRect,
|
|
|
- let animationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- let keyboardHeight = keyboardFrame.height
|
|
|
- let contentInset = UIEdgeInsets(top: 0, left: 0, bottom: keyboardHeight, right: 0)
|
|
|
-
|
|
|
- UIView.animate(withDuration: animationDuration) {
|
|
|
- self.messagesCollectionView.contentInset = contentInset
|
|
|
- self.messagesCollectionView.scrollIndicatorInsets = contentInset
|
|
|
- }
|
|
|
-
|
|
|
- kDelayMainShort {
|
|
|
- self.messagesCollectionView.scrollToLastItem(animated: false)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- @objc func keyboardWillHide(_ notification: Notification) {
|
|
|
- guard let animationDuration = notification.userInfo?[UIResponder.keyboardAnimationDurationUserInfoKey] as? TimeInterval else {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- let contentInset = UIEdgeInsets.zero
|
|
|
-
|
|
|
- UIView.animate(withDuration: animationDuration) {
|
|
|
- self.messagesCollectionView.contentInset = contentInset
|
|
|
- self.messagesCollectionView.scrollIndicatorInsets = contentInset
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- @objc func saveChatList() {
|
|
|
- messageList.remove(at: 0)
|
|
|
- viewModel.updateMessages(msgModels: messageList)
|
|
|
- }
|
|
|
-
|
|
|
- override func viewDidAppear(_ animated: Bool) {
|
|
|
- super.viewDidAppear(animated)
|
|
|
- }
|
|
|
-
|
|
|
- override func viewDidDisappear(_ animated: Bool) {
|
|
|
- super.viewDidDisappear(animated)
|
|
|
- }
|
|
|
-
|
|
|
- func loadFirstMessages() {
|
|
|
- //获取消息数量
|
|
|
- self.messageList = viewModel.getHistoryChatMessage()
|
|
|
- self.messagesCollectionView.reloadData()
|
|
|
- self.messagesCollectionView.scrollToLastItem(animated: false)
|
|
|
- }
|
|
|
-
|
|
|
- @objc
|
|
|
- func loadMoreMessages() {
|
|
|
- //获取更多消息数量
|
|
|
-
|
|
|
- }
|
|
|
-
|
|
|
- public func addNormalNavBarView(){
|
|
|
- navBarContentView.addSubview(normalNavBarView)
|
|
|
- normalNavBarView.snp.makeConstraints { make in
|
|
|
- make.edges.equalToSuperview()
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public func setTitleText(_ title: String) {
|
|
|
- _ = normalNavBarView.setTitleName(NSLocalizedString(title, comment: ""))
|
|
|
- }
|
|
|
-
|
|
|
- public func setPageTitle(_ title: String) {
|
|
|
- let pageTitle = title
|
|
|
- let backTitle = " "
|
|
|
- setTitleText(pageTitle)
|
|
|
- _ = setNavigationItem(backTitle, imageName: "navi_back_white", direction: .left, action: #selector(navBarClickLeftAction))
|
|
|
- }
|
|
|
- @objc public func navBarClickLeftAction() {
|
|
|
- debugPrint("navBarClickLeftAction -> \(type(of: self))")
|
|
|
- pop()
|
|
|
- }
|
|
|
-
|
|
|
- public func pop() {
|
|
|
- if navigationController == nil {
|
|
|
- dismiss(animated: true, completion: nil)
|
|
|
- } else if navigationController?.presentingViewController != nil, navigationController?.viewControllers.count == 1 {
|
|
|
- navigationController?.dismiss(animated: true, completion: nil)
|
|
|
- } else {
|
|
|
- navigationController?.popViewController(animated: true)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- public func setNavigationItem(_ name: String, imageName: String, direction: NSTextAlignment, action: Selector) -> UIButton {
|
|
|
- if direction == .left {
|
|
|
- return normalNavBarView.setLeftNavigationItem(name: name, imageName: imageName, target: self, action: action)
|
|
|
- } else {
|
|
|
- return normalNavBarView.setRightNavigationItem(name: name, imageName: imageName, target: self, action: action)
|
|
|
- }
|
|
|
- }
|
|
|
- @objc func clickDelete(){
|
|
|
- showCustomAlert(message: "Are you sure to delete".localized, deleteHandler: { [weak self] in
|
|
|
- guard let self = self else { return }
|
|
|
- viewModel.dbAIChatList.delete()
|
|
|
-
|
|
|
- deleteBlock?()
|
|
|
- pop()
|
|
|
- })
|
|
|
-
|
|
|
-
|
|
|
- }
|
|
|
- func configureNaviBarView() {
|
|
|
- view.addSubview(navBarContentView)
|
|
|
- navBarContentView.snp.makeConstraints { make in
|
|
|
- make.leading.top.trailing.equalToSuperview()
|
|
|
- make.height.equalTo(k_Nav_Height)
|
|
|
- }
|
|
|
-
|
|
|
- if viewModel.uiStyle == .history {
|
|
|
- addNormalNavBarView()
|
|
|
- setPageTitle("History".localized)
|
|
|
- _ = setNavigationItem("", imageName: "delete_white", direction: .right, action: #selector(clickDelete))
|
|
|
- }else{
|
|
|
- navBarContentView.addSubview(navBarView)
|
|
|
- navBarView.snp.makeConstraints { make in
|
|
|
- make.edges.equalToSuperview()
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-
|
|
|
- func configureMessageCollectionView() {
|
|
|
- clearAndResetConstraints()
|
|
|
- view.backgroundColor = .mainBg
|
|
|
- //设置自定义FlowLayout,itemsize等,都在这里控制
|
|
|
- let flowLayout = CustomMessagesFlowLayout()
|
|
|
- flowLayout.sectionInset = UIEdgeInsets(top: 4, left: 0, bottom: 4, right: 0)
|
|
|
-
|
|
|
- messagesCollectionView.collectionViewLayout = flowLayout
|
|
|
- messagesCollectionView.backgroundColor = .clear
|
|
|
- messagesCollectionView.register(TSTextMessageContentCell.self)
|
|
|
- messagesCollectionView.messagesLayoutDelegate = self
|
|
|
- messagesCollectionView.messagesDisplayDelegate = self
|
|
|
- messagesCollectionView.messagesDataSource = self
|
|
|
- messagesCollectionView.messageCellDelegate = self
|
|
|
- messagesCollectionView.clipsToBounds = true
|
|
|
- scrollsToLastItemOnKeyboardBeginsEditing = true // default false
|
|
|
- maintainPositionOnInputBarHeightChanged = true // default false
|
|
|
- showMessageTimestampOnSwipeLeft = false // default false
|
|
|
- // messagesCollectionView.refreshControl = refreshControl
|
|
|
- messagesCollectionView.reloadData()
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- func clearAndResetConstraints() {
|
|
|
- // 筛选出与 messagesCollectionView 相关的约束
|
|
|
- let constraintsToRemove = view.constraints.filter { constraint in
|
|
|
- return (constraint.firstItem as? UIView == messagesCollectionView) || (constraint.secondItem as? UIView == messagesCollectionView)
|
|
|
- }
|
|
|
- // 停用并移除这些约束
|
|
|
- NSLayoutConstraint.deactivate(constraintsToRemove)
|
|
|
- for constraint in constraintsToRemove {
|
|
|
- view.removeConstraint(constraint)
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
- messagesCollectionView.snp.remakeConstraints { make in
|
|
|
- make.leading.trailing.bottom.equalTo(0)
|
|
|
- make.top.equalTo(k_Nav_Height)
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func configureMessageInputBar() {
|
|
|
-
|
|
|
- inputBarBgView.addSubview(inputBarTopView)
|
|
|
- inputBarTopView.snp.makeConstraints { make in
|
|
|
- make.leading.equalTo(0)
|
|
|
- make.trailing.equalTo(0)
|
|
|
- make.top.equalTo(0)
|
|
|
- }
|
|
|
-
|
|
|
- if viewModel.uiStyle == .chat {
|
|
|
- inputBarBgView.addSubview(inputBarVC.view)
|
|
|
- inputBarVC.view.snp.makeConstraints { make in
|
|
|
- make.leading.equalTo(0)
|
|
|
- make.trailing.equalTo(0)
|
|
|
- make.top.equalTo(inputBarTopView.snp.bottom)
|
|
|
- make.bottom.equalTo(0)
|
|
|
- }
|
|
|
- }
|
|
|
- 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 {
|
|
|
- return IndexPath(item: 0, section: 0)
|
|
|
- }
|
|
|
- return IndexPath(item: messageList.count - 1, section: 0)
|
|
|
- }
|
|
|
-
|
|
|
- func insertMessage(_ message: TSChatMessage) {
|
|
|
- messageList.append(message)
|
|
|
- messagesCollectionView.performBatchUpdates({
|
|
|
- messagesCollectionView.insertItems(at: [lastIndexPath])
|
|
|
- if messageList.count >= 2 {
|
|
|
- messagesCollectionView.reloadItems(at: [lastIndexPath])
|
|
|
- }
|
|
|
- }, completion: { [weak self] _ in
|
|
|
- if self?.isLastSectionVisible() == true {
|
|
|
- self?.messagesCollectionView.scrollToLastItem(animated: true)
|
|
|
- }
|
|
|
- })
|
|
|
- }
|
|
|
-
|
|
|
- func isLastSectionVisible() -> Bool {
|
|
|
- guard !messageList.isEmpty else { return false }
|
|
|
- return messagesCollectionView.indexPathsForVisibleItems.contains(lastIndexPath)
|
|
|
- }
|
|
|
-
|
|
|
- private let formatter: DateFormatter = {
|
|
|
- let formatter = DateFormatter()
|
|
|
- formatter.dateStyle = .medium
|
|
|
- return formatter
|
|
|
- }()
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// MARK: MessagesDataSource
|
|
|
-extension TSChatViewController {
|
|
|
-
|
|
|
- var currentSender: SenderType {
|
|
|
- return viewModel.kUserSender
|
|
|
- }
|
|
|
-
|
|
|
- func numberOfSections(in _: MessagesCollectionView) -> Int {
|
|
|
- return 1
|
|
|
- }
|
|
|
-
|
|
|
- func numberOfItems(inSection section: Int, in messagesCollectionView: MessagesCollectionView) -> Int{
|
|
|
- messageList.count
|
|
|
- }
|
|
|
-
|
|
|
- func messageForItem(at indexPath: IndexPath, in _: MessagesCollectionView) -> MessageType {
|
|
|
- messageList[indexPath.item]
|
|
|
- }
|
|
|
-
|
|
|
- func cellTopLabelAttributedText(for message: MessageType, at indexPath: IndexPath) -> NSAttributedString? {
|
|
|
-// if indexPath.item % 3 == 0 {
|
|
|
-// return NSAttributedString(
|
|
|
-// string: MessageKitDateFormatter.shared.string(from: message.sentDate),
|
|
|
-// attributes: [
|
|
|
-// NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10),
|
|
|
-// NSAttributedString.Key.foregroundColor: UIColor.darkGray,
|
|
|
-// ])
|
|
|
-// }
|
|
|
- return nil
|
|
|
- }
|
|
|
-
|
|
|
- func cellBottomLabelAttributedText(for _: MessageType, at _: IndexPath) -> NSAttributedString? {
|
|
|
- NSAttributedString(
|
|
|
- string: "Read",
|
|
|
- attributes: [
|
|
|
- NSAttributedString.Key.font: UIFont.boldSystemFont(ofSize: 10),
|
|
|
- NSAttributedString.Key.foregroundColor: UIColor.darkGray,
|
|
|
- ])
|
|
|
- }
|
|
|
-
|
|
|
- func messageTopLabelAttributedText(for message: MessageType, at _: IndexPath) -> NSAttributedString? {
|
|
|
- let name = message.sender.displayName
|
|
|
- return NSAttributedString(
|
|
|
- string: name,
|
|
|
- attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption1)])
|
|
|
- }
|
|
|
-
|
|
|
- func messageBottomLabelAttributedText(for message: MessageType, at _: IndexPath) -> NSAttributedString? {
|
|
|
- let dateString = formatter.string(from: message.sentDate)
|
|
|
- return NSAttributedString(
|
|
|
- string: dateString,
|
|
|
- attributes: [NSAttributedString.Key.font: UIFont.preferredFont(forTextStyle: .caption2)])
|
|
|
- }
|
|
|
-
|
|
|
- func textCell(
|
|
|
- for message: MessageType,
|
|
|
- at indexPath: IndexPath,
|
|
|
- in messagesCollectionView: MessagesCollectionView)
|
|
|
- -> UICollectionViewCell?
|
|
|
- {
|
|
|
- let cell = messagesCollectionView.dequeueReusableCell(
|
|
|
- TSTextMessageContentCell.self,
|
|
|
- for: indexPath)
|
|
|
- cell.configure(
|
|
|
- with: message,
|
|
|
- at: indexPath,
|
|
|
- in: messagesCollectionView,
|
|
|
- dataSource: self,
|
|
|
- and: textMessageSizeCalculator)
|
|
|
-
|
|
|
- return cell
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-// MARK: InputBarAccessoryViewDelegate
|
|
|
-
|
|
|
-extension TSChatViewController: InputBarAccessoryViewDelegate {
|
|
|
- // MARK: Internal
|
|
|
-
|
|
|
- @objc
|
|
|
- func inputBar(_: InputBarAccessoryView, didPressSendButtonWith _: String) {
|
|
|
- processInputBar(messageInputBar)
|
|
|
- }
|
|
|
-
|
|
|
- //聊天发送内容
|
|
|
- func processInputBar(_ inputBar: InputBarAccessoryView) {
|
|
|
- // Here we can parse for which substrings were autocompleted
|
|
|
- let attributedText = inputBar.inputTextView.attributedText!
|
|
|
- let range = NSRange(location: 0, length: attributedText.length)
|
|
|
- attributedText.enumerateAttribute(.autocompleted, in: range, options: []) { _, range, _ in
|
|
|
-
|
|
|
- let substring = attributedText.attributedSubstring(from: range)
|
|
|
- let context = substring.attribute(.autocompletedContext, at: 0, effectiveRange: nil)
|
|
|
- print("Autocompleted: `", substring, "` with context: ", context ?? "-")
|
|
|
- }
|
|
|
-
|
|
|
- let components = inputBar.inputTextView.components
|
|
|
- inputBar.inputTextView.text = String()
|
|
|
- inputBar.invalidatePlugins()
|
|
|
-
|
|
|
- inputBar.inputTextView.placeholder = "Aa"
|
|
|
- sendMessages(components)
|
|
|
- messagesCollectionView.scrollToLastItem(animated: true)
|
|
|
- }
|
|
|
-
|
|
|
- // MARK: Private
|
|
|
-
|
|
|
- private func sendMessages(_ data: [Any]) {
|
|
|
- let user = viewModel.kUserSender
|
|
|
- for component in data {
|
|
|
- if let str = component as? String {
|
|
|
- let message = TSChatMessage(text: str, user: user, messageId: UUID().uuidString, date: Date())
|
|
|
- insertMessage(message)
|
|
|
- //保存这条消息到本地数据库
|
|
|
- //发送消息后,进行AI 对话生成
|
|
|
- generativeAIChat(message: message)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func generativeAIChat(message:TSChatMessage) {
|
|
|
- var messageString = ""
|
|
|
- switch message.kind {
|
|
|
- case .text(let message):
|
|
|
- messageString = message
|
|
|
- default:
|
|
|
- break
|
|
|
- }
|
|
|
-
|
|
|
- if messageString.count == 0 {
|
|
|
- return
|
|
|
- }
|
|
|
-
|
|
|
- let message = TSChatMessage(attributedText: kMDAttributedString(text: ""), user: viewModel.kAIUser, messageId: UUID().uuidString, date: Date())
|
|
|
- message.sendState = .start
|
|
|
- insertMessage(message)
|
|
|
-
|
|
|
- inputBarVC.sendEnabled(enabled: false)
|
|
|
- viewModel.sendChatMessage(message: messageString) {[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)
|
|
|
- updataAIChatCellUI()
|
|
|
-
|
|
|
- } completion: {[weak self] data, error in
|
|
|
- guard let self = self else { return }
|
|
|
- if let netData = data {
|
|
|
- message.sendState = .success("netData")
|
|
|
- //保存这条消息到本地数据库
|
|
|
- //消耗一次 AI 次数
|
|
|
- kPurchaseDefault.useOnceForFree(type: .aichat)
|
|
|
-
|
|
|
- }else {
|
|
|
- message.kind = .attributedText(kMDAttributedString(text: kAIErrorString))
|
|
|
- message.sendState = .failed(kAIErrorString)
|
|
|
- //保存这条消息到本地数据库
|
|
|
- }
|
|
|
- updataAIChatCellUI()
|
|
|
-
|
|
|
- kExecuteOnMainThread {
|
|
|
- self.inputBarVC.sendEnabled(enabled: true)
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- func updataAIChatCellUI(){
|
|
|
- kExecuteOnMainThread {
|
|
|
- if self.messageList.count >= 2 {
|
|
|
- UIView.performWithoutAnimation {
|
|
|
- self.messagesCollectionView.reloadItems(at: [self.lastIndexPath])
|
|
|
- }
|
|
|
- }else{
|
|
|
- self.messagesCollectionView.reloadData()
|
|
|
- }
|
|
|
-
|
|
|
- //更新 Vip
|
|
|
- if kPurchaseDefault.isVip == false{
|
|
|
- self.updateVipView()
|
|
|
- }
|
|
|
-
|
|
|
- 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)
|
|
|
- view.endEditing(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
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
-
|
|
|
-}
|