Browse Source

Merge branch 'AIChat' into 合并分支

# Conflicts:
#	AIEmoji.xcodeproj/project.pbxproj
100Years 1 month ago
parent
commit
ca82aacb27
100 changed files with 4237 additions and 284 deletions
  1. 233 204
      AIEmoji.xcodeproj/project.pbxproj
  2. 24 0
      AIEmoji.xcworkspace/xcshareddata/swiftpm/Package.resolved
  3. 15 2
      AIEmoji/AppDelegate.swift
  4. 6 0
      AIEmoji/Assets.xcassets/AIChat/Contents.json
  5. 22 0
      AIEmoji/Assets.xcassets/AIChat/aichat_avatar.imageset/Contents.json
  6. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_avatar.imageset/aichat_avatar@2x.png
  7. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_avatar.imageset/aichat_avatar@3x.png
  8. 22 0
      AIEmoji/Assets.xcassets/AIChat/aichat_history.imageset/Contents.json
  9. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_history.imageset/aichat_history@2x.png
  10. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_history.imageset/aichat_history@3x.png
  11. 22 0
      AIEmoji/Assets.xcassets/AIChat/aichat_refresh.imageset/Contents.json
  12. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_refresh.imageset/aichat_refresh@2x.png
  13. BIN
      AIEmoji/Assets.xcassets/AIChat/aichat_refresh.imageset/aichat_refresh@3x.png
  14. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/Contents.json
  15. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@2x.png
  16. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send.imageset/chat_send@3x.png
  17. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/Contents.json
  18. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@2x.png
  19. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_magnify.imageset/chat_send_magnify@3x.png
  20. 22 0
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/Contents.json
  21. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@2x.png
  22. BIN
      AIEmoji/Assets.xcassets/AIChat/chat_send_minify.imageset/chat_send_minify@3x.png
  23. 22 0
      AIEmoji/Assets.xcassets/AIChat/nav_title_aichat.imageset/Contents.json
  24. BIN
      AIEmoji/Assets.xcassets/AIChat/nav_title_aichat.imageset/nav_title_chat@2x.png
  25. BIN
      AIEmoji/Assets.xcassets/AIChat/nav_title_aichat.imageset/nav_title_chat@3x.png
  26. 22 0
      AIEmoji/Assets.xcassets/Common/delete_white.imageset/Contents.json
  27. BIN
      AIEmoji/Assets.xcassets/Common/delete_white.imageset/delete_white@2x.png
  28. BIN
      AIEmoji/Assets.xcassets/Common/delete_white.imageset/delete_white@3x.png
  29. 22 0
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/Contents.json
  30. BIN
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@2x.png
  31. BIN
      AIEmoji/Assets.xcassets/Common/down_arrow_line.imageset/down_arrow@3x.png
  32. 22 0
      AIEmoji/Assets.xcassets/Common/pageNull.imageset/Contents.json
  33. BIN
      AIEmoji/Assets.xcassets/Common/pageNull.imageset/pageNull@2x.png
  34. BIN
      AIEmoji/Assets.xcassets/Common/pageNull.imageset/pageNull@3x.png
  35. 22 0
      AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/Contents.json
  36. BIN
      AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/tabbar_select_aichat@2x.png
  37. BIN
      AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/tabbar_select_aichat@3x.png
  38. 22 0
      AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/Contents.json
  39. BIN
      AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/tabbar_unSelect_aichat@2x.png
  40. BIN
      AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/tabbar_unSelect_aichat@3x.png
  41. BIN
      AIEmoji/Assets.xcassets/VIP/nav_vip.imageset/nav_vip@2x.png
  42. BIN
      AIEmoji/Assets.xcassets/VIP/nav_vip.imageset/nav_vip@3x.png
  43. 22 0
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/Contents.json
  44. BIN
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@2x.png
  45. BIN
      AIEmoji/Assets.xcassets/VIP/vip_upgrade_bg.imageset/vip_upgrade_bg@3x.png
  46. 297 0
      AIEmoji/Business/AIChat/Data Generation/Lorem.swift
  47. 17 0
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/M/TSAIChatHistoryModel.swift
  48. 263 0
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSAIChatHistoryVC.swift
  49. 136 0
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSViewController.swift
  50. 77 0
      AIEmoji/Business/AIChat/TSAIChatHistoryVC/VM/TSAIChatHistoryVM.swift
  51. 70 0
      AIEmoji/Business/AIChat/TSChatViewController/Layout/CustomMessageFlowLayout.swift
  52. 63 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/MockMessage.swift
  53. 46 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatCellConfig.swift
  54. 34 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatUser.swift
  55. 208 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift
  56. 76 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift
  57. 75 0
      AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift
  58. 202 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputBarVC.swift
  59. 190 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputFullScreenVC.swift
  60. 304 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+ChatDelegate.swift
  61. 150 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+Keyboard.swift
  62. 158 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+NaviBar.swift
  63. 102 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+SendMsg.swift
  64. 100 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+VipView.swift
  65. 180 0
      AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController.swift
  66. 140 0
      AIEmoji/Business/AIChat/TSChatViewController/ViewModel/TSAIChatVM.swift
  67. 183 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/CameraInputBarAccessoryView.swift
  68. 205 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift
  69. 167 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift
  70. 63 0
      AIEmoji/Business/AIChat/TSChatViewController/Views/TableViewCells.swift
  71. 12 0
      AIEmoji/Business/General/Ex/Notification+Ex.swift
  72. 1 1
      AIEmoji/Business/General/TSBottomAlertVC.swift
  73. 15 12
      AIEmoji/Business/General/TSSmallIconBrowseVC/TSSmallIconBrowseVC.swift
  74. 1 1
      AIEmoji/Business/LaunchVC/TSLaunchVC.swift
  75. 4 4
      AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/TSEmojisChildVC.swift
  76. 4 1
      AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/VIew/TSEmojisCoLItemCell.swift
  77. 4 4
      AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/ViewModel/TSEmojisChildColViewModel.swift
  78. 1 1
      AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/ViewModel/TSEmojisChildViewModel.swift
  79. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateVC.swift
  80. 2 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiGennerateVC/TSGenmojiGennerateViewModel.swift
  81. 5 9
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift
  82. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiColSectionView.swift
  83. 16 3
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift
  84. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiItemCell.swift
  85. 2 2
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiTextView.swift
  86. 6 6
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/ViewModel/TSGenmojiCollectionViewModel.swift
  87. 1 1
      AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/ViewModel/TSGenmojiViewModel.swift
  88. 15 2
      AIEmoji/Business/TSPurchaseMembershipVC/TSPurchaseVC.swift
  89. 3 2
      AIEmoji/Business/TSSetingVC/SetingVC/TSSetingViewModel.swift
  90. 1 1
      AIEmoji/Business/TSSetingVC/SetingVC/View/SettingPurchaseTopView.swift
  91. 6 6
      AIEmoji/Business/TSSetingVC/SetingVC/View/TSSettingListView.swift
  92. 1 1
      AIEmoji/Business/TSSetingVC/TSBusinessWebVC/TSBusinessWebVC.swift
  93. 6 3
      AIEmoji/Business/TSTabBarController/TSTabBarController.swift
  94. 2 2
      AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Elemnet/DiyFixedTextElement.swift
  95. 2 2
      AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Elemnet/DiyTextElement.swift
  96. 1 1
      AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Templates/DiyPaperTemplateBaseView.swift
  97. 2 2
      AIEmoji/Business/TSWallpaperVC/TSDiyKeyboardVC/TSWallpaperVC.swift
  98. 5 6
      AIEmoji/Business/TSWallpaperVC/TSDiyKeyboardViewVC/View/TSKeyboardView.swift
  99. 45 0
      AIEmoji/Business/VIewTool/TSPageNullView.swift
  100. 1 1
      AIEmoji/Business/VIewTool/TSViewTool.swift

+ 233 - 204
AIEmoji.xcodeproj/project.pbxproj

@@ -21,7 +21,6 @@
 		A80E723F2D3F4D3700C64288 /* Universe🌍.json in Resources */ = {isa = PBXBuildFile; fileRef = A80E723E2D3F4D3000C64288 /* Universe🌍.json */; };
 		A80E72462D3F4EED00C64288 /* sticker.json in Resources */ = {isa = PBXBuildFile; fileRef = A80E72452D3F4EED00C64288 /* sticker.json */; };
 		A80E72482D3F4F0A00C64288 /* templates.json in Resources */ = {isa = PBXBuildFile; fileRef = A80E72472D3F4F0500C64288 /* templates.json */; };
-		A80E724D2D3F515B00C64288 /* TSBaseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E724C2D3F515200C64288 /* TSBaseViewModel.swift */; };
 		A80E724F2D3F6D7F00C64288 /* DiyFixedTextElement.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E724E2D3F6D6000C64288 /* DiyFixedTextElement.swift */; };
 		A80E72532D3F985E00C64288 /* TSWallpaperVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72522D3F985D00C64288 /* TSWallpaperVC.swift */; };
 		A80E72562D3F98D700C64288 /* TSDiyKeyboardViewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72552D3F98D600C64288 /* TSDiyKeyboardViewVC.swift */; };
@@ -34,13 +33,41 @@
 		A80E726C2D409E8300C64288 /* TSDiyTLPinkAnimalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E726B2D409E8000C64288 /* TSDiyTLPinkAnimalView.swift */; };
 		A80E726F2D40DE2B00C64288 /* TSWallpaperPreviewVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E726E2D40DE2900C64288 /* TSWallpaperPreviewVC.swift */; };
 		A80E72722D40F86000C64288 /* TSLaunchVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72712D40F86000C64288 /* TSLaunchVC.swift */; };
-		A80E72742D40FEA600C64288 /* UIApplication+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72732D40FEA000C64288 /* UIApplication+Ex.swift */; };
 		A80E72772D41EFF900C64288 /* TSEmojisTutorialsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72762D41EFF700C64288 /* TSEmojisTutorialsVC.swift */; };
 		A80E72792D42285500C64288 /* TSBootPageVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E72782D42285500C64288 /* TSBootPageVC.swift */; };
 		A80E73E12D533E5800C64288 /* TSPurchaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E73D82D533E5800C64288 /* TSPurchaseVC.swift */; };
 		A80E73E42D533EB000C64288 /* TSPurchaseManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E73E22D533EB000C64288 /* TSPurchaseManager.swift */; };
 		A80E73E62D5348D000C64288 /* SettingPurchaseTopView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A80E73E52D5348CF00C64288 /* SettingPurchaseTopView.swift */; };
 		A89EA6982D5B1A01000EB181 /* Butterfly🦋.json in Resources */ = {isa = PBXBuildFile; fileRef = A89EA6972D5B19F0000EB181 /* Butterfly🦋.json */; };
+		A89EA64B2D59A588000EB181 /* MessageKit in Frameworks */ = {isa = PBXBuildFile; productRef = A89EA64A2D59A588000EB181 /* MessageKit */; };
+		A89EA6542D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */; };
+		A89EA6552D59A9F4000EB181 /* MockMessage.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6502D59A9F4000EB181 /* MockMessage.swift */; };
+		A89EA6562D59A9F4000EB181 /* TSChatUser.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6522D59A9F4000EB181 /* TSChatUser.swift */; };
+		A89EA6582D59A9F4000EB181 /* TSLayoutSizeCalculator.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */; };
+		A89EA6592D59A9F4000EB181 /* CustomMessageFlowLayout.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA64C2D59A9F4000EB181 /* CustomMessageFlowLayout.swift */; };
+		A89EA65F2D59AA11000EB181 /* TSChatViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */; };
+		A89EA6692D59AA31000EB181 /* CameraInputBarAccessoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */; };
+		A89EA66B2D59AA31000EB181 /* TSTextMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */; };
+		A89EA66C2D59AA31000EB181 /* TSMessageContentCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */; };
+		A89EA66D2D59AA31000EB181 /* TableViewCells.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6672D59AA31000EB181 /* TableViewCells.swift */; };
+		A89EA67A2D59D25F000EB181 /* TSAIChatVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6792D59D248000EB181 /* TSAIChatVM.swift */; };
+		A89EA67D2D59F1AF000EB181 /* StreamPostRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA67C2D59F1AC000EB181 /* StreamPostRequest.swift */; };
+		A89EA6832D59F4F9000EB181 /* TSChatViewController+ChatDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6822D59F4F1000EB181 /* TSChatViewController+ChatDelegate.swift */; };
+		A89EA6A32D5B26E3000EB181 /* TSDBAIChatList.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6A22D5B26E3000EB181 /* TSDBAIChatList.swift */; };
+		A89EA6AC2D5B3EFB000EB181 /* TSRealmManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6AB2D5B3EFA000EB181 /* TSRealmManager.swift */; };
+		A89EA6B12D5C9D0C000EB181 /* TSAIChatHistoryVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6B02D5C9D0B000EB181 /* TSAIChatHistoryVC.swift */; };
+		A89EA6B42D5C9D43000EB181 /* TSAIChatHistoryVM.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6B32D5C9D3D000EB181 /* TSAIChatHistoryVM.swift */; };
+		A89EA6B82D5D7EE9000EB181 /* TSAIChatHistoryModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6B72D5D7EE4000EB181 /* TSAIChatHistoryModel.swift */; };
+		A89EA6BA2D5DDE5B000EB181 /* TSPageNullView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6B92D5DDE4E000EB181 /* TSPageNullView.swift */; };
+		A89EA6BC2D5DFB12000EB181 /* TSViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6BB2D5DFB0D000EB181 /* TSViewController.swift */; };
+		A89EA6BF2D5E03D6000EB181 /* Notification+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6BE2D5E03D3000EB181 /* Notification+Ex.swift */; };
+		A89EA6C12D5ED289000EB181 /* TSChatCellConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */; };
+		A89EA6C42D5F40CC000EB181 /* TSChatInputBarVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */; };
+		A89EA6C62D5F5C22000EB181 /* TSChatInputFullScreenVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */; };
+		A89EA6C82D6359ED000EB181 /* TSChatViewController+VipView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C72D6359EA000EB181 /* TSChatViewController+VipView.swift */; };
+		A89EA6CA2D642C0A000EB181 /* TSChatViewController+SendMsg.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6C92D642C03000EB181 /* TSChatViewController+SendMsg.swift */; };
+		A89EA6CC2D642CE2000EB181 /* TSChatViewController+NaviBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6CB2D642CD4000EB181 /* TSChatViewController+NaviBar.swift */; };
+		A89EA6CF2D6430F3000EB181 /* TSChatViewController+Keyboard.swift in Sources */ = {isa = PBXBuildFile; fileRef = A89EA6CE2D6430EE000EB181 /* TSChatViewController+Keyboard.swift */; };
 		A8EEADD42D3E6C660032C5A0 /* Flower💐.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD32D3E6C610032C5A0 /* Flower💐.json */; };
 		A8EEADD62D3E6CD80032C5A0 /* Fish🐠.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD52D3E6CD30032C5A0 /* Fish🐠.json */; };
 		A8EEADD82D3E74D20032C5A0 /* Pink🩷.json in Resources */ = {isa = PBXBuildFile; fileRef = A8EEADD72D3E74CB0032C5A0 /* Pink🩷.json */; };
@@ -52,52 +79,16 @@
 		A8F7748B2D38E8B700AA6E93 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774812D38E8B700AA6E93 /* AppDelegate.swift */; };
 		A8F7748E2D38E8B700AA6E93 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = A8F774822D38E8B700AA6E93 /* Assets.xcassets */; };
 		A8F774902D38E8B700AA6E93 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = A8F774852D38E8B700AA6E93 /* LaunchScreen.storyboard */; };
-		A8F774D92D38EA8C00AA6E93 /* UICollectionView+More.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774D22D38EA8C00AA6E93 /* UICollectionView+More.swift */; };
-		A8F774DA2D38EA8C00AA6E93 /* UIFont+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AA2D38EA8C00AA6E93 /* UIFont+Ex.swift */; };
-		A8F774DB2D38EA8C00AA6E93 /* AVAsset+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A02D38EA8C00AA6E93 /* AVAsset+Ex.swift */; };
-		A8F774DC2D38EA8C00AA6E93 /* NSString+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A52D38EA8C00AA6E93 /* NSString+Ex.swift */; };
-		A8F774DE2D38EA8C00AA6E93 /* UIDevice+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A92D38EA8C00AA6E93 /* UIDevice+Extension.swift */; };
 		A8F774E02D38EA8C00AA6E93 /* TSCommonTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774C72D38EA8C00AA6E93 /* TSCommonTool.swift */; };
 		A8F774E12D38EA8C00AA6E93 /* TSFileManagerTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774CA2D38EA8C00AA6E93 /* TSFileManagerTool.swift */; };
-		A8F774E22D38EA8C00AA6E93 /* UICollectionView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A72D38EA8C00AA6E93 /* UICollectionView+Ex.swift */; };
-		A8F774E32D38EA8C00AA6E93 /* TSBasicItemModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7749A2D38EA8C00AA6E93 /* TSBasicItemModel.swift */; };
-		A8F774E42D38EA8C00AA6E93 /* Component.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774D12D38EA8C00AA6E93 /* Component.swift */; };
-		A8F774E52D38EA8C00AA6E93 /* Color+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7749C2D38EA8C00AA6E93 /* Color+Ex.swift */; };
-		A8F774E62D38EA8C00AA6E93 /* UIScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AE2D38EA8C00AA6E93 /* UIScreen.swift */; };
-		A8F774E82D38EA8C00AA6E93 /* CollectionViewComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774CF2D38EA8C00AA6E93 /* CollectionViewComponent.swift */; };
-		A8F774E92D38EA8C00AA6E93 /* UILabel+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AD2D38EA8C00AA6E93 /* UILabel+Ex.swift */; };
-		A8F774EB2D38EA8C00AA6E93 /* TSBaseNavigationC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774962D38EA8C00AA6E93 /* TSBaseNavigationC.swift */; };
 		A8F774EC2D38EA8C00AA6E93 /* TSToastTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774CC2D38EA8C00AA6E93 /* TSToastTool.swift */; };
-		A8F774ED2D38EA8C00AA6E93 /* Font+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7749D2D38EA8C00AA6E93 /* Font+Ex.swift */; };
-		A8F774EE2D38EA8C00AA6E93 /* UIViewController+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774B02D38EA8C00AA6E93 /* UIViewController+Ex.swift */; };
-		A8F774EF2D38EA8C00AA6E93 /* UIImage+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AB2D38EA8C00AA6E93 /* UIImage+Ex.swift */; };
-		A8F774F02D38EA8C00AA6E93 /* TSBaseCollectionCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774932D38EA8C00AA6E93 /* TSBaseCollectionCell.swift */; };
-		A8F774F12D38EA8C00AA6E93 /* CGFloat+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A12D38EA8C00AA6E93 /* CGFloat+Ex.swift */; };
-		A8F774F22D38EA8C00AA6E93 /* TSBaseView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774992D38EA8C00AA6E93 /* TSBaseView.swift */; };
-		A8F774F42D38EA8C00AA6E93 /* TSBaseNavigationBarView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774952D38EA8C00AA6E93 /* TSBaseNavigationBarView.swift */; };
-		A8F774F52D38EA8C00AA6E93 /* Array+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7749F2D38EA8C00AA6E93 /* Array+Ex.swift */; };
-		A8F774F62D38EA8C00AA6E93 /* UIImageView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AC2D38EA8C00AA6E93 /* UIImageView+Ex.swift */; };
-		A8F774F82D38EA8C00AA6E93 /* TSSimpleTableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774D42D38EA8C00AA6E93 /* TSSimpleTableView.swift */; };
-		A8F774F92D38EA8C00AA6E93 /* UIColor+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A82D38EA8C00AA6E93 /* UIColor+Ex.swift */; };
-		A8F774FA2D38EA8C00AA6E93 /* TSBaseModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774942D38EA8C00AA6E93 /* TSBaseModel.swift */; };
-		A8F774FE2D38EA8C00AA6E93 /* Int+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A42D38EA8C00AA6E93 /* Int+Ex.swift */; };
-		A8F774FF2D38EA8C00AA6E93 /* UserDefault+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774B12D38EA8C00AA6E93 /* UserDefault+Ex.swift */; };
 		A8F775002D38EA8C00AA6E93 /* WindowHelper.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774CD2D38EA8C00AA6E93 /* WindowHelper.swift */; };
-		A8F775012D38EA8C00AA6E93 /* UIButton+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A62D38EA8C00AA6E93 /* UIButton+Ex.swift */; };
-		A8F775022D38EA8C00AA6E93 /* Dictionary+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A32D38EA8C00AA6E93 /* Dictionary+Ex.swift */; };
 		A8F775032D38EA8C00AA6E93 /* GlobalImports.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774B42D38EA8C00AA6E93 /* GlobalImports.swift */; };
-		A8F775062D38EA8C00AA6E93 /* Date+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774A22D38EA8C00AA6E93 /* Date+Ex.swift */; };
-		A8F775072D38EA8C00AA6E93 /* UIView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774B22D38EA8C00AA6E93 /* UIView+Ex.swift */; };
-		A8F775082D38EA8C00AA6E93 /* UITableView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774AF2D38EA8C00AA6E93 /* UITableView+Ex.swift */; };
-		A8F775092D38EA8C00AA6E93 /* CommonSectionComponent.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774D02D38EA8C00AA6E93 /* CommonSectionComponent.swift */; };
 		A8F7750A2D38EA8C00AA6E93 /* TSNetworkTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774CB2D38EA8C00AA6E93 /* TSNetworkTool.swift */; };
-		A8F7750B2D38EA8C00AA6E93 /* TSBaseTabViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774972D38EA8C00AA6E93 /* TSBaseTabViewCell.swift */; };
-		A8F7750D2D38EA8C00AA6E93 /* TSBaseVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F774982D38EA8C00AA6E93 /* TSBaseVC.swift */; };
 		A8F775172D38EB7400AA6E93 /* TSTabBarController.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775162D38EB7400AA6E93 /* TSTabBarController.swift */; };
 		A8F775192D38EC6800AA6E93 /* TSEmojisVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775182D38EC6700AA6E93 /* TSEmojisVC.swift */; };
 		A8F7751B2D38EC9800AA6E93 /* TSGenmojiVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7751A2D38EC9700AA6E93 /* TSGenmojiVC.swift */; };
 		A8F775252D38ED8300AA6E93 /* TSConfig.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775232D38ED8300AA6E93 /* TSConfig.swift */; };
-		A8F775322D38FA5E00AA6E93 /* UITextView+Ex.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775312D38FA5200AA6E93 /* UITextView+Ex.swift */; };
 		A8F775352D38FC9A00AA6E93 /* TSViewTool.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775342D38FC9A00AA6E93 /* TSViewTool.swift */; };
 		A8F775382D390C3C00AA6E93 /* TSNetworkManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F775372D390C3C00AA6E93 /* TSNetworkManager.swift */; };
 		A8F7753B2D3918DE00AA6E93 /* TSNetworkManager+Loading.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7753A2D3918D700AA6E93 /* TSNetworkManager+Loading.swift */; };
@@ -109,7 +100,6 @@
 		A8F7754B2D39376800AA6E93 /* TSSettingListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7754A2D39376700AA6E93 /* TSSettingListView.swift */; };
 		A8F7754E2D39E59100AA6E93 /* TSGenmojiModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7754D2D39E58B00AA6E93 /* TSGenmojiModel.swift */; };
 		A8F775502D39ECED00AA6E93 /* PhotoManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7754F2D39ECED00AA6E93 /* PhotoManager.swift */; };
-		A8F775532D3A038800AA6E93 /* SwiftUIX in Frameworks */ = {isa = PBXBuildFile; productRef = A8F775522D3A038800AA6E93 /* SwiftUIX */; };
 		A8F776212D3A3F0200AA6E93 /* TSEmojisChildVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F776202D3A3F0100AA6E93 /* TSEmojisChildVC.swift */; };
 		A8F776272D3A6EC200AA6E93 /* TSGenmojiTextView.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F776262D3A6EBA00AA6E93 /* TSGenmojiTextView.swift */; };
 		A8F7762B2D3A70B200AA6E93 /* PaddedLabel.swift in Sources */ = {isa = PBXBuildFile; fileRef = A8F7762A2D3A70AF00AA6E93 /* PaddedLabel.swift */; };
@@ -152,7 +142,6 @@
 		A80E723E2D3F4D3000C64288 /* Universe🌍.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Universe🌍.json"; sourceTree = "<group>"; };
 		A80E72452D3F4EED00C64288 /* sticker.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = sticker.json; sourceTree = "<group>"; };
 		A80E72472D3F4F0500C64288 /* templates.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = templates.json; sourceTree = "<group>"; };
-		A80E724C2D3F515200C64288 /* TSBaseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseViewModel.swift; sourceTree = "<group>"; };
 		A80E724E2D3F6D6000C64288 /* DiyFixedTextElement.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DiyFixedTextElement.swift; sourceTree = "<group>"; };
 		A80E72522D3F985D00C64288 /* TSWallpaperVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSWallpaperVC.swift; sourceTree = "<group>"; };
 		A80E72552D3F98D600C64288 /* TSDiyKeyboardViewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiyKeyboardViewVC.swift; sourceTree = "<group>"; };
@@ -165,13 +154,40 @@
 		A80E726B2D409E8000C64288 /* TSDiyTLPinkAnimalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDiyTLPinkAnimalView.swift; sourceTree = "<group>"; };
 		A80E726E2D40DE2900C64288 /* TSWallpaperPreviewVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSWallpaperPreviewVC.swift; sourceTree = "<group>"; };
 		A80E72712D40F86000C64288 /* TSLaunchVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSLaunchVC.swift; sourceTree = "<group>"; };
-		A80E72732D40FEA000C64288 /* UIApplication+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Ex.swift"; sourceTree = "<group>"; };
 		A80E72762D41EFF700C64288 /* TSEmojisTutorialsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSEmojisTutorialsVC.swift; sourceTree = "<group>"; };
 		A80E72782D42285500C64288 /* TSBootPageVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBootPageVC.swift; sourceTree = "<group>"; };
 		A80E73D82D533E5800C64288 /* TSPurchaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseVC.swift; sourceTree = "<group>"; };
 		A80E73E22D533EB000C64288 /* TSPurchaseManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPurchaseManager.swift; sourceTree = "<group>"; };
 		A80E73E52D5348CF00C64288 /* SettingPurchaseTopView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingPurchaseTopView.swift; sourceTree = "<group>"; };
 		A89EA6972D5B19F0000EB181 /* Butterfly🦋.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Butterfly🦋.json"; sourceTree = "<group>"; };
+		A89EA64C2D59A9F4000EB181 /* CustomMessageFlowLayout.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomMessageFlowLayout.swift; sourceTree = "<group>"; };
+		A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSLayoutSizeCalculator.swift; sourceTree = "<group>"; };
+		A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextLayoutSizeCalculator.swift; sourceTree = "<group>"; };
+		A89EA6502D59A9F4000EB181 /* MockMessage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockMessage.swift; sourceTree = "<group>"; };
+		A89EA6522D59A9F4000EB181 /* TSChatUser.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatUser.swift; sourceTree = "<group>"; };
+		A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatViewController.swift; sourceTree = "<group>"; };
+		A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CameraInputBarAccessoryView.swift; sourceTree = "<group>"; };
+		A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSMessageContentCell.swift; sourceTree = "<group>"; };
+		A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTextMessageContentCell.swift; sourceTree = "<group>"; };
+		A89EA6672D59AA31000EB181 /* TableViewCells.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TableViewCells.swift; sourceTree = "<group>"; };
+		A89EA6792D59D248000EB181 /* TSAIChatVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatVM.swift; sourceTree = "<group>"; };
+		A89EA67C2D59F1AC000EB181 /* StreamPostRequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = StreamPostRequest.swift; sourceTree = "<group>"; };
+		A89EA6822D59F4F1000EB181 /* TSChatViewController+ChatDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSChatViewController+ChatDelegate.swift"; sourceTree = "<group>"; };
+		A89EA6A22D5B26E3000EB181 /* TSDBAIChatList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSDBAIChatList.swift; sourceTree = "<group>"; };
+		A89EA6AB2D5B3EFA000EB181 /* TSRealmManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSRealmManager.swift; sourceTree = "<group>"; };
+		A89EA6B02D5C9D0B000EB181 /* TSAIChatHistoryVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatHistoryVC.swift; sourceTree = "<group>"; };
+		A89EA6B32D5C9D3D000EB181 /* TSAIChatHistoryVM.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatHistoryVM.swift; sourceTree = "<group>"; };
+		A89EA6B72D5D7EE4000EB181 /* TSAIChatHistoryModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSAIChatHistoryModel.swift; sourceTree = "<group>"; };
+		A89EA6B92D5DDE4E000EB181 /* TSPageNullView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSPageNullView.swift; sourceTree = "<group>"; };
+		A89EA6BB2D5DFB0D000EB181 /* TSViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSViewController.swift; sourceTree = "<group>"; };
+		A89EA6BE2D5E03D3000EB181 /* Notification+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Notification+Ex.swift"; sourceTree = "<group>"; };
+		A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatCellConfig.swift; sourceTree = "<group>"; };
+		A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatInputBarVC.swift; sourceTree = "<group>"; };
+		A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSChatInputFullScreenVC.swift; sourceTree = "<group>"; };
+		A89EA6C72D6359EA000EB181 /* TSChatViewController+VipView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSChatViewController+VipView.swift"; sourceTree = "<group>"; };
+		A89EA6C92D642C03000EB181 /* TSChatViewController+SendMsg.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSChatViewController+SendMsg.swift"; sourceTree = "<group>"; };
+		A89EA6CB2D642CD4000EB181 /* TSChatViewController+NaviBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSChatViewController+NaviBar.swift"; sourceTree = "<group>"; };
+		A89EA6CE2D6430EE000EB181 /* TSChatViewController+Keyboard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSChatViewController+Keyboard.swift"; sourceTree = "<group>"; };
 		A8EEADD32D3E6C610032C5A0 /* Flower💐.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Flower💐.json"; sourceTree = "<group>"; };
 		A8EEADD52D3E6CD30032C5A0 /* Fish🐠.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Fish🐠.json"; sourceTree = "<group>"; };
 		A8EEADD72D3E74CB0032C5A0 /* Pink🩷.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = "Pink🩷.json"; sourceTree = "<group>"; };
@@ -185,52 +201,16 @@
 		A8F774822D38E8B700AA6E93 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
 		A8F774832D38E8B700AA6E93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
 		A8F774842D38E8B700AA6E93 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; };
-		A8F774932D38EA8C00AA6E93 /* TSBaseCollectionCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseCollectionCell.swift; sourceTree = "<group>"; };
-		A8F774942D38EA8C00AA6E93 /* TSBaseModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseModel.swift; sourceTree = "<group>"; };
-		A8F774952D38EA8C00AA6E93 /* TSBaseNavigationBarView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseNavigationBarView.swift; sourceTree = "<group>"; };
-		A8F774962D38EA8C00AA6E93 /* TSBaseNavigationC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseNavigationC.swift; sourceTree = "<group>"; };
-		A8F774972D38EA8C00AA6E93 /* TSBaseTabViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseTabViewCell.swift; sourceTree = "<group>"; };
-		A8F774982D38EA8C00AA6E93 /* TSBaseVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseVC.swift; sourceTree = "<group>"; };
-		A8F774992D38EA8C00AA6E93 /* TSBaseView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBaseView.swift; sourceTree = "<group>"; };
-		A8F7749A2D38EA8C00AA6E93 /* TSBasicItemModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSBasicItemModel.swift; sourceTree = "<group>"; };
-		A8F7749C2D38EA8C00AA6E93 /* Color+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Color+Ex.swift"; sourceTree = "<group>"; };
-		A8F7749D2D38EA8C00AA6E93 /* Font+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Font+Ex.swift"; sourceTree = "<group>"; };
-		A8F7749F2D38EA8C00AA6E93 /* Array+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Array+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A02D38EA8C00AA6E93 /* AVAsset+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "AVAsset+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A12D38EA8C00AA6E93 /* CGFloat+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CGFloat+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A22D38EA8C00AA6E93 /* Date+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Date+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A32D38EA8C00AA6E93 /* Dictionary+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Dictionary+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A42D38EA8C00AA6E93 /* Int+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Int+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A52D38EA8C00AA6E93 /* NSString+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NSString+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A62D38EA8C00AA6E93 /* UIButton+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIButton+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A72D38EA8C00AA6E93 /* UICollectionView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A82D38EA8C00AA6E93 /* UIColor+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIColor+Ex.swift"; sourceTree = "<group>"; };
-		A8F774A92D38EA8C00AA6E93 /* UIDevice+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIDevice+Extension.swift"; sourceTree = "<group>"; };
-		A8F774AA2D38EA8C00AA6E93 /* UIFont+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIFont+Ex.swift"; sourceTree = "<group>"; };
-		A8F774AB2D38EA8C00AA6E93 /* UIImage+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImage+Ex.swift"; sourceTree = "<group>"; };
-		A8F774AC2D38EA8C00AA6E93 /* UIImageView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIImageView+Ex.swift"; sourceTree = "<group>"; };
-		A8F774AD2D38EA8C00AA6E93 /* UILabel+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UILabel+Ex.swift"; sourceTree = "<group>"; };
-		A8F774AE2D38EA8C00AA6E93 /* UIScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIScreen.swift; sourceTree = "<group>"; };
-		A8F774AF2D38EA8C00AA6E93 /* UITableView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITableView+Ex.swift"; sourceTree = "<group>"; };
-		A8F774B02D38EA8C00AA6E93 /* UIViewController+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIViewController+Ex.swift"; sourceTree = "<group>"; };
-		A8F774B12D38EA8C00AA6E93 /* UserDefault+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UserDefault+Ex.swift"; sourceTree = "<group>"; };
-		A8F774B22D38EA8C00AA6E93 /* UIView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIView+Ex.swift"; sourceTree = "<group>"; };
 		A8F774B42D38EA8C00AA6E93 /* GlobalImports.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GlobalImports.swift; sourceTree = "<group>"; };
 		A8F774C72D38EA8C00AA6E93 /* TSCommonTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSCommonTool.swift; sourceTree = "<group>"; };
 		A8F774CA2D38EA8C00AA6E93 /* TSFileManagerTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSFileManagerTool.swift; sourceTree = "<group>"; };
 		A8F774CB2D38EA8C00AA6E93 /* TSNetworkTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSNetworkTool.swift; sourceTree = "<group>"; };
 		A8F774CC2D38EA8C00AA6E93 /* TSToastTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSToastTool.swift; sourceTree = "<group>"; };
 		A8F774CD2D38EA8C00AA6E93 /* WindowHelper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WindowHelper.swift; sourceTree = "<group>"; };
-		A8F774CF2D38EA8C00AA6E93 /* CollectionViewComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CollectionViewComponent.swift; sourceTree = "<group>"; };
-		A8F774D02D38EA8C00AA6E93 /* CommonSectionComponent.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CommonSectionComponent.swift; sourceTree = "<group>"; };
-		A8F774D12D38EA8C00AA6E93 /* Component.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Component.swift; sourceTree = "<group>"; };
-		A8F774D22D38EA8C00AA6E93 /* UICollectionView+More.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+More.swift"; sourceTree = "<group>"; };
-		A8F774D42D38EA8C00AA6E93 /* TSSimpleTableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSSimpleTableView.swift; sourceTree = "<group>"; };
 		A8F775162D38EB7400AA6E93 /* TSTabBarController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSTabBarController.swift; sourceTree = "<group>"; };
 		A8F775182D38EC6700AA6E93 /* TSEmojisVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSEmojisVC.swift; sourceTree = "<group>"; };
 		A8F7751A2D38EC9700AA6E93 /* TSGenmojiVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSGenmojiVC.swift; sourceTree = "<group>"; };
 		A8F775232D38ED8300AA6E93 /* TSConfig.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSConfig.swift; sourceTree = "<group>"; };
-		A8F775312D38FA5200AA6E93 /* UITextView+Ex.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UITextView+Ex.swift"; sourceTree = "<group>"; };
 		A8F775342D38FC9A00AA6E93 /* TSViewTool.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSViewTool.swift; sourceTree = "<group>"; };
 		A8F775372D390C3C00AA6E93 /* TSNetworkManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TSNetworkManager.swift; sourceTree = "<group>"; };
 		A8F7753A2D3918D700AA6E93 /* TSNetworkManager+Loading.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "TSNetworkManager+Loading.swift"; sourceTree = "<group>"; };
@@ -270,8 +250,8 @@
 			isa = PBXFrameworksBuildPhase;
 			buildActionMask = 2147483647;
 			files = (
+				A89EA64B2D59A588000EB181 /* MessageKit in Frameworks */,
 				D34BB2B8FE0DBF83E06A3FC4 /* Pods_AIEmoji.framework in Frameworks */,
-				A8F775532D3A038800AA6E93 /* SwiftUIX in Frameworks */,
 			);
 			runOnlyForDeploymentPostprocessing = 0;
 		};
@@ -444,6 +424,133 @@
 			path = Purchase;
 			sourceTree = "<group>";
 		};
+		A80E74222D5996BF00C64288 /* AIChat */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6AF2D5C9861000EB181 /* TSAIChatHistoryVC */,
+				A89EA6772D59D224000EB181 /* TSChatViewController */,
+			);
+			path = AIChat;
+			sourceTree = "<group>";
+		};
+		A89EA64D2D59A9F4000EB181 /* Layout */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA64C2D59A9F4000EB181 /* CustomMessageFlowLayout.swift */,
+			);
+			path = Layout;
+			sourceTree = "<group>";
+		};
+		A89EA6532D59A9F4000EB181 /* Models */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6A22D5B26E3000EB181 /* TSDBAIChatList.swift */,
+				A89EA6C02D5ED278000EB181 /* TSChatCellConfig.swift */,
+				A89EA64E2D59A9F4000EB181 /* TSLayoutSizeCalculator.swift */,
+				A89EA64F2D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift */,
+				A89EA6502D59A9F4000EB181 /* MockMessage.swift */,
+				A89EA6522D59A9F4000EB181 /* TSChatUser.swift */,
+			);
+			path = Models;
+			sourceTree = "<group>";
+		};
+		A89EA6682D59AA31000EB181 /* Views */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6632D59AA31000EB181 /* CameraInputBarAccessoryView.swift */,
+				A89EA6652D59AA31000EB181 /* TSMessageContentCell.swift */,
+				A89EA6662D59AA31000EB181 /* TSTextMessageContentCell.swift */,
+				A89EA6672D59AA31000EB181 /* TableViewCells.swift */,
+			);
+			path = Views;
+			sourceTree = "<group>";
+		};
+		A89EA6772D59D224000EB181 /* TSChatViewController */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6D02D64333D000EB181 /* TSChatViewController */,
+				A89EA6C22D5F4094000EB181 /* TSChatInputBarVC */,
+				A89EA6782D59D238000EB181 /* ViewModel */,
+				A89EA6682D59AA31000EB181 /* Views */,
+				A89EA64D2D59A9F4000EB181 /* Layout */,
+				A89EA6532D59A9F4000EB181 /* Models */,
+			);
+			path = TSChatViewController;
+			sourceTree = "<group>";
+		};
+		A89EA6782D59D238000EB181 /* ViewModel */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6792D59D248000EB181 /* TSAIChatVM.swift */,
+			);
+			path = ViewModel;
+			sourceTree = "<group>";
+		};
+		A89EA6AA2D5B3EE8000EB181 /* TSRealmManager */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6AB2D5B3EFA000EB181 /* TSRealmManager.swift */,
+			);
+			path = TSRealmManager;
+			sourceTree = "<group>";
+		};
+		A89EA6AF2D5C9861000EB181 /* TSAIChatHistoryVC */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6BB2D5DFB0D000EB181 /* TSViewController.swift */,
+				A89EA6B62D5D7ED2000EB181 /* M */,
+				A89EA6B22D5C9D36000EB181 /* VM */,
+				A89EA6B02D5C9D0B000EB181 /* TSAIChatHistoryVC.swift */,
+			);
+			path = TSAIChatHistoryVC;
+			sourceTree = "<group>";
+		};
+		A89EA6B22D5C9D36000EB181 /* VM */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6B32D5C9D3D000EB181 /* TSAIChatHistoryVM.swift */,
+			);
+			path = VM;
+			sourceTree = "<group>";
+		};
+		A89EA6B62D5D7ED2000EB181 /* M */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6B72D5D7EE4000EB181 /* TSAIChatHistoryModel.swift */,
+			);
+			path = M;
+			sourceTree = "<group>";
+		};
+		A89EA6BD2D5E03CD000EB181 /* Ex */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6BE2D5E03D3000EB181 /* Notification+Ex.swift */,
+			);
+			path = Ex;
+			sourceTree = "<group>";
+		};
+		A89EA6C22D5F4094000EB181 /* TSChatInputBarVC */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA6C52D5F5C21000EB181 /* TSChatInputFullScreenVC.swift */,
+				A89EA6C32D5F40CB000EB181 /* TSChatInputBarVC.swift */,
+			);
+			path = TSChatInputBarVC;
+			sourceTree = "<group>";
+		};
+		A89EA6D02D64333D000EB181 /* TSChatViewController */ = {
+			isa = PBXGroup;
+			children = (
+				A89EA65E2D59AA11000EB181 /* TSChatViewController.swift */,
+				A89EA6822D59F4F1000EB181 /* TSChatViewController+ChatDelegate.swift */,
+				A89EA6C72D6359EA000EB181 /* TSChatViewController+VipView.swift */,
+				A89EA6C92D642C03000EB181 /* TSChatViewController+SendMsg.swift */,
+				A89EA6CB2D642CD4000EB181 /* TSChatViewController+NaviBar.swift */,
+				A89EA6CE2D6430EE000EB181 /* TSChatViewController+Keyboard.swift */,
+			);
+			path = TSChatViewController;
+			sourceTree = "<group>";
+		};
 		A8F774602D38E8B000AA6E93 = {
 			isa = PBXGroup;
 			children = (
@@ -480,6 +587,7 @@
 		A8F774922D38EA8C00AA6E93 /* Business */ = {
 			isa = PBXGroup;
 			children = (
+				A80E74222D5996BF00C64288 /* AIChat */,
 				A80E73DD2D533E5800C64288 /* TSPurchaseMembershipVC */,
 				A80E72702D40F85800C64288 /* LaunchVC */,
 				A8F775142D38EB5300AA6E93 /* TSWallpaperVC */,
@@ -493,61 +601,6 @@
 			path = Business;
 			sourceTree = "<group>";
 		};
-		A8F7749B2D38EA8C00AA6E93 /* BaseClass */ = {
-			isa = PBXGroup;
-			children = (
-				A80E724C2D3F515200C64288 /* TSBaseViewModel.swift */,
-				A8F774932D38EA8C00AA6E93 /* TSBaseCollectionCell.swift */,
-				A8F774942D38EA8C00AA6E93 /* TSBaseModel.swift */,
-				A8F774952D38EA8C00AA6E93 /* TSBaseNavigationBarView.swift */,
-				A8F774962D38EA8C00AA6E93 /* TSBaseNavigationC.swift */,
-				A8F774972D38EA8C00AA6E93 /* TSBaseTabViewCell.swift */,
-				A8F774982D38EA8C00AA6E93 /* TSBaseVC.swift */,
-				A8F774992D38EA8C00AA6E93 /* TSBaseView.swift */,
-				A8F7749A2D38EA8C00AA6E93 /* TSBasicItemModel.swift */,
-			);
-			path = BaseClass;
-			sourceTree = "<group>";
-		};
-		A8F7749E2D38EA8C00AA6E93 /* SwiftUI */ = {
-			isa = PBXGroup;
-			children = (
-				A8F7749C2D38EA8C00AA6E93 /* Color+Ex.swift */,
-				A8F7749D2D38EA8C00AA6E93 /* Font+Ex.swift */,
-			);
-			path = SwiftUI;
-			sourceTree = "<group>";
-		};
-		A8F774B32D38EA8C00AA6E93 /* Ex */ = {
-			isa = PBXGroup;
-			children = (
-				A80E72732D40FEA000C64288 /* UIApplication+Ex.swift */,
-				A8F7749E2D38EA8C00AA6E93 /* SwiftUI */,
-				A8F7749F2D38EA8C00AA6E93 /* Array+Ex.swift */,
-				A8F774A02D38EA8C00AA6E93 /* AVAsset+Ex.swift */,
-				A8F774A12D38EA8C00AA6E93 /* CGFloat+Ex.swift */,
-				A8F774A22D38EA8C00AA6E93 /* Date+Ex.swift */,
-				A8F774A32D38EA8C00AA6E93 /* Dictionary+Ex.swift */,
-				A8F774A42D38EA8C00AA6E93 /* Int+Ex.swift */,
-				A8F774A52D38EA8C00AA6E93 /* NSString+Ex.swift */,
-				A8F774A62D38EA8C00AA6E93 /* UIButton+Ex.swift */,
-				A8F774A72D38EA8C00AA6E93 /* UICollectionView+Ex.swift */,
-				A8F774A82D38EA8C00AA6E93 /* UIColor+Ex.swift */,
-				A8F774A92D38EA8C00AA6E93 /* UIDevice+Extension.swift */,
-				A8F774AA2D38EA8C00AA6E93 /* UIFont+Ex.swift */,
-				A8F774AB2D38EA8C00AA6E93 /* UIImage+Ex.swift */,
-				A8F774AC2D38EA8C00AA6E93 /* UIImageView+Ex.swift */,
-				A8F774AD2D38EA8C00AA6E93 /* UILabel+Ex.swift */,
-				A8F774AE2D38EA8C00AA6E93 /* UIScreen.swift */,
-				A8F774AF2D38EA8C00AA6E93 /* UITableView+Ex.swift */,
-				A8F774B02D38EA8C00AA6E93 /* UIViewController+Ex.swift */,
-				A8F774B12D38EA8C00AA6E93 /* UserDefault+Ex.swift */,
-				A8F774B22D38EA8C00AA6E93 /* UIView+Ex.swift */,
-				A8F775312D38FA5200AA6E93 /* UITextView+Ex.swift */,
-			);
-			path = Ex;
-			sourceTree = "<group>";
-		};
 		A8F774B52D38EA8C00AA6E93 /* GlobalImports */ = {
 			isa = PBXGroup;
 			children = (
@@ -592,31 +645,10 @@
 			path = Tool;
 			sourceTree = "<group>";
 		};
-		A8F774D32D38EA8C00AA6E93 /* UICollectionView+Component */ = {
-			isa = PBXGroup;
-			children = (
-				A8F774CF2D38EA8C00AA6E93 /* CollectionViewComponent.swift */,
-				A8F774D02D38EA8C00AA6E93 /* CommonSectionComponent.swift */,
-				A8F774D12D38EA8C00AA6E93 /* Component.swift */,
-				A8F774D22D38EA8C00AA6E93 /* UICollectionView+More.swift */,
-			);
-			path = "UICollectionView+Component";
-			sourceTree = "<group>";
-		};
-		A8F774D62D38EA8C00AA6E93 /* UITableView+TSItemModel */ = {
-			isa = PBXGroup;
-			children = (
-				A8F774D42D38EA8C00AA6E93 /* TSSimpleTableView.swift */,
-			);
-			path = "UITableView+TSItemModel";
-			sourceTree = "<group>";
-		};
 		A8F774D72D38EA8C00AA6E93 /* View */ = {
 			isa = PBXGroup;
 			children = (
 				A8F776292D3A70AA00AA6E93 /* UILabel */,
-				A8F774D32D38EA8C00AA6E93 /* UICollectionView+Component */,
-				A8F774D62D38EA8C00AA6E93 /* UITableView+TSItemModel */,
 			);
 			path = View;
 			sourceTree = "<group>";
@@ -624,9 +656,8 @@
 		A8F774D82D38EA8C00AA6E93 /* Common */ = {
 			isa = PBXGroup;
 			children = (
+				A89EA6AA2D5B3EE8000EB181 /* TSRealmManager */,
 				A80E73E32D533EB000C64288 /* Purchase */,
-				A8F7749B2D38EA8C00AA6E93 /* BaseClass */,
-				A8F774B32D38EA8C00AA6E93 /* Ex */,
 				A8F774B52D38EA8C00AA6E93 /* GlobalImports */,
 				A8F774B72D38EA8C00AA6E93 /* NetworkManager */,
 				A8F774C62D38EA8C00AA6E93 /* ThirdParty */,
@@ -703,6 +734,7 @@
 		A8F775332D38FC8E00AA6E93 /* VIewTool */ = {
 			isa = PBXGroup;
 			children = (
+				A89EA6B92D5DDE4E000EB181 /* TSPageNullView.swift */,
 				A8F7763B2D3B429A00AA6E93 /* TSCommonloadingView.swift */,
 				A8F776282D3A709200AA6E93 /* UILabel */,
 				A8F775342D38FC9A00AA6E93 /* TSViewTool.swift */,
@@ -716,6 +748,7 @@
 				A8F775372D390C3C00AA6E93 /* TSNetworkManager.swift */,
 				A8F7753C2D3918E200AA6E93 /* TSNetWork+Business.swift */,
 				A8F7753A2D3918D700AA6E93 /* TSNetworkManager+Loading.swift */,
+				A89EA67C2D59F1AC000EB181 /* StreamPostRequest.swift */,
 			);
 			path = TSNetWork;
 			sourceTree = "<group>";
@@ -834,6 +867,7 @@
 		A8F776402D3B75EA00AA6E93 /* General */ = {
 			isa = PBXGroup;
 			children = (
+				A89EA6BD2D5E03CD000EB181 /* Ex */,
 				A8F776412D3B75EF00AA6E93 /* TSBottomAlertVC.swift */,
 				A8F776432D3DE89900AA6E93 /* TSSmallIconBrowseVC */,
 			);
@@ -957,7 +991,7 @@
 			mainGroup = A8F774602D38E8B000AA6E93;
 			minimizedProjectReferenceProxies = 1;
 			packageReferences = (
-				A8F775512D3A038800AA6E93 /* XCLocalSwiftPackageReference "../SwiftUIX-master" */,
+				A89EA6492D59A588000EB181 /* XCRemoteSwiftPackageReference "MessageKit" */,
 			);
 			preferredProjectObjectVersion = 77;
 			productRefGroup = A8F7746A2D38E8B000AA6E93 /* Products */;
@@ -1043,82 +1077,68 @@
 			buildActionMask = 2147483647;
 			files = (
 				A8F775252D38ED8300AA6E93 /* TSConfig.swift in Sources */,
-				A8F774D92D38EA8C00AA6E93 /* UICollectionView+More.swift in Sources */,
 				A80E726C2D409E8300C64288 /* TSDiyTLPinkAnimalView.swift in Sources */,
 				A80E72652D409B0D00C64288 /* DiyStaticElement.swift in Sources */,
-				A8F774DA2D38EA8C00AA6E93 /* UIFont+Ex.swift in Sources */,
+				A89EA6B42D5C9D43000EB181 /* TSAIChatHistoryVM.swift in Sources */,
 				A80E72532D3F985E00C64288 /* TSWallpaperVC.swift in Sources */,
 				A8FB02B32D3E39A40031A396 /* TSEmojisModel.swift in Sources */,
-				A8F774DB2D38EA8C00AA6E93 /* AVAsset+Ex.swift in Sources */,
+				A89EA6542D59A9F4000EB181 /* TSTextLayoutSizeCalculator.swift in Sources */,
+				A89EA6552D59A9F4000EB181 /* MockMessage.swift in Sources */,
+				A89EA6562D59A9F4000EB181 /* TSChatUser.swift in Sources */,
+				A89EA6582D59A9F4000EB181 /* TSLayoutSizeCalculator.swift in Sources */,
+				A89EA6592D59A9F4000EB181 /* CustomMessageFlowLayout.swift in Sources */,
 				A80E72792D42285500C64288 /* TSBootPageVC.swift in Sources */,
 				A80E726A2D409E5400C64288 /* TSDiyTLYFlowersView.swift in Sources */,
 				A8F7764E2D3E00A800AA6E93 /* TSEmojisColViewModel.swift in Sources */,
+				A89EA6C82D6359ED000EB181 /* TSChatViewController+VipView.swift in Sources */,
 				A8F776422D3B75FC00AA6E93 /* TSBottomAlertVC.swift in Sources */,
-				A8F774DC2D38EA8C00AA6E93 /* NSString+Ex.swift in Sources */,
 				A8F775192D38EC6800AA6E93 /* TSEmojisVC.swift in Sources */,
 				A80E725C2D3FB09400C64288 /* TSKeyboardView.swift in Sources */,
+				A89EA6CC2D642CE2000EB181 /* TSChatViewController+NaviBar.swift in Sources */,
 				A8F7753B2D3918DE00AA6E93 /* TSNetworkManager+Loading.swift in Sources */,
-				A8F774DE2D38EA8C00AA6E93 /* UIDevice+Extension.swift in Sources */,
 				A80E721A2D3F393A00C64288 /* DiyStickerModel.swift in Sources */,
-				A80E724D2D3F515B00C64288 /* TSBaseViewModel.swift in Sources */,
 				A80E726F2D40DE2B00C64288 /* TSWallpaperPreviewVC.swift in Sources */,
 				A8F775492D3935D600AA6E93 /* TSBusinessWebVC.swift in Sources */,
 				A8F776392D3B38E600AA6E93 /* TSGenmojiGennerateVC.swift in Sources */,
 				A8F774E02D38EA8C00AA6E93 /* TSCommonTool.swift in Sources */,
+				A89EA6BF2D5E03D6000EB181 /* Notification+Ex.swift in Sources */,
 				A8F774E12D38EA8C00AA6E93 /* TSFileManagerTool.swift in Sources */,
-				A8F774E22D38EA8C00AA6E93 /* UICollectionView+Ex.swift in Sources */,
 				A80E73E42D533EB000C64288 /* TSPurchaseManager.swift in Sources */,
-				A8F774E32D38EA8C00AA6E93 /* TSBasicItemModel.swift in Sources */,
 				A8F7762F2D3A765400AA6E93 /* TSGenmojiViewModel.swift in Sources */,
 				A8F7751B2D38EC9800AA6E93 /* TSGenmojiVC.swift in Sources */,
-				A8F774E42D38EA8C00AA6E93 /* Component.swift in Sources */,
-				A8F774E52D38EA8C00AA6E93 /* Color+Ex.swift in Sources */,
+				A89EA6CF2D6430F3000EB181 /* TSChatViewController+Keyboard.swift in Sources */,
 				A8F7754E2D39E59100AA6E93 /* TSGenmojiModel.swift in Sources */,
 				A8F776452D3DE8A800AA6E93 /* TSSmallIconBrowseVC.swift in Sources */,
 				A80E72632D40925000C64288 /* TSDiyKeyboardVM.swift in Sources */,
-				A8F774E62D38EA8C00AA6E93 /* UIScreen.swift in Sources */,
 				A8F775432D39346400AA6E93 /* TSSetingModel.swift in Sources */,
-				A8F774E82D38EA8C00AA6E93 /* CollectionViewComponent.swift in Sources */,
-				A8F774E92D38EA8C00AA6E93 /* UILabel+Ex.swift in Sources */,
-				A8F774EB2D38EA8C00AA6E93 /* TSBaseNavigationC.swift in Sources */,
+				A89EA6BA2D5DDE5B000EB181 /* TSPageNullView.swift in Sources */,
 				A8F774EC2D38EA8C00AA6E93 /* TSToastTool.swift in Sources */,
+				A89EA6C12D5ED289000EB181 /* TSChatCellConfig.swift in Sources */,
 				A8F775502D39ECED00AA6E93 /* PhotoManager.swift in Sources */,
 				A8F7763F2D3B68E100AA6E93 /* TSGenmojiGennerateViewModel.swift in Sources */,
 				A80E72352D3F473400C64288 /* DiyPaperTemplateBaseView.swift in Sources */,
 				A80E72362D3F473400C64288 /* DiyPaperTemplate.swift in Sources */,
+				A89EA6B82D5D7EE9000EB181 /* TSAIChatHistoryModel.swift in Sources */,
 				A80E72722D40F86000C64288 /* TSLaunchVC.swift in Sources */,
 				A8F775352D38FC9A00AA6E93 /* TSViewTool.swift in Sources */,
 				A80E72592D3FA67800C64288 /* TSWallpaperViewModel.swift in Sources */,
-				A8F774ED2D38EA8C00AA6E93 /* Font+Ex.swift in Sources */,
-				A8F774EE2D38EA8C00AA6E93 /* UIViewController+Ex.swift in Sources */,
-				A8F774EF2D38EA8C00AA6E93 /* UIImage+Ex.swift in Sources */,
-				A8F774F02D38EA8C00AA6E93 /* TSBaseCollectionCell.swift in Sources */,
+				A89EA67D2D59F1AF000EB181 /* StreamPostRequest.swift in Sources */,
 				A80E722F2D3F3E1400C64288 /* TSDiyCanvasView.swift in Sources */,
-				A8F774F12D38EA8C00AA6E93 /* CGFloat+Ex.swift in Sources */,
-				A8F774F22D38EA8C00AA6E93 /* TSBaseView.swift in Sources */,
-				A8F774F42D38EA8C00AA6E93 /* TSBaseNavigationBarView.swift in Sources */,
-				A8F774F52D38EA8C00AA6E93 /* Array+Ex.swift in Sources */,
 				A80E72672D409C7D00C64288 /* Template+More.swift in Sources */,
+				A89EA6AC2D5B3EFB000EB181 /* TSRealmManager.swift in Sources */,
 				A8F775172D38EB7400AA6E93 /* TSTabBarController.swift in Sources */,
-				A8F774F62D38EA8C00AA6E93 /* UIImageView+Ex.swift in Sources */,
 				A8F776272D3A6EC200AA6E93 /* TSGenmojiTextView.swift in Sources */,
 				A80E73E12D533E5800C64288 /* TSPurchaseVC.swift in Sources */,
-				A8F774F82D38EA8C00AA6E93 /* TSSimpleTableView.swift in Sources */,
 				A8F776352D3A7C2B00AA6E93 /* TSGenmojiColSectionView.swift in Sources */,
-				A8F774F92D38EA8C00AA6E93 /* UIColor+Ex.swift in Sources */,
 				A80E724F2D3F6D7F00C64288 /* DiyFixedTextElement.swift in Sources */,
-				A8F774FA2D38EA8C00AA6E93 /* TSBaseModel.swift in Sources */,
+				A89EA6C42D5F40CC000EB181 /* TSChatInputBarVC.swift in Sources */,
 				A8F775452D39347100AA6E93 /* TSSetingViewModel.swift in Sources */,
 				A80E72202D3F3A8600C64288 /* DiyElementBaseView.swift in Sources */,
 				A8F776212D3A3F0200AA6E93 /* TSEmojisChildVC.swift in Sources */,
 				A80E72222D3F3A9200C64288 /* DiyStickerElement.swift in Sources */,
-				A8F774FE2D38EA8C00AA6E93 /* Int+Ex.swift in Sources */,
-				A8F774FF2D38EA8C00AA6E93 /* UserDefault+Ex.swift in Sources */,
 				A8F775002D38EA8C00AA6E93 /* WindowHelper.swift in Sources */,
 				A8F7764B2D3E008500AA6E93 /* TSEmojisChildColViewModel.swift in Sources */,
-				A8F775012D38EA8C00AA6E93 /* UIButton+Ex.swift in Sources */,
 				A80E72772D41EFF900C64288 /* TSEmojisTutorialsVC.swift in Sources */,
-				A8F775022D38EA8C00AA6E93 /* Dictionary+Ex.swift in Sources */,
 				A8F776482D3DE9F600AA6E93 /* TSSmallIconBrowseCell.swift in Sources */,
 				A8F7753D2D3918F800AA6E93 /* TSNetWork+Business.swift in Sources */,
 				A80E72262D3F3A9A00C64288 /* HYHAddImageView.m in Sources */,
@@ -1126,25 +1146,29 @@
 				A8FB02BA2D3E3BB20031A396 /* TSEmojisCoLItemCell.swift in Sources */,
 				A80E72562D3F98D700C64288 /* TSDiyKeyboardViewVC.swift in Sources */,
 				A8F775032D38EA8C00AA6E93 /* GlobalImports.swift in Sources */,
-				A8F775062D38EA8C00AA6E93 /* Date+Ex.swift in Sources */,
-				A8F775072D38EA8C00AA6E93 /* UIView+Ex.swift in Sources */,
-				A8F775082D38EA8C00AA6E93 /* UITableView+Ex.swift in Sources */,
+				A89EA67A2D59D25F000EB181 /* TSAIChatVM.swift in Sources */,
 				A8F7763C2D3B429B00AA6E93 /* TSCommonloadingView.swift in Sources */,
-				A8F775092D38EA8C00AA6E93 /* CommonSectionComponent.swift in Sources */,
 				A8F776322D3A771400AA6E93 /* TSGenmojiCollectionViewModel.swift in Sources */,
-				A80E72742D40FEA600C64288 /* UIApplication+Ex.swift in Sources */,
 				A80E721E2D3F3A7500C64288 /* DiyElement.swift in Sources */,
 				A8F776372D3A806E00AA6E93 /* TSGenmojiItemCell.swift in Sources */,
+				A89EA6692D59AA31000EB181 /* CameraInputBarAccessoryView.swift in Sources */,
+				A89EA66B2D59AA31000EB181 /* TSTextMessageContentCell.swift in Sources */,
+				A89EA66C2D59AA31000EB181 /* TSMessageContentCell.swift in Sources */,
+				A89EA66D2D59AA31000EB181 /* TableViewCells.swift in Sources */,
 				A8F7750A2D38EA8C00AA6E93 /* TSNetworkTool.swift in Sources */,
 				A8F7762D2D3A74A100AA6E93 /* TSGenmojiGennerateCell.swift in Sources */,
-				A8F775322D38FA5E00AA6E93 /* UITextView+Ex.swift in Sources */,
+				A89EA6BC2D5DFB12000EB181 /* TSViewController.swift in Sources */,
+				A89EA6832D59F4F9000EB181 /* TSChatViewController+ChatDelegate.swift in Sources */,
+				A89EA6A32D5B26E3000EB181 /* TSDBAIChatList.swift in Sources */,
 				A8F7753F2D39340E00AA6E93 /* TSSetingVC.swift in Sources */,
 				A8F7762B2D3A70B200AA6E93 /* PaddedLabel.swift in Sources */,
 				A80E73E62D5348D000C64288 /* SettingPurchaseTopView.swift in Sources */,
 				A80E72382D3F473B00C64288 /* DiyPaperProtocol.swift in Sources */,
 				A8F775382D390C3C00AA6E93 /* TSNetworkManager.swift in Sources */,
-				A8F7750B2D38EA8C00AA6E93 /* TSBaseTabViewCell.swift in Sources */,
-				A8F7750D2D38EA8C00AA6E93 /* TSBaseVC.swift in Sources */,
+				A89EA65F2D59AA11000EB181 /* TSChatViewController.swift in Sources */,
+				A89EA6C62D5F5C22000EB181 /* TSChatInputFullScreenVC.swift in Sources */,
+				A89EA6CA2D642C0A000EB181 /* TSChatViewController+SendMsg.swift in Sources */,
+				A89EA6B12D5C9D0C000EB181 /* TSAIChatHistoryVC.swift in Sources */,
 				A8FB02B72D3E3A3D0031A396 /* TSEmojisChildViewModel.swift in Sources */,
 				A8F7754B2D39376800AA6E93 /* TSSettingListView.swift in Sources */,
 				A8F7748B2D38E8B700AA6E93 /* AppDelegate.swift in Sources */,
@@ -1189,7 +1213,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.2;
+				MARKETING_VERSION = 1.4;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -1228,7 +1252,7 @@
 					"$(inherited)",
 					"@executable_path/Frameworks",
 				);
-				MARKETING_VERSION = 1.2;
+				MARKETING_VERSION = 1.4;
 				PRODUCT_BUNDLE_IDENTIFIER = com.girl.music.wallpaper;
 				PRODUCT_NAME = "$(TARGET_NAME)";
 				SUPPORTED_PLATFORMS = "iphoneos iphonesimulator";
@@ -1384,17 +1408,22 @@
 		};
 /* End XCConfigurationList section */
 
-/* Begin XCLocalSwiftPackageReference section */
-		A8F775512D3A038800AA6E93 /* XCLocalSwiftPackageReference "../SwiftUIX-master" */ = {
-			isa = XCLocalSwiftPackageReference;
-			relativePath = "../SwiftUIX-master";
+/* Begin XCRemoteSwiftPackageReference section */
+		A89EA6492D59A588000EB181 /* XCRemoteSwiftPackageReference "MessageKit" */ = {
+			isa = XCRemoteSwiftPackageReference;
+			repositoryURL = "https://github.com/MessageKit/MessageKit";
+			requirement = {
+				kind = upToNextMajorVersion;
+				minimumVersion = 5.0.0;
+			};
 		};
-/* End XCLocalSwiftPackageReference section */
+/* End XCRemoteSwiftPackageReference section */
 
 /* Begin XCSwiftPackageProductDependency section */
-		A8F775522D3A038800AA6E93 /* SwiftUIX */ = {
+		A89EA64A2D59A588000EB181 /* MessageKit */ = {
 			isa = XCSwiftPackageProductDependency;
-			productName = SwiftUIX;
+			package = A89EA6492D59A588000EB181 /* XCRemoteSwiftPackageReference "MessageKit" */;
+			productName = MessageKit;
 		};
 /* End XCSwiftPackageProductDependency section */
 	};

+ 24 - 0
AIEmoji.xcworkspace/xcshareddata/swiftpm/Package.resolved

@@ -0,0 +1,24 @@
+{
+  "originHash" : "31f65194aa647911f2c75f6e5a1350dbdbfaa36b7891d0134fbd39d509426e08",
+  "pins" : [
+    {
+      "identity" : "inputbaraccessoryview",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/nathantannar4/InputBarAccessoryView",
+      "state" : {
+        "revision" : "dfdc822b0d4a0d1b5777100347c399069e9bfc62",
+        "version" : "7.0.0"
+      }
+    },
+    {
+      "identity" : "messagekit",
+      "kind" : "remoteSourceControl",
+      "location" : "https://github.com/MessageKit/MessageKit",
+      "state" : {
+        "revision" : "dc48356f830d40de5acc0e126a44257d0a1329b6",
+        "version" : "5.0.0"
+      }
+    }
+  ],
+  "version" : 3
+}

+ 15 - 2
AIEmoji/AppDelegate.swift

@@ -6,7 +6,7 @@
 //
 
 import UIKit
-
+import SQLite3
 @main
 class AppDelegate: UIResponder, UIApplicationDelegate {
 
@@ -16,7 +16,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         window = UIWindow(frame: UIScreen.main.bounds)
         window?.backgroundColor = UIColor.white
         window?.makeKeyAndVisible()
-        
+        initPlatform()
         goToLoadVC()
         return true
     }
@@ -48,6 +48,19 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
         }
     }
     
+    
+    
+    func initPlatform() {
+        TSColorConfigShared.naviMianTextColor = .white
+    }
 
 }
 
+extension AppDelegate {
+    
+    func applicationWillTerminate(_ application: UIApplication) {
+        // 当应用即将被终止时,这里也可以添加数据保存逻辑,但系统留给的时间很有限
+        NotificationCenter.default.post(name: .kApplicationWillTerminate, object: nil)
+    }
+}
+

+ 6 - 0
AIEmoji/Assets.xcassets/AIChat/Contents.json

@@ -0,0 +1,6 @@
+{
+  "info" : {
+    "author" : "xcode",
+    "version" : 1
+  }
+}

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

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

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


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


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

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

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


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


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

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

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


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


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

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

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


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


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

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

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


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


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

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

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


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


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

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

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


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


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

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

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


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


+ 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/Common/pageNull.imageset/Contents.json

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

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


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


+ 22 - 0
AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/tabbar_select_aichat@2x.png


BIN
AIEmoji/Assets.xcassets/Tabbar/tabbar_select_aichat.imageset/tabbar_select_aichat@3x.png


+ 22 - 0
AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/Contents.json

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

BIN
AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/tabbar_unSelect_aichat@2x.png


BIN
AIEmoji/Assets.xcassets/Tabbar/tabbar_unSelect_aichat.imageset/tabbar_unSelect_aichat@3x.png


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


BIN
AIEmoji/Assets.xcassets/VIP/nav_vip.imageset/nav_vip@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


+ 297 - 0
AIEmoji/Business/AIChat/Data Generation/Lorem.swift

@@ -0,0 +1,297 @@
+// MIT License
+//
+// Copyright (c) 2017-2019 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 Foundation
+
+// MARK: - Lorem
+
+public class Lorem {
+  // MARK: Public
+
+  /// Return a random word.
+  ///
+  /// - returns: Returns a random word.
+  public class func word() -> String {
+    wordList.random()!
+  }
+
+  /// Return an array of `count` words.
+  ///
+  /// - parameter count: The number of words to return.
+  ///
+  /// - returns: Returns an array of `count` words.
+  public class func words(nbWords: Int = 3) -> [String] {
+    wordList.random(nbWords)
+  }
+
+  /// Return a string of `count` words.
+  ///
+  /// - parameter count: The number of words the string should contain.
+  ///
+  /// - returns: Returns a string of `count` words.
+  public class func words(nbWords: Int = 3) -> String {
+    words(nbWords: nbWords).joined(separator: " ")
+  }
+
+  /// Generate a sentence of `nbWords` words.
+  /// - parameter nbWords:  The number of words the sentence should contain.
+  /// - parameter variable: If `true`, the number of words will vary between
+  /// +/- 40% of `nbWords`.
+  /// - returns:
+  public class func sentence(nbWords: Int = 6, variable: Bool = true) -> String {
+    if nbWords <= 0 {
+      return ""
+    }
+
+    let result: String = words(nbWords: variable ? nbWords.randomize(variation: 40) : nbWords)
+
+    return result.firstCapitalized + "."
+  }
+
+  /// Generate an array of sentences.
+  /// - parameter nbSentences: The number of sentences to generate.
+  ///
+  /// - returns: Returns an array of random sentences.
+  public class func sentences(nbSentences: Int = 3) -> [String] {
+    (0 ..< nbSentences).map { _ in sentence() }
+  }
+
+  /// Generate a paragraph with `nbSentences` random sentences.
+  /// - parameter nbSentences: The number of sentences the paragraph should
+  /// contain.
+  /// - parameter variable:    If `true`, the number of sentences will vary
+  /// between +/- 40% of `nbSentences`.
+  /// - returns: Returns a paragraph with `nbSentences` random sentences.
+  public class func paragraph(nbSentences: Int = 3, variable: Bool = true) -> String {
+    if nbSentences <= 0 {
+      return ""
+    }
+
+    return sentences(nbSentences: variable ? nbSentences.randomize(variation: 40) : nbSentences).joined(separator: " ")
+  }
+
+  /// Generate an array of random paragraphs.
+  /// - parameter nbParagraphs: The number of paragraphs to generate.
+  /// - returns: Returns an array of `nbParagraphs` paragraphs.
+  public class func paragraphs(nbParagraphs: Int = 3) -> [String] {
+    (0 ..< nbParagraphs).map { _ in paragraph() }
+  }
+
+  /// Generate a string of random paragraphs.
+  /// - parameter nbParagraphs: The number of paragraphs to generate.
+  /// - returns: Returns a string of random paragraphs.
+  public class func paragraphs(nbParagraphs: Int = 3) -> String {
+    paragraphs(nbParagraphs: nbParagraphs).joined(separator: "\n\n")
+  }
+
+  /// Generate a string of at most `maxNbChars` characters.
+  /// - parameter maxNbChars: The maximum number of characters the string
+  /// should contain.
+  /// - returns: Returns a string of at most `maxNbChars` characters.
+  public class func text(maxNbChars: Int = 200) -> String {
+    var result: [String] = []
+
+    if maxNbChars < 5 {
+      return ""
+    } else if maxNbChars < 25 {
+      while result.count == 0 {
+        var size = 0
+
+        while size < maxNbChars {
+          let w = (size != 0 ? " " : "") + word()
+          result.append(w)
+          size += w.count
+        }
+
+        _ = result.popLast()
+      }
+    } else if maxNbChars < 100 {
+      while result.count == 0 {
+        var size = 0
+
+        while size < maxNbChars {
+          let s = (size != 0 ? " " : "") + sentence()
+          result.append(s)
+          size += s.count
+        }
+
+        _ = result.popLast()
+      }
+    } else {
+      while result.count == 0 {
+        var size = 0
+
+        while size < maxNbChars {
+          let p = (size != 0 ? "\n" : "") + paragraph()
+          result.append(p)
+          size += p.count
+        }
+
+        _ = result.popLast()
+      }
+    }
+
+    return result.joined(separator: "")
+  }
+
+  // MARK: Private
+
+  private static let wordList = [
+    "alias", "consequatur", "aut", "perferendis", "sit", "voluptatem",
+    "accusantium", "doloremque", "aperiam", "eaque", "ipsa", "quae", "ab",
+    "illo", "inventore", "veritatis", "et", "quasi", "architecto",
+    "beatae", "vitae", "dicta", "sunt", "explicabo", "aspernatur", "aut",
+    "odit", "aut", "fugit", "sed", "quia", "consequuntur", "magni",
+    "dolores", "eos", "qui", "ratione", "voluptatem", "sequi", "nesciunt",
+    "neque", "dolorem", "ipsum", "quia", "dolor", "sit", "amet",
+    "consectetur", "adipisci", "velit", "sed", "quia", "non", "numquam",
+    "eius", "modi", "tempora", "incidunt", "ut", "labore", "et", "dolore",
+    "magnam", "aliquam", "quaerat", "voluptatem", "ut", "enim", "ad",
+    "minima", "veniam", "quis", "nostrum", "exercitationem", "ullam",
+    "corporis", "nemo", "enim", "ipsam", "voluptatem", "quia", "voluptas",
+    "sit", "suscipit", "laboriosam", "nisi", "ut", "aliquid", "ex", "ea",
+    "commodi", "consequatur", "quis", "autem", "vel", "eum", "iure",
+    "reprehenderit", "qui", "in", "ea", "voluptate", "velit", "esse",
+    "quam", "nihil", "molestiae", "et", "iusto", "odio", "dignissimos",
+    "ducimus", "qui", "blanditiis", "praesentium", "laudantium", "totam",
+    "rem", "voluptatum", "deleniti", "atque", "corrupti", "quos",
+    "dolores", "et", "quas", "molestias", "excepturi", "sint",
+    "occaecati", "cupiditate", "non", "provident", "sed", "ut",
+    "perspiciatis", "unde", "omnis", "iste", "natus", "error",
+    "similique", "sunt", "in", "culpa", "qui", "officia", "deserunt",
+    "mollitia", "animi", "id", "est", "laborum", "et", "dolorum", "fuga",
+    "et", "harum", "quidem", "rerum", "facilis", "est", "et", "expedita",
+    "distinctio", "nam", "libero", "tempore", "cum", "soluta", "nobis",
+    "est", "eligendi", "optio", "cumque", "nihil", "impedit", "quo",
+    "porro", "quisquam", "est", "qui", "minus", "id", "quod", "maxime",
+    "placeat", "facere", "possimus", "omnis", "voluptas", "assumenda",
+    "est", "omnis", "dolor", "repellendus", "temporibus", "autem",
+    "quibusdam", "et", "aut", "consequatur", "vel", "illum", "qui",
+    "dolorem", "eum", "fugiat", "quo", "voluptas", "nulla", "pariatur",
+    "at", "vero", "eos", "et", "accusamus", "officiis", "debitis", "aut",
+    "rerum", "necessitatibus", "saepe", "eveniet", "ut", "et",
+    "voluptates", "repudiandae", "sint", "et", "molestiae", "non",
+    "recusandae", "itaque", "earum", "rerum", "hic", "tenetur", "a",
+    "sapiente", "delectus", "ut", "aut", "reiciendis", "voluptatibus",
+    "maiores", "doloribus", "asperiores", "repellat",
+  ]
+}
+
+extension String {
+  var firstCapitalized: String {
+    var string = self
+    string.replaceSubrange(string.startIndex ... string.startIndex, with: String(string[string.startIndex]).capitalized)
+    return string
+  }
+}
+
+extension Array {
+  /// Shuffle the array in-place using the Fisher-Yates algorithm.
+  public mutating func shuffle() {
+    if count == 0 {
+      return
+    }
+
+    for i in 0 ..< (count - 1) {
+      let j = Int(arc4random_uniform(UInt32(count - i))) + i
+      if j != i {
+        swapAt(i, j)
+      }
+    }
+  }
+
+  /// Return a shuffled version of the array using the Fisher-Yates
+  /// algorithm.
+  ///
+  /// - returns: Returns a shuffled version of the array.
+  public func shuffled() -> [Element] {
+    var list = self
+    list.shuffle()
+
+    return list
+  }
+
+  /// Return a random element from the array.
+  /// - returns: Returns a random element from the array or `nil` if the
+  /// array is empty.
+  public func random() -> Element? {
+    (count > 0) ? shuffled()[0] : nil
+  }
+
+  /// Return a random subset of `cnt` elements from the array.
+  /// - returns: Returns a random subset of `cnt` elements from the array.
+  public func random(_ count: Int = 1) -> [Element] {
+    let result = shuffled()
+
+    return (count > result.count) ? result : Array(result[0 ..< count])
+  }
+}
+
+extension Int {
+  /// Return a random number between `min` and `max`.
+  /// - note: The maximum value cannot be more than `UInt32.max - 1`
+  ///
+  /// - parameter min: The minimum value of the random value (defaults to `0`).
+  /// - parameter max: The maximum value of the random value (defaults to `UInt32.max - 1`)
+  ///
+  /// - returns: Returns a random value between `min` and `max`.
+  public static func random(min: Int = 0, max: Int = Int.max) -> Int {
+    precondition(min <= max, "attempt to call random() with min > max")
+
+    let diff = UInt(bitPattern: max &- min)
+    let result = UInt.random(min: 0, max: diff)
+
+    return min + Int(bitPattern: result)
+  }
+
+  public func randomize(variation: Int) -> Int {
+    let multiplier = Double(Int.random(min: 100 - variation, max: 100 + variation)) / 100
+    let randomized = Double(self) * multiplier
+
+    return Int(randomized) + 1
+  }
+}
+
+extension UInt {
+  fileprivate static func random(min: UInt, max: UInt) -> UInt {
+    precondition(min <= max, "attempt to call random() with min > max")
+
+    if min == UInt.min, max == UInt.max {
+      var result: UInt = 0
+      arc4random_buf(&result, MemoryLayout.size(ofValue: result))
+
+      return result
+    } else {
+      let range = max - min + 1
+      let limit = UInt.max - UInt.max % range
+      var result: UInt = 0
+
+      repeat {
+        arc4random_buf(&result, MemoryLayout.size(ofValue: result))
+      } while result >= limit
+
+      result = result % range
+
+      return min + result
+    }
+  }
+}

+ 17 - 0
AIEmoji/Business/AIChat/TSAIChatHistoryVC/M/TSAIChatHistoryModel.swift

@@ -0,0 +1,17 @@
+//
+//  TSAIChatHistoryModel.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/12.
+//
+
+
+class TSAIChatHistoryModel {
+    var title:String = ""
+    var chatList:[TSDBAIChatList] = []
+    
+    init(title: String, chatList: [TSDBAIChatList]) {
+        self.title = title
+        self.chatList = chatList
+    }
+}

+ 263 - 0
AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSAIChatHistoryVC.swift

@@ -0,0 +1,263 @@
+//
+//  TSAIChatHistoryVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/12.
+//
+
+import SwipeCellKit
+
+class TSAIChatHistoryVC: TSBaseVC {
+    
+    lazy var viewModel : TSAIChatHistoryVM = {
+        let viewModel = TSAIChatHistoryVM()
+        return viewModel
+    }()
+    
+    lazy var layout: UICollectionViewFlowLayout = {
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .vertical
+        layout.itemSize = CGSize(width: k_ScreenWidth-32, height: 74)
+        layout.minimumInteritemSpacing = 10.0
+        layout.minimumLineSpacing = 18.0
+        layout.headerReferenceSize = CGSizeMake(k_ScreenWidth, 48)
+        return layout
+    }()
+    
+    lazy var collectionView: UICollectionView = {
+        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = .clear
+        collectionView.register(TSAIChatHistoryCell.self, forCellWithReuseIdentifier: TSAIChatHistoryCell.cellID)
+        collectionView.register(TSAIChatHistorySectionHeaderView.self, forSupplementaryViewOfKind: UICollectionView.elementKindSectionHeader, withReuseIdentifier: TSAIChatHistorySectionHeaderView.reuseIdentifier)
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        return collectionView
+    }()
+    
+    lazy var pageNullView: TSPageNullView = {
+        let pageNullView = TSPageNullView()
+        pageNullView.isHidden = true
+        return pageNullView
+    }()
+    
+    var deleteAllBtn:UIButton = UIButton()
+    
+    override func createView() {
+        
+        addNormalNavBarView()
+        setPageTitle("History".localized)
+        
+        deleteAllBtn = setNavigationItem("", imageName: "delete_white", direction: .right, action: #selector(deleteAll))
+        
+        contentView.addSubview(pageNullView)
+        contentView.addSubview(collectionView)
+        collectionView.snp.makeConstraints { make in
+            make.edges.equalToSuperview()
+        }
+    }
+
+    override func dealThings() {
+        updateListView()
+    }
+    
+
+    func updateListView(){
+        pageNullView.isHidden = viewModel.historyModelChatList.count > 0 ? true : false
+        deleteAllBtn.isHidden = !pageNullView.isHidden
+        collectionView.reloadData()
+    }
+    
+    @objc func deleteAll(){
+        showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  { [weak self]  in
+            guard let self = self else { return }
+            viewModel.deleteAll()
+            self.viewModel.historyModelChatList.removeAll()
+            self.updateListView()
+        })
+    }
+}
+
+extension TSAIChatHistoryVC: UICollectionViewDataSource ,UICollectionViewDelegate {
+    
+    public func numberOfSections(in collectionView: UICollectionView) -> Int {
+        return viewModel.historyModelChatList.count
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        if let sectionModel = viewModel.historyModelChatList.safeObj(At: section) {
+            return sectionModel.chatList.count
+        }
+        return 0
+    }
+    
+    public func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: TSAIChatHistoryCell.cellID, for: indexPath)
+        
+        if let sectionModel = viewModel.historyModelChatList.safeObj(At: indexPath.section),
+            let itemModel = sectionModel.chatList.safeObj(At: indexPath.item),
+           let cell = cell as? TSAIChatHistoryCell
+        {
+            cell.delegate = self
+            cell.model = itemModel
+        }
+        
+        return cell
+    }
+
+    public func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
+        if let sectionModel = viewModel.historyModelChatList.safeObj(At: indexPath.section),
+           let itemModel = sectionModel.chatList.safeObj(At: indexPath.item)
+        {
+            
+            let chatVC = TSChatViewController()
+            chatVC.viewModel.uiStyle = .history
+            chatVC.viewModel.dbAIChatList = itemModel
+            chatVC.deleteBlock = { [weak self]  in
+                guard let self = self else { return }
+                //删除 UI 层的 cell
+                sectionModel.chatList.remove(at: indexPath.item)
+                if sectionModel.chatList.count == 0 {
+                    self.viewModel.historyModelChatList.remove(at: indexPath.section)
+                }
+                updateListView()
+                
+            }
+            self.navigationController?.pushViewController(chatVC, animated: true)
+        }
+    }
+    
+    
+    public func collectionView(_ collectionView: UICollectionView, viewForSupplementaryElementOfKind kind: String, at indexPath: IndexPath) -> UICollectionReusableView {
+        if let sectionModel = viewModel.historyModelChatList.safeObj(At: indexPath.section) {
+            if kind == UICollectionView.elementKindSectionHeader {
+                if let header = collectionView.dequeueReusableSupplementaryView(ofKind: kind, withReuseIdentifier: TSAIChatHistorySectionHeaderView.reuseIdentifier, for: indexPath) as? TSAIChatHistorySectionHeaderView {
+                    header.titleLabel.text = sectionModel.title
+                    return header
+                }
+            }
+        }
+        return TSAIChatHistorySectionHeaderView()
+    }
+}
+
+extension TSAIChatHistoryVC: SwipeCollectionViewCellDelegate {
+    
+    func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
+        guard orientation == .right else { return nil }
+
+        // 删除操作
+        let deleteAction = SwipeAction(style: .destructive, title: nil) {[weak self] action, indexPath in
+            guard let self = self else { return }
+            showCustomAlert(message: "Are you sure to delete".localized, deleteHandler:  {
+                if let sectionModel = self.viewModel.historyModelChatList.safeObj(At: indexPath.section),
+                let itemModel = sectionModel.chatList.safeObj(At: indexPath.item){
+                    sectionModel.chatList.remove(at: indexPath.item)
+                    itemModel.delete()
+                
+                    if sectionModel.chatList.count == 0 {
+                        self.viewModel.historyModelChatList.remove(at: indexPath.section)
+                    }
+                    
+                    self.updateListView()
+                }
+            })
+        }
+        
+        deleteAction.backgroundColor = "#E83E3E".uiColor
+        deleteAction.image = UIImage(named: "delete_white")
+        return [deleteAction]
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
+        var options = SwipeOptions()
+//        options.expansionStyle = .destructive(automaticallyDelete: false) // 完全滑动时是否自动触发操作
+        options.transitionStyle = .border // 滑动动画样式
+        return options
+    }
+    
+}
+class TSAIChatHistoryCell: SwipeCollectionViewCell {
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        creatUI()
+    }
+    
+    required public init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+
+    
+    static let cellID = "TSAIChatHistoryCell"
+    
+    lazy var titleLabel: UILabel = {
+        let titleLabel = UILabel.createLabel( font:.font(size: 16.0),textColor: .white)
+        return titleLabel
+    }()
+    
+    lazy var infoLabel: UILabel = {
+        let titleLabel = UILabel.createLabel( font:.font(size: 14.0),textColor: .white.withAlphaComponent(0.4))
+        return titleLabel
+    }()
+    
+    
+    var model:TSDBAIChatList?{
+        didSet{
+            if let dbMessage = model?.messages.last {
+                titleLabel.text = dbMessage.kindValue
+                infoLabel.text = dbMessage.sentDate.dateTimeString
+            }
+        }
+    }
+    
+    func creatUI() {
+
+        backgroundColor = "#333333".uiColor
+        cornerRadius = 16.0
+        
+        contentView.addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.top.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.height.equalTo(16)
+        }
+
+        contentView.addSubview(infoLabel)
+        infoLabel.snp.makeConstraints { make in
+            make.top.equalTo(44)
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.height.equalTo(14)
+        }
+    }
+}
+
+class TSAIChatHistorySectionHeaderView: UICollectionReusableView {
+    static let reuseIdentifier = "TSAIChatHistorySectionHeaderView"
+    
+    let titleLabel: UILabel = {
+        let label = UILabel.createLabel(text: "",font: .font(size: 16),textColor: .white)
+        return label
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        
+        addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.centerY.equalToSuperview()
+        }
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+}

+ 136 - 0
AIEmoji/Business/AIChat/TSAIChatHistoryVC/TSViewController.swift

@@ -0,0 +1,136 @@
+//
+//  TSViewController.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/13.
+//
+
+import UIKit
+import SwipeCellKit
+
+class ViewController: UIViewController, UICollectionViewDelegate, UICollectionViewDataSource, SwipeCollectionViewCellDelegate {
+    
+    // MARK: - UI Components
+    private var collectionView: UICollectionView!
+    
+    // MARK: - Data
+    private var data: [String] = [
+        "Cell 1", "Cell 2", "Cell 3", "Cell 4", "Cell 5",
+        "Cell 6", "Cell 7", "Cell 8", "Cell 9", "Cell 10"
+    ]
+    
+    // MARK: - Lifecycle
+    override func viewDidLoad() {
+        super.viewDidLoad()
+        setupUI()
+    }
+    
+    // MARK: - Setup UI
+    private func setupUI() {
+        view.backgroundColor = .white
+        title = "SwipeCellKit with UICollectionView"
+        
+        // 设置 UICollectionViewLayout
+        let layout = UICollectionViewFlowLayout()
+        layout.itemSize = CGSize(width: view.frame.width, height: 60) // 每个 Cell 的宽度为屏幕宽度
+        layout.minimumLineSpacing = 10 // 行间距
+        layout.scrollDirection = .vertical
+        
+        // 初始化 UICollectionView
+        collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.translatesAutoresizingMaskIntoConstraints = false
+        collectionView.backgroundColor = .white
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.register(CustomSwipeCell.self, forCellWithReuseIdentifier: "CustomSwipeCell")
+        
+        // 添加 UICollectionView 到视图
+        view.addSubview(collectionView)
+        
+        // 设置约束
+        NSLayoutConstraint.activate([
+            collectionView.topAnchor.constraint(equalTo: view.safeAreaLayoutGuide.topAnchor),
+            collectionView.leadingAnchor.constraint(equalTo: view.leadingAnchor),
+            collectionView.trailingAnchor.constraint(equalTo: view.trailingAnchor),
+            collectionView.bottomAnchor.constraint(equalTo: view.bottomAnchor)
+        ])
+    }
+    
+    // MARK: - UICollectionViewDataSource
+    func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
+        return data.count
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
+        let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "CustomSwipeCell", for: indexPath) as! CustomSwipeCell
+        cell.textLabel.text = data[indexPath.row]
+        cell.delegate = self // 设置 SwipeCollectionViewCellDelegate
+        return cell
+    }
+    
+    // MARK: - SwipeCollectionViewCellDelegate
+    func collectionView(_ collectionView: UICollectionView, editActionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> [SwipeAction]? {
+        guard orientation == .right else { return nil }
+        
+        // 删除操作
+        let deleteAction = SwipeAction(style: .destructive, title: "Delete") { action, indexPath in
+            print("Delete action for cell \(indexPath.row)")
+            self.data.remove(at: indexPath.row) // 删除数据源
+            self.collectionView.deleteItems(at: [indexPath]) // 删除 Cell
+        }
+        deleteAction.backgroundColor = .red
+        deleteAction.image = UIImage(systemName: "trash")
+        
+        // 更多操作
+        let moreAction = SwipeAction(style: .default, title: "More") { action, indexPath in
+            print("More action for cell \(indexPath.row)")
+        }
+        moreAction.backgroundColor = .blue
+        moreAction.image = UIImage(systemName: "ellipsis")
+        
+        return [deleteAction, moreAction]
+    }
+    
+    func collectionView(_ collectionView: UICollectionView, editActionsOptionsForItemAt indexPath: IndexPath, for orientation: SwipeActionsOrientation) -> SwipeOptions {
+        var options = SwipeOptions()
+        options.expansionStyle = .destructive(automaticallyDelete: false) // 完全滑动时是否自动触发操作
+        options.transitionStyle = .border // 滑动动画样式
+        return options
+    }
+}
+
+// MARK: - CustomSwipeCell
+class CustomSwipeCell: SwipeCollectionViewCell {
+    
+    // 文本标签
+    let textLabel: UILabel = {
+        let label = UILabel()
+        label.translatesAutoresizingMaskIntoConstraints = false
+        label.font = UIFont.systemFont(ofSize: 16, weight: .medium)
+        label.textColor = .black
+        return label
+    }()
+    
+    override init(frame: CGRect) {
+        super.init(frame: frame)
+        setupUI()
+    }
+    
+    required init?(coder: NSCoder) {
+        fatalError("init(coder:) has not been implemented")
+    }
+    
+    private func setupUI() {
+        backgroundColor = .lightGray
+        layer.cornerRadius = 8
+        clipsToBounds = true
+        // 添加文本标签
+        contentView.addSubview(textLabel)
+        
+        // 设置约束
+        NSLayoutConstraint.activate([
+            textLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 16),
+            textLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor)
+        ])
+    }
+}

+ 77 - 0
AIEmoji/Business/AIChat/TSAIChatHistoryVC/VM/TSAIChatHistoryVM.swift

@@ -0,0 +1,77 @@
+//
+//  TSAIChatHistoryVM.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/12.
+//
+
+class TSAIChatHistoryVM {
+    
+    lazy var historyDBChatList: [TSDBAIChatList] = {
+        let list = TSDBAIChatList.getAll()
+        return list
+    }()
+    
+    lazy var historyModelChatList: [TSAIChatHistoryModel] = {
+        return categorizeDates()
+    }()
+    
+    func categorizeDates() -> [TSAIChatHistoryModel] {
+        let calendar = Calendar.current
+        let now = Date()
+        let todayStart = calendar.startOfDay(for: now)
+        let yesterdayStart = calendar.date(byAdding: .day, value: -1, to: todayStart)!
+        let oneWeekAgo = calendar.date(byAdding: .day, value: -7, to: todayStart)!
+        let thirtyDaysAgo = calendar.date(byAdding: .day, value: -30, to: todayStart)!
+        
+        var todayDates: [TSDBAIChatList] = []
+        var yesterdayDates: [TSDBAIChatList] = []
+        var withinWeekDates: [TSDBAIChatList] = []
+        var within30DaysDates: [TSDBAIChatList] = []
+        
+        for model in historyDBChatList {
+            let date = Date(timeIntervalSince1970: TimeInterval(model.creatTimestampInt))
+            let dateStart = calendar.startOfDay(for: date)
+            if dateStart == todayStart {
+                todayDates.append(model)
+            } else if dateStart == yesterdayStart {
+                yesterdayDates.append(model)
+            } else if dateStart > oneWeekAgo {
+                withinWeekDates.append(model)
+            } else if dateStart > thirtyDaysAgo {
+                within30DaysDates.append(model)
+            }
+        }
+        
+        todayDates = todayDates.sorted { $0.creatTimestampInt > $1.creatTimestampInt}
+        yesterdayDates = yesterdayDates.sorted { $0.creatTimestampInt > $1.creatTimestampInt }
+        withinWeekDates = withinWeekDates.sorted { $0.creatTimestampInt > $1.creatTimestampInt }
+        within30DaysDates = within30DaysDates.sorted { $0.creatTimestampInt > $1.creatTimestampInt }
+        
+        var result = [TSAIChatHistoryModel]()
+        
+        if todayDates.count > 0 {
+            result.append(TSAIChatHistoryModel(title: "Today".localized, chatList: todayDates))
+        }
+        
+        if yesterdayDates.count > 0 {
+            result.append(TSAIChatHistoryModel(title: "1 Days".localized, chatList: yesterdayDates))
+        }
+        
+        if withinWeekDates.count > 0 {
+            result.append(TSAIChatHistoryModel(title: "7 Days".localized, chatList: withinWeekDates))
+        }
+        
+        if within30DaysDates.count > 0 {
+            result.append(TSAIChatHistoryModel(title: "30 Days".localized, chatList: within30DaysDates))
+        }
+        
+        return result
+    }
+
+    
+    func deleteAll() {
+        
+        TSDBAIChatList.deleteAll()
+    }
+}

+ 70 - 0
AIEmoji/Business/AIChat/TSChatViewController/Layout/CustomMessageFlowLayout.swift

@@ -0,0 +1,70 @@
+// MIT License
+//
+// Copyright (c) 2017-2019 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 Foundation
+import MessageKit
+import UIKit
+
+// MARK: - CustomMessagesFlowLayout
+open class CustomMessagesFlowLayout: MessagesCollectionViewFlowLayout {
+  lazy var customMessageSizeCalculator = TSTextLayoutSizeCalculator(layout: self)
+
+  open override func cellSizeCalculatorForItem(at indexPath: IndexPath) -> CellSizeCalculator {
+//    if isSectionReservedForTypingIndicator(indexPath.section) {
+//      return typingIndicatorSizeCalculator
+//    }
+//    let message = messagesDataSource.messageForItem(at: indexPath, in: messagesCollectionView)
+//    if case .custom = message.kind {
+      return customMessageSizeCalculator
+//    }
+//    return super.cellSizeCalculatorForItem(at: indexPath)
+  }
+
+  open override func messageSizeCalculators() -> [MessageSizeCalculator] {
+    var superCalculators = super.messageSizeCalculators()
+    // Append any of your custom `MessageSizeCalculator` if you wish for the convenience
+    // functions to work such as `setMessageIncoming...` or `setMessageOutgoing...`
+    superCalculators.append(customMessageSizeCalculator)
+    return superCalculators
+  }
+}
+
+// MARK: - CustomMessageSizeCalculator
+
+open class CustomMessageSizeCalculator: MessageSizeCalculator {
+  // MARK: Lifecycle
+
+  public override init(layout: MessagesCollectionViewFlowLayout? = nil) {
+    super.init()
+    self.layout = layout
+  }
+
+  // MARK: Open
+
+  open override func sizeForItem(at _: IndexPath) -> CGSize {
+    guard let layout = layout else { return .zero }
+    let collectionViewWidth = layout.collectionView?.bounds.width ?? 0
+    let contentInset = layout.collectionView?.contentInset ?? .zero
+    let inset = layout.sectionInset.left + layout.sectionInset.right + contentInset.left + contentInset.right
+    return CGSize(width: collectionViewWidth - inset, height: 44)
+  }
+}

+ 63 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/MockMessage.swift

@@ -0,0 +1,63 @@
+// MIT License
+//
+// Copyright (c) 2017-2019 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 Foundation
+import MessageKit
+import UIKit
+// MARK: - TSChatMessage
+internal class TSChatMessage: MessageType {
+  // MARK: Lifecycle
+
+    init(kind: MessageKind, user: TSChatUser, messageId: String, date: Date) {
+        self.kind = kind
+        self.user = user
+        self.messageId = messageId
+        sentDate = date
+    }
+
+    convenience init(custom: Any?, user: TSChatUser, messageId: String, date: Date) {
+        self.init(kind: .custom(custom), user: user, messageId: messageId, date: date)
+    }
+
+    convenience init(text: String, user: TSChatUser, messageId: String, date: Date) {
+        self.init(kind: .text(text), user: user, messageId: messageId, date: date)
+    }
+
+    convenience init(attributedText: NSAttributedString, user: TSChatUser, messageId: String, date: Date) {
+        self.init(kind: .attributedText(attributedText), user: user, messageId: messageId, date: date)
+    }
+
+    convenience init(linkItem: LinkItem, user: TSChatUser, messageId: String, date: Date) {
+        self.init(kind: .linkPreview(linkItem), user: user, messageId: messageId, date: date)
+    }
+
+    // MARK: Internal
+    var messageId: String
+    var sentDate: Date
+    var kind: MessageKind
+    var user: TSChatUser
+    var sender: SenderType {
+        user
+    }
+    var sendState: TSProgressState = .none
+}
+

+ 46 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatCellConfig.swift

@@ -0,0 +1,46 @@
+//
+//  TSChatCellConfig.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/13.
+//
+
+
+private let topSpaceH = 0.0
+private let bottomSpaceH = 0.0
+
+
+struct TSChatCellConfig {
+    
+
+    static var avatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################左边头像###################################
+    //左边头像的
+    static var leadingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 0)
+ 
+    //MARK: ###################################右边头像###################################
+    //左边头像的
+    static var traingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 0, bottom: 0, right: 16)
+    
+    
+    //MARK: ###################################消息容器###################################
+    //消息容器的 UIEdgeInsets
+    static var cellMessageContainerEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 8, bottom: 0, right: 16)
+    
+    static var cellMessageContainerMinSize:CGSize = CGSizeMake(48, 48)
+    
+//    static var cellMessageContainerMaxSize:CGSize{
+//        
+//        
+//        
+//        return CGSizeMake(40, 99999)
+//        
+//    }
+    
+    
+    
+    
+    //MARK: ###################################消息label###################################
+    static var cellMessageLabelEdge:UIEdgeInsets = UIEdgeInsets(top: 12, left: 12, bottom: 12, right: 12)
+    
+}

+ 34 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSChatUser.swift

@@ -0,0 +1,34 @@
+// MIT License
+//
+// Copyright (c) 2017-2019 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 Foundation
+import MessageKit
+struct TSChatUser: SenderType {
+    var senderId: String = ""
+    var displayName: String = ""
+    
+    init(senderId: String, displayName: String) {
+        self.senderId = senderId
+        self.displayName = displayName
+    }
+}
+

+ 208 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSDBAIChatList.swift

@@ -0,0 +1,208 @@
+//
+//  TSDBAIChatList.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/10.
+//
+
+import RealmSwift
+import MessageKit
+
+//MARK: TSDBAIChatList - 用于存储会话及消息列表
+class TSDBAIChatList: Object {
+    override static func primaryKey() -> String? {
+        return "sessionId"
+    }
+    @objc dynamic var sessionId = UUID().uuidString
+    dynamic var messages = List<TSDBChatMessage>()
+    @objc dynamic var creatTimestampInt:Int = 0
+    
+    //获取 App 层的 TSChatMessage 数组
+    func getMessageList() -> [TSChatMessage] {
+        var msgModel:[TSChatMessage] = []
+        for msgDBModel in messages {
+            msgModel.append(msgDBModel.getTSChatMessage())
+        }
+        return msgModel
+    }
+    
+//    //添加 App 层的 TSChatMessage 数组
+//    func updateMessageList(list:[TSChatMessage]){
+//        TSRMShared.writeThread {
+//            if creatTimestampInt == 0 {
+//                creatTimestampInt = Date.timestampInt
+//            }
+//            messages.removeAll()
+//            for model in list {
+//                messages.append(getTSDBChatMessage(chatMsg: model))
+//            }
+//        }
+//        TSRMShared.update(self)
+//    }
+    
+    func delete() {
+        TSRMShared.delete(self)
+    }
+    
+    static func deleteAll() {
+        do {
+            let realm = try Realm()
+            // 开始写入事务
+            try realm.write {
+                // 查询 Person 模型的所有对象
+                let allPersons = realm.objects(TSDBAIChatList.self)
+                // 删除所有查询到的对象
+                realm.delete(allPersons)
+            }
+            print("Person 模型的所有数据删除成功")
+        } catch {
+            print("删除 Person 模型数据时出错: \(error)")
+        }
+    }
+    
+    func updateMessage(msgModel:TSChatMessage){
+        TSRMShared.writeThread {
+            if creatTimestampInt == 0 {
+                creatTimestampInt = Date.timestampInt
+            }
+            let dbModel = getTSDBChatMessage(chatMsg: msgModel)
+            if let index = messages.firstIndex(where: { $0.messageId == dbModel.messageId }) {
+                messages[index] = dbModel// 如果找到,替换该元素
+            } else {
+                messages.append(dbModel)// 如果没有找到,添加到末尾
+            }
+        }
+        TSRMShared.update(self)
+    }
+    
+    
+    func updateMessages(msgModels:[TSChatMessage]){
+        TSRMShared.writeThread {
+            if creatTimestampInt == 0 {
+                creatTimestampInt = Date.timestampInt
+            }
+            for msgModel in msgModels {
+                let dbModel = getTSDBChatMessage(chatMsg: msgModel)
+                if let index = messages.firstIndex(where: { $0.messageId == dbModel.messageId }) {
+                    messages[index] = dbModel// 如果找到,替换该元素
+                } else {
+                    messages.append(dbModel)// 如果没有找到,添加到末尾
+                }
+            }
+        }
+        TSRMShared.update(self)
+    }
+    
+}
+
+
+extension TSDBAIChatList {
+    
+    static func getOneDB() -> TSDBAIChatList{
+        let dbModel = TSDBAIChatList()
+        return dbModel
+    }
+    
+    //获取所有储存的 TSDBAIChatList
+    static func getAll() -> [TSDBAIChatList] {
+        var list:[TSDBAIChatList] = []
+        let allPersons = TSRMShared.read(TSDBAIChatList.self)
+        for person in allPersons {
+            list.append(person)
+        }
+        return list
+    }
+}
+
+extension TSDBAIChatList {
+    
+    func getTSDBChatMessage(chatMsg:TSChatMessage) -> TSDBChatMessage {
+        let keyValue = chatMsg.kind.keyValue
+        
+        let dbModel = TSDBChatMessage()
+        dbModel.user = TSDBChatUser(senderId: chatMsg.user.senderId, displayName: chatMsg.user.displayName)
+        dbModel.messageId = chatMsg.messageId
+        dbModel.sentDate = chatMsg.sentDate
+        dbModel.kindKey = keyValue.0
+        dbModel.kindValue = keyValue.1
+        return dbModel
+    }
+    
+}
+
+
+//MARK: extension
+extension MessageKind {
+
+    static func fromKeyValue(key string: String,value:String) -> MessageKind {
+        if string == "text" {
+            return .text(value)
+        } else if string == "attributedText" {
+            return .attributedText(kMDAttributedString(text: value))
+        } else {
+            return .custom(nil)
+        }
+    }
+    
+    var keyValue:(String,String){
+        switch self {
+        case .text(let text):
+            return ("text",text)
+        case .attributedText(let attributedString):
+            return ("attributedText",attributedString.string)
+        default:
+            return ("default","")
+        }
+    }
+    
+    static func textKind(_ string: String) -> MessageKind {
+        return .text(string)
+    }
+    
+}
+
+
+// TSChatMessage - 用于存储聊天消息
+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 kindKey: String = ""
+    @objc dynamic var kindValue: 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)
+        return model
+    }
+    
+    override init() {
+        super.init()
+    }
+    
+}
+
+class TSDBChatUser: Object {
+    @objc dynamic var senderId: String = ""
+    @objc dynamic var displayName: String = ""
+    
+    func getTSChatUser() -> TSChatUser {
+        let user = TSChatUser(senderId: senderId, displayName: displayName)
+        return user
+    }
+    
+    init(senderId: String, displayName: String) {
+        self.senderId = senderId
+        self.displayName = displayName
+    }
+    
+    override init() {
+        super.init()
+    }
+}
+
+
+

+ 76 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSLayoutSizeCalculator.swift

@@ -0,0 +1,76 @@
+//
+//  CustomLayoutSizeCalculator.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSLayoutSizeCalculator: MessageSizeCalculator {//CellSizeCalculator {
+    
+//    override init(layout: MessagesCollectionViewFlowLayout? = nil) {
+//        super.init()
+//        self.layout = layout
+//    }
+
+//    var messagesLayout: MessagesCollectionViewFlowLayout {
+//        layout as! MessagesCollectionViewFlowLayout
+//    }
+    
+    static let topSpaceH = 0.0
+    static let bottomSpaceH = 0.0
+    
+
+    static var avatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################左边头像###################################
+    static var leadingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 0)
+    static var leadingAvatarSize = CGSizeMake(40, 40)
+    //MARK: ###################################右边头像###################################
+    static var traingAvatarEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 0, bottom: 0, right: 0)
+    static var traingAvatarSize = CGSizeMake(0, 0)
+    
+    //MARK: ###################################消息容器###################################
+    //消息容器的 UIEdgeInsets
+    static var cellMessageContainerLeadingEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 8, bottom: 0, right: 16)
+    static var cellMessageContainerTraingEdge:UIEdgeInsets = UIEdgeInsets(top: topSpaceH, left: 16, bottom: 0, right: 16)
+    static var cellMessageContainerMinSize:CGSize = CGSizeMake(48, 48)
+    
+    //消息容器最大宽度
+    lazy var messageContainerLeadingMaxWidth: CGFloat = {
+        //左头像
+        var cellMessageContainerMaxWidth = messagesLayout.itemWidth - Self.leadingAvatarEdge.left - Self.leadingAvatarSize.width
+        //容器 填充
+        cellMessageContainerMaxWidth = cellMessageContainerMaxWidth - Self.cellMessageContainerLeadingEdge.left - Self.cellMessageContainerLeadingEdge.right
+        return cellMessageContainerMaxWidth
+    }()
+
+    lazy var messageContainerTraingMaxWidth: CGFloat = {
+        //右头像
+        var cellMessageContainerMaxWidth = messagesLayout.itemWidth - Self.traingAvatarEdge.right - Self.traingAvatarSize.width
+        //容器 填充
+        cellMessageContainerMaxWidth = cellMessageContainerMaxWidth - Self.cellMessageContainerTraingEdge.left - Self.cellMessageContainerTraingEdge.right
+        return cellMessageContainerMaxWidth
+    }()
+
+
+    var messagesDataSource: MessagesDataSource {
+        self.messagesLayout.messagesDataSource
+    }
+    
+    //cell itemSize
+    override func sizeForItem(at indexPath: IndexPath) -> CGSize {
+        let dataSource = messagesDataSource
+        let message = dataSource.messageForItem(at: indexPath,in: messagesLayout.messagesCollectionView)
+        let itemHeight = cellContentHeight(for: message,at: indexPath,fromCurrentSender:dataSource.isFromCurrentSender(message: message))
+        return CGSize(width: messagesLayout.itemWidth,height: itemHeight)
+    }
+    
+    //消息容器的高度
+    func cellContentHeight(for message: MessageType,at indexPath: IndexPath,fromCurrentSender: Bool)-> CGFloat {
+        return Self.cellMessageContainerMinSize.height
+    }
+    
+}

+ 75 - 0
AIEmoji/Business/AIChat/TSChatViewController/Models/TSTextLayoutSizeCalculator.swift

@@ -0,0 +1,75 @@
+//
+//  CustomTextMessageSizeCalculator.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 30/04/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSTextLayoutSizeCalculator: TSLayoutSizeCalculator {
+    
+    
+    //MARK: ###################################消息label###################################
+    //消息容器的 UIEdgeInsets
+    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
+    }
+    
+    var cellMessagelabelTraingMaxWidth:CGFloat{
+        messageContainerTraingMaxWidth - Self.cellMessagelabelEdge.left - Self.cellMessagelabelEdge.right
+    }
+    
+    //消息容器的高度
+    override func cellContentHeight(for message: MessageType,at indexPath: IndexPath, fromCurrentSender: Bool)-> CGFloat {
+        let superH = super.cellContentHeight(for: message, at: indexPath, fromCurrentSender: fromCurrentSender)
+        let size = messageLabelSize(for: message, at: indexPath, fromCurrentSender: fromCurrentSender)
+        var height = size.height + Self.cellMessagelabelEdge.top + Self.cellMessagelabelEdge.bottom
+
+        if height < superH {
+            height = superH
+        }
+
+        return height
+    }
+    
+    func messageLabelSize(for message: MessageType,at _: IndexPath, fromCurrentSender: Bool) -> CGSize{
+        let attributedText: NSAttributedString
+//        var sendText = ""
+        let textMessageKind = message.kind
+        switch textMessageKind {
+        case .attributedText(let text):
+            attributedText = text
+//            sendText = text.string
+        case .text(let text), .emoji(let text):
+            attributedText = NSAttributedString(string: text, attributes: [.font: Self.messageLabelFont])
+//            sendText = text
+        default:
+            fatalError("messageLabelSize received unhandled MessageDataType: \(message.kind)")
+        }
+        let maxWidth = fromCurrentSender ? cellMessagelabelTraingMaxWidth: cellMessagelabelLeadingMaxWidth
+//        let textSize = sendText.height(ofFont: Self.messageLabelFont, maxWidth: maxWidth)
+
+        //用 label 计算更加精准
+        var size = UILabel.getAttributedTextSize(attributedText: attributedText, maxWidth: maxWidth)
+
+//        var size = attributedText.size(consideringWidth: maxWidth)
+//        size.width = size.width + 10
+
+        let minSize = Self.cellMessagelabelMinSize
+        if size.width < minSize.width {
+            size.width = minSize.width
+        }
+        if size.height < minSize.height {
+            size.height = minSize.height
+        }
+        return size
+    }
+}

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

@@ -0,0 +1,202 @@
+//
+//  TSChatInputBarVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/14.
+//
+
+import UIKit
+
+class TSChatInputBarVC: TSBaseVC, UITextViewDelegate {
+    
+    var sendComplete:(([Any])->Void)?
+    
+    lazy var InputBarView:UIView = {
+        let InputBarView = UIView()
+        InputBarView.backgroundColor = "#222222".uiColor
+        InputBarView.cornerRadius = 28.0
+        return InputBarView
+    }()
+    
+    lazy var sendBtn: UIButton = {
+        let sendBtn = UIButton.createButton(image: UIImage(named: "chat_send")) { [weak self]  in
+            guard let self = self else { return }
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            emptyInput()
+        }
+        sendBtn.isEnabled = false
+        return sendBtn
+    }()
+    
+    lazy var magnifyBtn: UIButton = {
+        let magnifyBtn = UIButton.createButton(image: UIImage(named: "chat_send_magnify")) { [weak self]  in
+            guard let self = self else { return }
+            let vc = TSChatInputFullScreenVC()
+            vc.text = textView.text
+            vc.sendComplete = { [weak self] date in
+                guard let self = self else { return }
+                sendComplete?(date)
+                emptyInput()
+            }
+            
+            vc.closeComplete = { [weak self] text in
+                guard let self = self else { return }
+                textView.text = text
+                textDidChange()
+                scrollTextViewToBottom()
+            }
+            
+            kPresentModalVC(target: self, modelVC: vc)
+        }
+        magnifyBtn.isHidden = true
+        return magnifyBtn
+    }()
+    
+    private let minHeight: CGFloat = 56//24
+    private let maxHeight: CGFloat = 154
+    lazy var textView: TSCustomTextView = {
+        let textView = TSCustomTextView()
+        textView.backgroundColor = .clear
+        textView.textColor = .white
+        textView.delegate = self
+        textView.font = .font(size: 16)
+        textView.clipsToBounds = true
+        textView.isScrollEnabled = false
+        textView.tintColor = .themeColor
+        textView.returnKeyType = .send
+        textView.placeholder = "Type all necessary details".localized
+        textView.placeholderColor = .white.withAlphaComponent(0.4)
+        textView.placeholderLabel.font = .font(size: 16.0)
+        textView.textInsets = UIEdgeInsets(top: 18, left: 16, bottom: 14, right: 0)
+        return textView
+    }()
+
+  
+    override func createView() {
+        setNavBarViewHidden(true)
+        
+        view.backgroundColor = .clear
+        
+        contentView.addSubview(InputBarView)
+        InputBarView.addSubview(textView)
+        InputBarView.addSubview(magnifyBtn)
+        InputBarView.addSubview(sendBtn)
+        
+        InputBarView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.top.equalTo(12)
+            make.bottom.equalTo(-12)
+        }
+        
+        textView.snp.makeConstraints { make in
+            make.leading.equalTo(0)
+            make.trailing.equalTo(-60)
+            make.top.equalTo(0)
+            make.bottom.equalTo(0)
+            make.height.equalTo(minHeight)
+        }
+        
+        sendBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.bottom.equalTo(-16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+        
+        magnifyBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.top.equalTo(16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+
+    }
+    
+    
+    override func dealThings() {
+        // 监听文本变化事件
+        NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: textView)
+    }
+
+    
+    func sendEnabled(enabled:Bool){
+    
+        if enabled == true ,textView.text.replacingOccurrences(of: " ", with: "").count > 0 {
+            sendBtn.isEnabled = false
+        }else{
+            sendBtn.isEnabled = false
+        }
+    }
+
+    
+    func emptyInput() {
+        textView.text = ""
+        textDidChange()
+        magnifyBtn.isHidden = true
+    }
+    
+    deinit {
+        // 移除通知监听
+        NotificationCenter.default.removeObserver(self, name: UITextView.textDidChangeNotification, object: textView)
+    }
+}
+
+
+extension TSChatInputBarVC {
+
+    @objc private func textDidChange() {
+        // 计算输入框的内容高度
+        let sizeToFit = CGSize(width: textView.bounds.width, height: .greatestFiniteMagnitude)
+        let estimatedSize = textView.sizeThatFits(sizeToFit)
+
+        // 根据内容高度调整输入框的高度
+        var newHeight = estimatedSize.height
+        if newHeight <= minHeight {
+            newHeight = minHeight
+            textView.isScrollEnabled = false
+            magnifyBtn.isHidden = true
+        } else if newHeight > maxHeight {
+            newHeight = maxHeight
+            textView.isScrollEnabled = true
+            magnifyBtn.isHidden = false
+        } else {
+            textView.isScrollEnabled = false
+            magnifyBtn.isHidden = true
+        }
+         
+        
+        sendEnabled(enabled: true)
+
+        // 更新输入框的高度
+        textView.snp.updateConstraints { make in
+            make.height.equalTo(newHeight)
+        }
+    }
+
+    private func scrollTextViewToBottom() {
+        let bottomOffset = CGPoint(x: 0, y: textView.contentSize.height - textView.bounds.size.height)
+        textView.setContentOffset(bottomOffset, animated: true)
+    }
+    
+}
+
+
+extension TSChatInputBarVC {
+    
+    // 实现 UITextViewDelegate 协议方法,控制 return 键行为
+    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+        if text == "\n" {
+            // 当输入为换行符(即按下 return 键)时,执行相应操作
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            emptyInput()
+            textView.resignFirstResponder()
+            return false
+        }
+        return true
+    }
+}

+ 190 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatInputBarVC/TSChatInputFullScreenVC.swift

@@ -0,0 +1,190 @@
+//
+//  TSChatInputFullScreenVC.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/14.
+//
+
+import UIKit
+
+class TSChatInputFullScreenVC: TSBaseVC, UITextViewDelegate {
+    
+    var sendComplete:(([Any])->Void)?
+    var closeComplete:((String)->Void)?
+    var text:String?
+    
+    lazy var InputBarView:UIView = {
+        let InputBarView = UIView()
+        InputBarView.backgroundColor = "#222222".uiColor
+        InputBarView.cornerRadius = 28.0
+        return InputBarView
+    }()
+    
+    lazy var sendBtn: UIButton = {
+        let sendBtn = UIButton.createButton(image: UIImage(named: "chat_send")) { [weak self]  in
+            guard let self = self else { return }
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            textView.text = ""
+            dismiss(animated: true)
+        }
+        return sendBtn
+    }()
+    
+    lazy var minificationBtn: UIButton = {
+        let minificationBtn = UIButton.createButton(image: UIImage(named: "chat_send_minify")) { [weak self]  in
+            guard let self = self else { return }
+            closeComplete?(textView.text)
+            self.dismiss(animated: true)
+        }
+        return minificationBtn
+    }()
+    
+    private let minHeight: CGFloat = 24
+    private let maxHeight: CGFloat = 154
+    lazy var textView: TSCustomTextView = {
+        let textView = TSCustomTextView()
+        textView.backgroundColor = .clear
+        textView.textColor = .white
+        textView.delegate = self
+        textView.font = .font(size: 16)
+        textView.clipsToBounds = true
+        textView.isScrollEnabled = false
+        textView.tintColor = .themeColor
+        textView.returnKeyType = .send
+        textView.placeholder = "Type all necessary details".localized
+        textView.placeholderColor = .white.withAlphaComponent(0.4)
+        textView.placeholderLabel.font = .font(size: 16.0)
+        textView.textInsets = UIEdgeInsets(top: 5, left: 0, bottom: 5, right: 0)
+        return textView
+    }()
+  
+    override func createView() {
+        setNavBarViewHidden(true)
+        edgesForExtendedLayout = []
+        
+        contentView.addSubview(InputBarView)
+        InputBarView.addSubview(textView)
+        InputBarView.addSubview(minificationBtn)
+        InputBarView.addSubview(sendBtn)
+        
+        InputBarView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.top.equalTo(12+k_Height_NavBar)
+            make.bottom.equalTo(-12-k_Height_safeAreaInsetsBottom())
+        }
+        
+        textView.snp.makeConstraints { make in
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-60)
+            make.top.equalTo(16)
+            make.bottom.equalTo(-16)
+            make.height.equalTo(minHeight)
+        }
+        
+        sendBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.bottom.equalTo(-16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+        
+        minificationBtn.snp.makeConstraints { make in
+            make.trailing.equalTo(-16)
+            make.top.equalTo(16)
+            make.width.equalTo(24)
+            make.height.equalTo(24)
+        }
+    }
+    
+
+    override func dealThings() {
+        textView.text = text
+        
+        let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickView))
+        tapGesture.cancelsTouchesInView = false
+        InputBarView.addGestureRecognizer(tapGesture)
+        
+        // 监听文本变化事件
+        NotificationCenter.default.addObserver(self, selector: #selector(textDidChange), name: UITextView.textDidChangeNotification, object: textView)
+
+        // 监听键盘事件
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
+        NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
+    }
+
+    @objc func clickView(){
+        view.endEditing(true)
+    }
+    
+    @objc private func textDidChange() {
+        sendEnabled(enabled: textView.text.count > 0)
+    }
+
+    func sendEnabled(enabled:Bool){
+        sendBtn.isEnabled = enabled
+    }
+    
+    @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.textView.contentInset = contentInset
+            self.textView.scrollIndicatorInsets = contentInset
+        }
+        
+        UIView.animate(withDuration: animationDuration+0.8) {
+            self.sendBtn.snp.updateConstraints { make in
+                make.bottom.equalTo(-keyboardHeight + k_Height_safeAreaInsetsBottom())
+            }
+        }
+    }
+
+    @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.textView.contentInset = contentInset
+            self.textView.scrollIndicatorInsets = contentInset
+            
+            self.sendBtn.snp.updateConstraints { make in
+                make.bottom.equalTo(-16)
+            }
+        }
+    }
+    
+    deinit {
+        // 移除通知监听
+        NotificationCenter.default.removeObserver(self, name: UITextView.textDidChangeNotification, object: textView)
+    }
+}
+
+extension TSChatInputFullScreenVC {
+    
+    // 实现 UITextViewDelegate 协议方法,控制 return 键行为
+    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+        if text == "\n" {
+            // 当输入为换行符(即按下 return 键)时,执行相应操作
+            if let string = textView.text {
+                sendComplete?([string])
+            }
+            dismiss(animated: true)
+            textView.text = ""
+            textView.resignFirstResponder()
+            return false
+        }
+        return true
+    }
+}

+ 304 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+ChatDelegate.swift

@@ -0,0 +1,304 @@
+//
+//  TSChatViewController+Ex.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/10.
+//
+import InputBarAccessoryView
+import MessageKit
+import UIKit
+import MapKit
+
+
+
+
+
+
+// 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: MessagesLayoutDelegate
+
+extension TSChatViewController: MessagesLayoutDelegate {
+    func customCellSizeCalculator(for message: any MessageType, at indexPath: IndexPath, in messagesCollectionView: MessagesCollectionView) -> CellSizeCalculator {
+        self.textMessageSizeCalculator
+    }
+}
+
+
+// MARK: MessagesDisplayDelegate
+
+extension TSChatViewController: MessagesDisplayDelegate {
+    // MARK: - Text Messages
+    
+    func textColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        return UIColor.mainBg
+        //      isFromCurrentSender(message: message) ? UIColor.mainBg : .white
+    }
+    
+    func detectorAttributes(for detector: DetectorType, and _: MessageType, at _: IndexPath) -> [NSAttributedString.Key: Any] {
+        switch detector {
+        case .hashtag, .mention: return [.foregroundColor: UIColor.blue]
+        default: return MessageLabel.defaultAttributes
+        }
+    }
+    
+    func enabledDetectors(for _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> [DetectorType] {
+        [.url, .address, .phoneNumber, .date, .transitInformation, .mention, .hashtag]
+    }
+    
+    // MARK: - All Messages
+    
+    func backgroundColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        isFromCurrentSender(message: message) ? .themeColor : "#333333".uiColor
+    }
+    
+    func messageStyle(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MessageStyle {
+        let tail: MessageStyle.TailCorner = isFromCurrentSender(message: message) ? .bottomRight : .bottomLeft
+        if let image = UIImage(named: "bobbly") {
+            return .customImageTail(image, tail)
+        } else {
+            return .bubbleTail(tail, .curved)
+        }
+    }
+    
+    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")))
+    }
+    
+    func configureMediaMessageImageView(
+        _ imageView: UIImageView,
+        for message: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView)
+    {
+        if case MessageKind.photo(let media) = message.kind, let imageURL = media.url {
+            imageView.kf.setImage(with: imageURL)
+        } else {
+            imageView.kf.cancelDownloadTask()
+        }
+    }
+    
+    // MARK: - Location Messages
+    
+    func annotationViewForLocation(message _: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> MKAnnotationView? {
+        let annotationView = MKAnnotationView(annotation: nil, reuseIdentifier: nil)
+        let pinImage = #imageLiteral(resourceName: "ic_map_marker")
+        annotationView.image = pinImage
+        annotationView.centerOffset = CGPoint(x: 0, y: -pinImage.size.height / 2)
+        return annotationView
+    }
+    
+    func animationBlockForLocation(
+        message _: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView) -> ((UIImageView) -> Void)?
+    {
+        { view in
+            view.layer.transform = CATransform3DMakeScale(2, 2, 2)
+            UIView.animate(
+                withDuration: 0.6,
+                delay: 0,
+                usingSpringWithDamping: 0.9,
+                initialSpringVelocity: 0,
+                options: [],
+                animations: {
+                    view.layer.transform = CATransform3DIdentity
+                },
+                completion: nil)
+        }
+    }
+    
+    func snapshotOptionsForLocation(
+        message _: MessageType,
+        at _: IndexPath,
+        in _: MessagesCollectionView)
+    -> LocationMessageSnapshotOptions
+    {
+        LocationMessageSnapshotOptions(
+            showsBuildings: true,
+            showsPointsOfInterest: true,
+            span: MKCoordinateSpan(latitudeDelta: 10, longitudeDelta: 10))
+    }
+    
+    // MARK: - Audio Messages
+    
+    func audioTintColor(for message: MessageType, at _: IndexPath, in _: MessagesCollectionView) -> UIColor {
+        isFromCurrentSender(message: message) ? .white : UIColor(red: 15 / 255, green: 135 / 255, blue: 255 / 255, alpha: 1.0)
+    }
+    
+    func configureAudioCell(_ cell: AudioMessageCell, message: MessageType) {
+        
+    }
+}
+
+
+// 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 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)")
+    }
+}

+ 150 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+Keyboard.swift

@@ -0,0 +1,150 @@
+//
+//  Keyboard.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/17.
+//
+
+extension TSChatViewController {
+    
+    var creatInputBarVC: TSChatInputBarVC{
+        let inputBarVC = TSChatInputBarVC()
+        inputBarVC.sendComplete = { [weak self] components in
+            guard let self = self else { return }
+            inputSendMsg(components)
+        }
+        return inputBarVC
+    }
+    
+    
+    var creatInputBarBgView:UIView{
+        let inputBarBgView = UIView()
+        inputBarBgView.addShadow(shadowColor: "#111111".uiColor.cgColor, shadowOffset: CGSize(width: 0, height: -10), shadowRadius: 10, shadowOpacity: 1.0)
+        return inputBarBgView
+    }
+    
+    var creatScrollToBottomButton: 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
+    }
+    
+}
+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
+        }
+        
+
+    }
+    
+    
+}
+
+
+
+extension TSChatViewController{
+
+    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)
+        
+        handleKeyBoard()
+        configureScrollToBottomButton()
+    }
+    
+    
+    func handleKeyBoard(){
+        if viewModel.uiStyle == .chat {
+            let tapGesture = UITapGestureRecognizer(target: self, action: #selector(clickView))
+            tapGesture.cancelsTouchesInView = false
+            messagesCollectionView.addGestureRecognizer(tapGesture)
+            
+            // 监听键盘事件
+            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillShow(_:)), name: UIResponder.keyboardWillShowNotification, object: nil)
+            NotificationCenter.default.addObserver(self, selector: #selector(keyboardWillHide(_:)), name: UIResponder.keyboardWillHideNotification, object: nil)
+        }
+    }
+    
+    
+    func configureScrollToBottomButton() {
+        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)
+        
+
+    }
+    
+    @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
+        }
+    }
+    
+}

+ 158 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+NaviBar.swift

@@ -0,0 +1,158 @@
+//
+//  TSChatViewController+Nav.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/17.
+//
+
+extension TSChatViewController {
+    
+   var creatNavBarContentView: UIView{
+        let view = UIView()
+        view.backgroundColor = .clear
+        return view
+    }
+    
+    var creatNormalNavBarView: TSNormalNavigationBarView{
+        let view = TSNormalNavigationBarView()
+        return view
+    }
+    
+     var creatVipBtn: UIButton{
+        let vipBtn = UIButton.createButton(image: UIImage(named: "nav_vip")) { [weak self]  in
+            guard let self = self else { return }
+            TSPurchaseVC.show(target: self) {
+      
+            }
+        }
+        return vipBtn
+    }
+    
+     var creatNavBarView: TSBaseNavContentBarView{
+        let navBarView = TSBaseNavContentBarView()
+        
+        let titleImageView = UIImageView.createImageView(imageName: "nav_title_aichat",contentMode: .scaleToFill)
+        navBarView.barView.addSubview(titleImageView)
+        titleImageView.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.left.equalTo(16)
+        }
+        
+        let setBtn = UIButton.createButton(image: UIImage(named: "setting")) { [weak self]  in
+            guard let self = self else { return }
+            let setingVC = TSSetingVC()
+            setingVC.hidesBottomBarWhenPushed = true
+            navigationController?.pushViewController(setingVC, animated: true)
+        }
+        navBarView.barView.addSubview(setBtn)
+        setBtn.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-16)
+            make.width.height.equalTo(24)
+        }
+        
+        let historyBtn = UIButton.createButton(image: UIImage(named: "aichat_history")) { [weak self]  in
+            guard let self = self else { return }
+            let historyVC = TSAIChatHistoryVC()
+            historyVC.hidesBottomBarWhenPushed = true
+            navigationController?.pushViewController(historyVC, animated: true)
+        }
+        navBarView.barView.addSubview(historyBtn)
+        historyBtn.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-60)
+            make.width.height.equalTo(24)
+        }
+        
+        navBarView.barView.addSubview(vipBtn)
+        vipBtn.snp.makeConstraints { make in
+            make.centerY.equalToSuperview()
+            make.trailing.equalTo(-104)
+            make.width.height.equalTo(24)
+        }
+        
+        return navBarView
+    }
+    
+    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)
+        }
+    }
+    
+    
+}
+
+ 
+extension TSChatViewController {
+    
+    func configureNaviBarView(){
+        
+        navigationItem.title = "MessageKit"
+        edgesForExtendedLayout = [.top]
+        
+        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()
+            }
+        }
+
+    }
+    
+    @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()
+        })
+    }
+
+    
+}

+ 102 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+SendMsg.swift

@@ -0,0 +1,102 @@
+//
+//  TSChatViewController+SendMsg.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/17.
+//
+
+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)
+    }
+    
+    
+    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)
+                
+                //更新 Vip
+                if kPurchaseDefault.isVip == false{
+                    self.updateVipView()
+                }
+                
+//                self.messagesCollectionView.scrollToLastItem(animated: false)
+            }
+        }
+    }
+    
+    func updataAIChatCellUI(){
+        kExecuteOnMainThread {
+            if self.messageList.count >= 2 {
+                UIView.performWithoutAnimation {
+                    self.messagesCollectionView.reloadItems(at: [self.lastIndexPath])
+                }
+            }else{
+                self.messagesCollectionView.reloadData()
+            }
+        }
+    }
+    
+
+}

+ 100 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController+VipView.swift

@@ -0,0 +1,100 @@
+//
+//  TSChatViewController+View.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/17.
+//
+
+extension TSChatViewController {
+
+    var creatFreeText: 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
+    }
+    
+    
+    var creatUpgradeVipBg: 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: .medium),
+            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
+    }
+    
+}
+
+extension TSChatViewController{
+    
+    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)
+                }
+            }
+        }
+    }
+
+}

+ 180 - 0
AIEmoji/Business/AIChat/TSChatViewController/TSChatViewController/TSChatViewController.swift

@@ -0,0 +1,180 @@
+//
+// 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 MessageKit
+import UIKit
+
+class TSChatViewController: MessagesViewController, MessagesDataSource {
+
+    var deleteBlock:(()->Void)?
+    //数据
+    var viewModel:TSAIChatVM = TSAIChatVM()
+    lazy var messageList: [TSChatMessage] = []
+    
+    //导航栏
+    lazy var vipBtn: UIButton = creatVipBtn
+    lazy var navBarView: TSBaseNavContentBarView = creatNavBarView
+    lazy var navBarContentView: UIView = creatNavBarContentView
+    lazy var normalNavBarView: TSNormalNavigationBarView = creatNormalNavBarView
+
+    //collectionView 布局
+    lazy var textMessageSizeCalculator = TSTextLayoutSizeCalculator(layout:self.messagesCollectionView.messagesCollectionViewFlowLayout)
+    
+    //键盘
+    lazy var inputBarVC: TSChatInputBarVC = creatInputBarVC
+    lazy var inputBarBgView:UIView = creatInputBarBgView
+    let inputBarTopView:UIView = UIView()
+    lazy var scrollToBottomButton: UIButton = creatScrollToBottomButton
+
+    // vip 相关
+    lazy var freeText: UILabel = creatFreeText
+    lazy var upgradeVipBg: UIView = creatUpgradeVipBg
+
+    
+    let formatter: DateFormatter = {
+        let formatter = DateFormatter()
+        formatter.dateStyle = .medium
+        return formatter
+    }()
+    
+    override func viewDidLoad() {
+        super.viewDidLoad()
+       
+        configureNaviBarView()
+        configureMessageCollectionView()
+        configureMessageInputBar()
+        loadFirstMessages()
+        updateVipView()
+        dealThings()
+    }
+    
+    
+    func dealThings(){
+        
+        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)
+        }
+    }
+    
+    @objc func vipInfoChanged() {
+        kExecuteOnMainThread {
+            self.vipBtn.isHidden = PurchaseManager.default.isVip
+            self.updateVipView()
+        }
+    }
+
+    @objc func saveChatList() {
+        messageList.removeFirst()
+        if messageList.count > 0 {
+            //保存本次聊天记录
+            viewModel.updateMessages(msgModels: messageList)
+        }
+    }
+    
+    func loadFirstMessages() {
+        self.messageList = viewModel.getHistoryChatMessage()
+        self.messagesCollectionView.reloadData()
+        self.messagesCollectionView.scrollToLastItem(animated: false)
+    }
+
+    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)
+        }
+    }
+
+
+    // 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)
+    }
+    
+    
+    override func viewWillAppear(_ animated: Bool) {
+        
+    }
+
+}
+
+

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

@@ -0,0 +1,140 @@
+//
+//  TSAIChatVM.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/9.
+//
+
+
+
+let kAIErrorString = "Server is busy, please try again later".localized
+class TSAIChatVM {
+
+    enum UIStype {
+        case chat
+        case history
+    }
+        
+    lazy var dbAIChatList: TSDBAIChatList = {
+        let dbAIChatList = TSDBAIChatList.getOneDB()
+        return dbAIChatList
+    }()
+
+    var uiStyle:UIStype = UIStype.chat
+    var streamRequest:StreamPostRequest?
+    
+    let kAIUser = TSChatUser(senderId: "000", displayName: "AI")
+    let kUserSender = TSChatUser(senderId: "001", displayName: "001")
+    
+    
+    //ai markDown 回答的string
+    var AiMDString:String = ""
+
+}
+
+
+//MARK: AI 聊天请求
+extension TSAIChatVM {
+    
+    func sendChatMessage(
+        message:String,
+        streamHandler:@escaping (String) -> Void,
+        completion: @escaping (Any?, Error?) -> Void
+    ) {
+        let parameters: [String: String] = [
+            "sessionId": dbAIChatList.sessionId,
+            "message": message
+        ]
+        
+//        AiMDString = ""
+//        streamRequest = TSNetworkShared.postStream(urlType: .chat,parameters: parameters) {[weak self] string in
+//            guard let self = self else { return }
+//            AiMDString = AiMDString + string
+//            streamHandler(string)
+//        } completion: { result in
+//            switch result {
+//            case .success(let data):
+//                completion(data,nil)
+//            case .failure(let error):
+//                completion(nil,error)
+//            }
+//        }
+        
+        AiMDString = ""
+        _ = TSNetworkShared.postStream(urlType: .chat,parameters: parameters) {[weak self] string in
+            guard let self = self else { return }
+            
+            if AiMDString.count == 0 {
+                //{"code":500,"message":"Server Error"}
+                //如果错误,基本都是第一条就返回结果了,这里需要做下 code 判断,来确定接口
+                if let dataDict = string.jsonDict() , let code = dataDict["code"] {
+                    completion(nil,NSError(domain: dataDict.safeString(forKey: "message"), code: 0))
+                    return
+                }
+            }
+            
+            AiMDString = AiMDString + string
+            streamHandler(string)
+        } completion: { result in
+            switch result {
+            case .success(let data):
+                completion(data,nil)
+            case .failure(let error):
+                completion(nil,error)
+            }
+        }
+    }
+    
+    func stopAiGenerate () {
+        streamRequest?.stopRequest()
+    }
+}
+
+//MARK: 数据存储
+extension TSAIChatVM {
+    
+    func getHistoryChatMessage() -> [TSChatMessage] {
+        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🇺🇸 Facilitating language learning  \n📑 Assisting in your studies  \n💡 Brainstorming ideas and much more!"
+            let msg = TSChatMessage(kind: .attributedText(kMDAttributedString(text: aiString)), user: kAIUser, messageId: "", date: Date())
+            return [msg]
+        }
+    }
+    
+//    func updateMessage(msgModel:TSChatMessage){
+//        kExecuteOnMainThread {
+//            //保存数据库
+//            self.dbAIChatList.updateMessage(msgModel: msgModel)
+//            //保存服务器
+//        }
+//    }
+   
+    
+    func updateMessages(msgModels:[TSChatMessage]){
+        kExecuteOnMainThread {
+            //保存数据库
+            self.dbAIChatList.updateMessages(msgModels: msgModels)
+            //保存服务器
+        }
+        
+        
+    }
+}
+
+
+
+import SwiftyMarkdown
+private var md: SwiftyMarkdown = {
+    let md = SwiftyMarkdown(string: "")
+    md.setFontColorForAllStyles(with: .white)
+    md.code.color = .red
+    
+    return md
+}()
+
+//字符串转成 markdown NSAttributedString
+func kMDAttributedString(text:String) -> NSAttributedString{
+    return md.attributedString(from: text)
+}

+ 183 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/CameraInputBarAccessoryView.swift

@@ -0,0 +1,183 @@
+//
+//  CameraInput.swift
+//  ChatExample
+//
+//  Created by Mohannad on 12/25/20.
+//  Copyright © 2020 MessageKit. All rights reserved.
+//
+
+import InputBarAccessoryView
+import UIKit
+
+// MARK: - CameraInputBarAccessoryViewDelegate
+
+protocol CameraInputBarAccessoryViewDelegate: InputBarAccessoryViewDelegate {
+  func inputBar(_ inputBar: InputBarAccessoryView, didPressSendButtonWith attachments: [AttachmentManager.Attachment])
+}
+
+extension CameraInputBarAccessoryViewDelegate {
+  func inputBar(_: InputBarAccessoryView, didPressSendButtonWith _: [AttachmentManager.Attachment]) { }
+}
+
+// MARK: - CameraInputBarAccessoryView
+
+class CameraInputBarAccessoryView: InputBarAccessoryView {
+  // MARK: Lifecycle
+
+  override init(frame: CGRect) {
+    super.init(frame: frame)
+    configure()
+  }
+
+  required init?(coder _: NSCoder) {
+    fatalError("init(coder:) has not been implemented")
+  }
+
+  // MARK: Internal
+
+  lazy var attachmentManager: AttachmentManager = { [unowned self] in
+    let manager = AttachmentManager()
+    manager.delegate = self
+    return manager
+  }()
+
+  func configure() {
+    let camera = makeButton(named: "ic_camera")
+    camera.tintColor = .darkGray
+    camera.onTouchUpInside { [weak self] _ in
+      self?.showImagePickerControllerActionSheet()
+    }
+    setLeftStackViewWidthConstant(to: 35, animated: true)
+    setStackViewItems([camera], forStack: .left, animated: false)
+    inputPlugins = [attachmentManager]
+  }
+
+  override func didSelectSendButton() {
+    if attachmentManager.attachments.count > 0 {
+      (delegate as? CameraInputBarAccessoryViewDelegate)?
+        .inputBar(self, didPressSendButtonWith: attachmentManager.attachments)
+    }
+    else {
+      delegate?.inputBar(self, didPressSendButtonWith: inputTextView.text)
+    }
+  }
+
+  // MARK: Private
+
+  private func makeButton(named _: String) -> InputBarButtonItem {
+    InputBarButtonItem()
+      .configure {
+        $0.spacing = .fixed(10)
+        $0.image = UIImage(systemName: "camera.fill")?.withRenderingMode(.alwaysTemplate)
+        $0.setSize(CGSize(width: 30, height: 30), animated: false)
+      }.onSelected {
+        $0.tintColor = .systemBlue
+      }.onDeselected {
+        $0.tintColor = UIColor.lightGray
+      }.onTouchUpInside { _ in
+        print("Item Tapped")
+      }
+  }
+}
+
+// MARK: UIImagePickerControllerDelegate, UINavigationControllerDelegate
+
+extension CameraInputBarAccessoryView: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
+  @objc
+  func showImagePickerControllerActionSheet() {
+    let photoLibraryAction = UIAlertAction(title: "Choose From Library", style: .default) { [weak self] _ in
+      self?.showImagePickerController(sourceType: .photoLibrary)
+    }
+
+    let cameraAction = UIAlertAction(title: "Take From Camera", style: .default) { [weak self] _ in
+      self?.showImagePickerController(sourceType: .camera)
+    }
+
+    let cancelAction = UIAlertAction(title: "Cancel", style: .default, handler: nil)
+
+
+  }
+
+  func showImagePickerController(sourceType: UIImagePickerController.SourceType) {
+    let imgPicker = UIImagePickerController()
+    imgPicker.delegate = self
+    imgPicker.allowsEditing = true
+    imgPicker.sourceType = sourceType
+    imgPicker.presentationController?.delegate = self
+    inputAccessoryView?.isHidden = true
+    getRootViewController()?.present(imgPicker, animated: true, completion: nil)
+  }
+
+  func imagePickerController(
+    _: UIImagePickerController,
+    didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any])
+  {
+    if let editedImage = info[UIImagePickerController.InfoKey.editedImage] as? UIImage {
+      // self.sendImageMessage(photo: editedImage)
+      inputPlugins.forEach { _ = $0.handleInput(of: editedImage) }
+    }
+    else if let originImage = info[UIImagePickerController.InfoKey.originalImage] as? UIImage {
+      inputPlugins.forEach { _ = $0.handleInput(of: originImage) }
+      // self.sendImageMessage(photo: originImage)
+    }
+    getRootViewController()?.dismiss(animated: true, completion: nil)
+    inputAccessoryView?.isHidden = false
+  }
+
+  func imagePickerControllerDidCancel(_: UIImagePickerController) {
+    getRootViewController()?.dismiss(animated: true, completion: nil)
+    inputAccessoryView?.isHidden = false
+  }
+
+  func getRootViewController() -> UIViewController? {
+    (UIApplication.shared.delegate as? AppDelegate)?.window?.rootViewController
+  }
+}
+
+// MARK: AttachmentManagerDelegate
+
+extension CameraInputBarAccessoryView: AttachmentManagerDelegate {
+  // MARK: - AttachmentManagerDelegate
+
+  func attachmentManager(_: AttachmentManager, shouldBecomeVisible: Bool) {
+    setAttachmentManager(active: shouldBecomeVisible)
+  }
+
+  func attachmentManager(_ manager: AttachmentManager, didReloadTo _: [AttachmentManager.Attachment]) {
+    sendButton.isEnabled = manager.attachments.count > 0
+  }
+
+  func attachmentManager(_ manager: AttachmentManager, didInsert _: AttachmentManager.Attachment, at _: Int) {
+    sendButton.isEnabled = manager.attachments.count > 0
+  }
+
+  func attachmentManager(_ manager: AttachmentManager, didRemove _: AttachmentManager.Attachment, at _: Int) {
+    sendButton.isEnabled = manager.attachments.count > 0
+  }
+
+  func attachmentManager(_: AttachmentManager, didSelectAddAttachmentAt _: Int) {
+    showImagePickerControllerActionSheet()
+  }
+
+  // MARK: - AttachmentManagerDelegate Helper
+
+  func setAttachmentManager(active: Bool) {
+    let topStackView = topStackView
+    if active, !topStackView.arrangedSubviews.contains(attachmentManager.attachmentView) {
+      topStackView.insertArrangedSubview(attachmentManager.attachmentView, at: topStackView.arrangedSubviews.count)
+      topStackView.layoutIfNeeded()
+    } else if !active, topStackView.arrangedSubviews.contains(attachmentManager.attachmentView) {
+      topStackView.removeArrangedSubview(attachmentManager.attachmentView)
+      topStackView.layoutIfNeeded()
+    }
+  }
+}
+
+// MARK: UIAdaptivePresentationControllerDelegate
+
+extension CameraInputBarAccessoryView: UIAdaptivePresentationControllerDelegate {
+  // Swipe to dismiss image modal
+  public func presentationControllerWillDismiss(_: UIPresentationController) {
+    isHidden = false
+  }
+}

+ 205 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSMessageContentCell.swift

@@ -0,0 +1,205 @@
+//
+//  CustomMessageContentCell.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+
+class TSMessageContentCell: MessageCollectionViewCell {
+    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()
+    }
+    
+    /// The `MessageCellDelegate` for the cell.
+    weak var delegate: MessageCellDelegate?
+    
+    /// 顶部内容
+    var topContainerView: UIView = {
+        let containerView = UIView()
+        return containerView
+    }()
+    
+    /// 底部内容
+    var centerContainerView: UIView = {
+        let containerView = UIView()
+        return containerView
+    }()
+    
+    /// 底部内容
+    var bottomContainerView: UIView = {
+        let containerView = UIView()
+        return containerView
+    }()
+    
+    
+    /// 消息内容容器.frame由 子视图自动布局撑起大小
+    var messageContainerView: UIView = {
+        let containerView = UIView()
+        containerView.cornerRadius = 16
+        return containerView
+    }()
+
+    // 左头像
+    var leadingAvatarImageView: UIImageView = {
+        let imageView = UIImageView.createImageView(imageName: "aichat_avatar")
+        return imageView
+    }()
+    
+    // 右头像(预留)
+    var trailingAvatarImageView: UIImageView = {
+        let imageView = UIImageView.createImageView(imageName: "aichat_avatar")
+        imageView.isHidden = true
+        return imageView
+    }()
+    
+    
+    //初始化 cell
+    func setupSubviews() {
+        
+        contentView.addSubview(topContainerView)
+        contentView.addSubview(centerContainerView)
+        contentView.addSubview(bottomContainerView)
+        topContainerView.snp.makeConstraints { make in
+            make.leading.top.trailing.equalTo(0)
+        }
+        centerContainerView.snp.makeConstraints { make in
+            make.top.equalTo(topContainerView.snp.bottom)
+            make.leading.trailing.equalTo(0)
+        }
+        
+        bottomContainerView.snp.makeConstraints { make in
+            make.top.equalTo(centerContainerView.snp.bottom)
+            make.leading.trailing.bottom.equalTo(0)
+        }
+        
+        centerContainerView.addSubview(leadingAvatarImageView)
+        centerContainerView.addSubview(messageContainerView)
+        centerContainerView.addSubview(trailingAvatarImageView)
+        
+        
+        let leadingAvatarEdge = TSLayoutSizeCalculator.leadingAvatarEdge
+        let leadingAvatarSize = TSLayoutSizeCalculator.leadingAvatarSize
+        leadingAvatarImageView.snp.makeConstraints { make in
+            make.leading.equalTo(leadingAvatarEdge.left)
+            make.top.equalTo(leadingAvatarEdge.top)
+            make.size.equalTo(leadingAvatarSize)
+        }
+        
+        //暂时没用,预留的,数值都是 0
+        let traingAvatarEdge = TSLayoutSizeCalculator.traingAvatarEdge
+        let trainggAvatarSize = TSLayoutSizeCalculator.traingAvatarSize
+        trailingAvatarImageView.snp.makeConstraints { make in
+            make.trailing.equalTo(traingAvatarEdge.right)
+            make.top.equalTo(traingAvatarEdge.top)
+            make.size.equalTo(trainggAvatarSize)
+        }
+        
+        let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerLeadingEdge
+        messageContainerView.snp.makeConstraints { make in
+            make.leading.equalTo(leadingAvatarImageView.snp.trailing).offset(cellMessageContainerEdge.left)
+            make.trailing.lessThanOrEqualTo(trailingAvatarImageView.snp.leading).offset(-16)
+            make.top.equalTo(cellMessageContainerEdge.top)
+            make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+        }
+    }
+    
+
+    
+    override func prepareForReuse() {
+        super.prepareForReuse()
+    }
+    
+    // 点击触发事件
+    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 configure(
+        with message: MessageType,
+        at indexPath: IndexPath,
+        in messagesCollectionView: MessagesCollectionView,
+        dataSource: MessagesDataSource,
+        and sizeCalculator: TSLayoutSizeCalculator)
+    {
+        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)
+        
+        
+        
+
+        //是否是当前用户发送的消息
+        let fromCurrentSender = dataSource.isFromCurrentSender(message: message)
+        if fromCurrentSender {
+            leadingAvatarImageView.isHidden = true
+            let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerTraingEdge
+            messageContainerView.snp.remakeConstraints { make in
+                make.leading.greaterThanOrEqualTo(cellMessageContainerEdge.left)
+                make.trailing.equalTo(-cellMessageContainerEdge.right)
+                make.top.equalTo(cellMessageContainerEdge.top)
+                make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+            }
+        }else{
+            leadingAvatarImageView.isHidden = false
+            let cellMessageContainerEdge = TSLayoutSizeCalculator.cellMessageContainerLeadingEdge
+            messageContainerView.snp.remakeConstraints { make in
+                make.leading.equalTo(leadingAvatarImageView.snp.trailing).offset(cellMessageContainerEdge.left)
+                make.trailing.lessThanOrEqualTo(-cellMessageContainerEdge.right)
+                make.top.equalTo(cellMessageContainerEdge.top)
+                make.bottom.equalTo(-cellMessageContainerEdge.bottom)
+            }
+        }
+        
+        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
+    }
+}

+ 167 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TSTextMessageContentCell.swift

@@ -0,0 +1,167 @@
+//
+//  CustomTextMessageContentCell.swift
+//  ChatExample
+//
+//  Created by Vignesh J on 01/05/21.
+//  Copyright © 2021 MessageKit. All rights reserved.
+//
+
+import MessageKit
+import UIKit
+import SwiftyMarkdown
+class TSTextMessageContentCell: TSMessageContentCell {
+    var messageLabel: UILabel = {
+        let label = UILabel.createLabel(font: .font(size: 16),numberOfLines: 0)
+        label.lineBreakMode = .byWordWrapping
+        return label
+    }()
+    
+    lazy var activityIndicator: UIActivityIndicatorView = {
+        activityIndicator = UIActivityIndicatorView(style: .medium)
+        activityIndicator.color = .white
+        activityIndicator.isHidden = true
+        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()
+    }
+    
+    override func setupSubviews() {
+        super.setupSubviews()
+        
+        let labelEdge = TSTextLayoutSizeCalculator.cellMessagelabelEdge
+        messageContainerView.addSubview(messageLabel)
+        messageLabel.snp.makeConstraints { make in
+            make.edges.equalTo(labelEdge)
+            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)
+        }
+        
+        
+        bottomToolView.addSubview(refreshBtn)
+        refreshBtn.snp.makeConstraints { make in
+            make.left.equalTo(12)
+            make.bottom.equalTo(-9)
+            make.width.height.equalTo(20)
+        }
+        
+        
+        
+    }
+    
+    override func configure(
+        with message: MessageType,
+        at indexPath: IndexPath,
+        in messagesCollectionView: MessagesCollectionView,
+        dataSource: MessagesDataSource,
+        and sizeCalculator: TSLayoutSizeCalculator)
+    {
+        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
+            make.width.equalTo(labelFrame.width)
+            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赋值")
+        case .attributedText(let text):
+            messageLabel.text = text.string
+            messageLabel.attributedText = text
+//            debugPrint("attributedText赋值")
+            
+//            extractAndPrintSubstring(from: messageLabel.text ?? "", to: text.string)
+        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...]
+            
+            // 将提取的子字符串逐个字符输出
+            var string = sourceString
+            for character in extractedSubstring {
+//                print(character)
+                string = string + String(character)
+                messageLabel.text = string
+                messageLabel.attributedText = kMDAttributedString(text: string)
+            }
+        } else {
+            print("未找到 sourceString")
+            messageLabel.text = targetString
+            messageLabel.attributedText = kMDAttributedString(text: targetString)
+        }
+    }
+    
+    func startAnimating() {
+        activityIndicator.isHidden = false
+        activityIndicator.startAnimating()
+    }
+    
+    func stopAnimating() {
+        activityIndicator.isHidden = true
+        activityIndicator.stopAnimating()
+    }
+}

+ 63 - 0
AIEmoji/Business/AIChat/TSChatViewController/Views/TableViewCells.swift

@@ -0,0 +1,63 @@
+// MIT License
+//
+// Copyright (c) 2017-2019 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 UIKit
+
+internal class TextFieldTableViewCell: UITableViewCell {
+  // MARK: Lifecycle
+
+  // MARK: - View lifecycle
+
+  override init(style: UITableViewCell.CellStyle, reuseIdentifier: String?) {
+    super.init(style: style, reuseIdentifier: reuseIdentifier)
+
+    mainLabel.translatesAutoresizingMaskIntoConstraints = false
+    textField.translatesAutoresizingMaskIntoConstraints = false
+
+    contentView.addSubview(mainLabel)
+    contentView.addSubview(textField)
+
+    NSLayoutConstraint.activate([
+      mainLabel.leadingAnchor.constraint(equalTo: contentView.leadingAnchor, constant: 20),
+      mainLabel.widthAnchor.constraint(equalToConstant: 200),
+      mainLabel.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+
+      textField.centerYAnchor.constraint(equalTo: contentView.centerYAnchor),
+
+      textField.trailingAnchor.constraint(equalTo: contentView.trailingAnchor, constant: -20),
+      textField.widthAnchor.constraint(equalToConstant: 50),
+    ])
+
+    textField.textAlignment = .right
+  }
+
+  required init?(coder _: NSCoder) {
+    fatalError("init(coder:) has not been implemented")
+  }
+
+  // MARK: Internal
+
+  static let identifier = "TextFieldTableViewCellIdentifier"
+
+  var mainLabel = UILabel()
+  var textField = UITextField()
+}

+ 12 - 0
AIEmoji/Business/General/Ex/Notification+Ex.swift

@@ -0,0 +1,12 @@
+//
+//  Notification+Ex.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/13.
+//
+
+import Foundation
+
+extension Notification.Name {
+    static let kApplicationWillTerminate = Notification.Name("applicationWillTerminate")
+}

+ 1 - 1
AIEmoji/Business/General/TSBottomAlertVC.swift

@@ -18,7 +18,7 @@ class TSBottomAlertVC: TSBaseVC {
     
     lazy var bottomView: UIView = {
         let bottomView = UIView(frame: CGRectMake(0, k_ScreenHeight-448, k_ScreenWidth, 448))
-        bottomView.backgroundColor = "#222222".color
+        bottomView.backgroundColor = "#222222".uiColor
         bottomView.cornersRound(radius: 20, corner: [.topLeft,.topRight])
         return bottomView
     }()

+ 15 - 12
AIEmoji/Business/General/TSSmallIconBrowseVC/TSSmallIconBrowseVC.swift

@@ -31,7 +31,20 @@ class TSSmallIconBrowseVC: TSBottomAlertVC {
     }
     
     lazy var collectionView: UICollectionView = {
-        let collectionView = UICollectionView.createCommon(delegate: self, cellReuseIds: [cellId])
+
+        let layout = UICollectionViewFlowLayout()
+        layout.scrollDirection = .vertical
+        
+        let collectionView = UICollectionView(frame: .zero, collectionViewLayout: layout)
+        collectionView.delegate = self
+        collectionView.dataSource = self
+        collectionView.showsVerticalScrollIndicator = false
+        collectionView.showsHorizontalScrollIndicator = false
+        collectionView.backgroundColor = .clear
+        if #available(iOS 11.0, *) {
+            collectionView.contentInsetAdjustmentBehavior = .never
+        }
+        collectionView.register(TSSmallIconBrowseCell.self, forCellWithReuseIdentifier: cellId)
         collectionView.isPagingEnabled = true
         collectionView.isHidden = true
         if let flowLayout = collectionView.collectionViewLayout as? UICollectionViewFlowLayout {
@@ -130,17 +143,7 @@ class TSSmallIconBrowseVC: TSBottomAlertVC {
     
     
     func JudgeVip() -> Bool {
-        //判断 vip
-        if currentModel?.response.vip
-            == true,
-           PurchaseManager.default.isVip == false
-        {
-            TSPurchaseVC.show(target: self) {[weak self]  in
-                guard let self = self else { return }
-            }
-            return true
-        }
-        return false
+        return kJudgeVip(externalBool: currentModel?.response.vip ?? false , vc: self, closePageBlock: nil)
     }
 }
 

+ 1 - 1
AIEmoji/Business/LaunchVC/TSLaunchVC.swift

@@ -15,7 +15,7 @@ class TSLaunchVC: UIViewController {
     private var timer: DispatchSourceTimer?
     // 闪屏页剩余显示时长
     #if DEBUG
-        private var remindTimeInterval: TimeInterval = 2.0
+    private var remindTimeInterval: TimeInterval = 0.5
     #else
         private var remindTimeInterval: TimeInterval = 2.0
     #endif

+ 4 - 4
AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/TSEmojisChildVC.swift

@@ -28,9 +28,9 @@ class TSEmojisChildVC: TSBaseVC {
     //联动滚动回调
     var listViewDidScrollCallback: ((UIScrollView) -> ())?
     
-    lazy var collectionComponent: CollectionViewComponent = {
+    lazy var collectionComponent: TSCollectionViewComponent = {
         let layout = UICollectionViewFlowLayout()
-        let cp = CollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
+        let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
         cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
         cp.didScrollHandler = { [weak self] scrollView in
             guard let self = self else { return }
@@ -61,7 +61,7 @@ class TSEmojisChildVC: TSBaseVC {
 
 
     override func createView() {
-        view.backgroundColor = "#222222".color
+        view.backgroundColor = "#222222".uiColor
         view.cornersRound(radius: 20, corner: [.topLeft,.topRight])
         edgesForExtendedLayout = []
         setNavBarViewHidden(true)
@@ -77,7 +77,7 @@ class TSEmojisChildVC: TSBaseVC {
 
     override func viewDidLayoutSubviews() {
         super.viewDidLayoutSubviews()
-        print("View did layout subviews")
+//        print("View did layout subviews")
         self.collectionComponent.collectionView.frame = self.view.bounds
     }
 }

+ 4 - 1
AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/VIew/TSEmojisCoLItemCell.swift

@@ -37,12 +37,15 @@ class TSEmojisCoLItemCell: TSBaseCollectionCell {
         }
     }
     
-    override func renderView(with object: Any?, component: CollectionViewComponent, attributes: [String : Any]?) {
+    
+    
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
         super.renderView(with: object, component: component, attributes: attributes)
         if let itemModel = object as? TSEmojisColItemModel{
             showImageView.setAsyncImage(urlString: itemModel.dataModel.imageUrl,placeholder: kPlaceholderImage)
             vipImageView.isHidden = !itemModel.dataModel.vip
         }
     }
+
     
 }

+ 4 - 4
AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/ViewModel/TSEmojisChildColViewModel.swift

@@ -26,7 +26,7 @@ class TSEmojisColSectionModel: TSBaseModel {
 
 private let emojisColSection = UIEdgeInsets(top: 24, left: 24, bottom: 24, right: 24)
 private let itemW = (k_ScreenWidth - emojisColSection.left - emojisColSection.right - 64 - 1)/3.0
-extension TSEmojisColSectionModel : CollectionViewSectionComponent{
+extension TSEmojisColSectionModel : TSCollectionViewSectionComponent{
     var sectionInset: UIEdgeInsets {
         return emojisColSection
     }
@@ -38,12 +38,12 @@ extension TSEmojisColSectionModel : CollectionViewSectionComponent{
     var itemSpacing: CGFloat {
         return 32
     }
-    var headerComponent: CollectionViewReuseViewComponent? {
+    var headerComponent: TSCollectionViewReuseViewComponent? {
         //暂时没有分区头,但是已经预留有模型了
         return nil
     }
     
-    var cells: [CollectionViewCellComponent] {
+    var cells: [TSCollectionViewCellComponent] {
         return items
     }
 }
@@ -58,7 +58,7 @@ class TSEmojisColItemModel: TSBaseModel {
     }
 }
 
-extension TSEmojisColItemModel: CollectionViewCellComponent{
+extension TSEmojisColItemModel: TSCollectionViewCellComponent{
     var cellClass: UICollectionViewCell.Type {
         return TSEmojisCoLItemCell.self
     }

+ 1 - 1
AIEmoji/Business/TSEmojisVC/TSEmojisChildVC/ViewModel/TSEmojisChildViewModel.swift

@@ -15,7 +15,7 @@ class TSEmojisChildViewModel {
     }
     
     var dataArray:[TSEmojisModel] = [TSEmojisModel]()
-    var colDataArray:[Component] = [Component]()
+    var colDataArray:[TSComponent] = [TSComponent]()
 }
 
 extension TSEmojisChildViewModel {

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

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

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

@@ -7,13 +7,14 @@
 
 import Combine
 
-enum TSProgressState {
+enum TSProgressState  {
     case none
     case start
     case pending
     case progress(CGFloat)
     case success(Any?)
     case failed(String)
+
 }
 
 class TSGenmojiGennerateViewModel {

+ 5 - 9
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/TSGenmojiVC.swift

@@ -53,9 +53,9 @@ class TSGenmojiVC: TSBaseVC {
     }()
     
     
-    lazy var collectionComponent: CollectionViewComponent = {
+    lazy var collectionComponent: TSCollectionViewComponent = {
         let layout = UICollectionViewFlowLayout()
-        let cp = CollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
+        let cp = TSCollectionViewComponent(frame: CGRect.zero, layout: layout, attributes: [ :])
         cp.collectionView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: k_Height_TabBar, right: 0)
         cp.itemActionHandler = { [weak self] cellCp, indexPath in
             guard let self = self else { return }
@@ -135,13 +135,9 @@ extension TSGenmojiVC {
     func generateImage(text:String) {
         
         //判断 vip
-        if kPurchaseDefault.freeNumAvailable() == false{
-            TSPurchaseVC.show(target: self) {[weak self] in
-                guard let self = self else { return }
-                collectionComponent.reloadData()
-            }
-            return
-        }
+        if kJudgeVip(externalBool: kPurchaseDefault.freeNumAvailable(type: .generatePic) == false, vc: self) {[weak self] in
+            guard let self = self else { return }
+        }{ return }
         
         let gennerateVC = TSGenmojiGennerateVC(aiText: text) {[weak self] model in
             guard let self = self else { return }

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

@@ -37,7 +37,7 @@ class TSGenmojiColSectionView: TSBaseCollectionnReusableView {
         }
     }
     
-    override func renderView(with object: Any?, component: CollectionViewComponent, attributes: [String : Any]?) {
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
         super.renderView(with: object, component: component, attributes: attributes)
         if let componentReuseViewModel = object as? TSGenmojiColComponentReuseViewModel {
             leftLab.text = componentReuseViewModel.sectionModel.name

+ 16 - 3
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiGennerateCell.swift

@@ -17,11 +17,12 @@ class TSGenmojiGennerateCell : TSBaseCollectionCell{
             text: "",
             font: .font(size: 12),
             textColor: .white,
-            backgroundColor: "#333333".color,
+            backgroundColor: "#333333".uiColor,
             textInsets: UIEdgeInsets(top: 21, left: 16, bottom: 21, right: 16)
         )
         customTextView.delegate = self
         customTextView.layer.cornerRadius = 12
+        customTextView.returnKeyType = .send
         return customTextView
     }()
     
@@ -68,10 +69,10 @@ 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: CollectionViewComponent, attributes: [String : Any]?) {
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
         super.renderView(with: object, component: component, attributes: attributes)
         submitBtn.setTitle(getVipText(), for: .normal)
     }
@@ -81,4 +82,16 @@ extension TSGenmojiGennerateCell: UITextViewDelegate{
     func textViewDidChange(_ textView: UITextView) {
         submitBtn.isEnabled = textView.text.count > 0
     }
+    
+    // 实现 UITextViewDelegate 协议方法,控制 return 键行为
+    func textView(_ textView: UITextView, shouldChangeTextIn range: NSRange, replacementText text: String) -> Bool {
+        if text == "\n" {
+            // 当输入为换行符(即按下 return 键)时,执行相应操作
+            actionHandler(any: customTextView.text)
+            textView.text = ""
+            textView.resignFirstResponder()
+            return false
+        }
+        return true
+    }
 }

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

@@ -25,7 +25,7 @@ class TSGenmojiItemCell: TSBaseCollectionCell {
     }
     
     
-    override func renderView(with object: Any?, component: CollectionViewComponent, attributes: [String : Any]?) {
+    override func renderView(with object: Any?, component: TSCollectionViewComponent, attributes: [String : Any]?) {
         super.renderView(with: object, component: component, attributes: attributes)
         
         if let itemModel = object as? TSGenmojiCoLItemModel{

+ 2 - 2
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/View/TSGenmojiTextView.swift

@@ -8,7 +8,7 @@
 class TSCustomTextView: UITextView {
     
     // Placeholder Label
-    private lazy var  placeholderLabel: TopLeftLabel = {
+    lazy var placeholderLabel: TopLeftLabel = {
         let placeholderLabel = TopLeftLabel()
         placeholderLabel.font = font
         placeholderLabel.textColor = placeholderColor
@@ -33,7 +33,7 @@ class TSCustomTextView: UITextView {
         }
     }
     
-    // Text Insets
+    // Placeholder Insets
     var textInsets: UIEdgeInsets = .zero {
         didSet {
             updateTextContainerInset()

+ 6 - 6
AIEmoji/Business/TSGenmojiVC/TSGenmojiVC/ViewModel/TSGenmojiCollectionViewModel.swift

@@ -102,7 +102,7 @@ class TSGenmojiCoLSectionModel: TSBaseModel {
 }
 
 
-extension TSGenmojiCoLSectionModel : CollectionViewSectionComponent{
+extension TSGenmojiCoLSectionModel : TSCollectionViewSectionComponent{
     var sectionInset: UIEdgeInsets {
         return style.sectionInset
     }
@@ -114,11 +114,11 @@ extension TSGenmojiCoLSectionModel : CollectionViewSectionComponent{
     var itemSpacing: CGFloat {
         return style.itemSpacing
     }
-    var headerComponent: CollectionViewReuseViewComponent? {
+    var headerComponent: TSCollectionViewReuseViewComponent? {
         return TSGenmojiColComponentReuseViewModel(sectionModel: self)
     }
     
-    var cells: [CollectionViewCellComponent] {
+    var cells: [TSCollectionViewCellComponent] {
         return items
     }
 }
@@ -137,7 +137,7 @@ class TSGenmojiCoLItemModel: TSBaseModel {
     }
 }
 
-extension TSGenmojiCoLItemModel: CollectionViewCellComponent{
+extension TSGenmojiCoLItemModel: TSCollectionViewCellComponent{
     var cellClass: UICollectionViewCell.Type {
         style.cellClass
     }
@@ -149,7 +149,7 @@ extension TSGenmojiCoLItemModel: CollectionViewCellComponent{
 
 
 //MARK: 分区头
-class TSGenmojiColComponentReuseViewModel : TSBaseModel ,CollectionViewReuseViewComponent {
+class TSGenmojiColComponentReuseViewModel : TSBaseModel ,TSCollectionViewReuseViewComponent {
     var sectionModel:TSGenmojiCoLSectionModel
 
     init(sectionModel:TSGenmojiCoLSectionModel) {
@@ -180,7 +180,7 @@ class TSGenmojiColComponentReuseViewModel : TSBaseModel ,CollectionViewReuseView
         return sectionModel.style.headerViewSize
     }
     
-    var viewType: CollectionViewReuseViewType {
+    var viewType: TSCollectionViewReuseViewType {
         return .header
     }
 }

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

@@ -9,7 +9,7 @@
 import ObjectMapper
 
 class TSGenmojiViewModel {
-    var colDataArray:[Component] = [Component]()
+    var colDataArray:[TSComponent] = [TSComponent]()
 
     lazy var generateSectionModel: TSGenmojiCoLSectionModel = {
         let generateSectionModel = TSGenmojiCoLSectionModel()

+ 15 - 2
AIEmoji/Business/TSPurchaseMembershipVC/TSPurchaseVC.swift

@@ -186,6 +186,19 @@ class TSPurchaseVC: TSBaseVC {
 
 
 
+func kJudgeVip(externalBool:Bool,
+               vc:UIViewController,
+               closePageBlock:(()->Void)?) -> Bool {
+    //判断 vip
+    if externalBool,
+       PurchaseManager.default.isVip == false
+    {
+        TSPurchaseVC.show(target: vc, closePageBlock: nil)
+        return true
+    }
+    return false
+}
+
 
 extension TSPurchaseVC{
     
@@ -226,11 +239,11 @@ struct PurchaseView :View {
                     VStack(alignment: .leading,spacing: 8) {
                         HStack(spacing: 8) {
                             Image("check").resizable().frame(width: 24, height: 24)
-                            Text("All Premium Wallpapers")
+                            Text("Unlock all Emojis Include AI")
                         }
                         HStack(spacing: 8) {
                             Image("check").resizable().frame(width: 24, height: 24)
-                            Text("Unlimited Video To Live")
+                            Text("Unlimited AI Chat")
                         }
                         
                         HStack(spacing: 8) {

+ 3 - 2
AIEmoji/Business/TSSetingVC/SetingVC/TSSetingViewModel.swift

@@ -12,6 +12,7 @@ class TSSetingViewModel: ObservableObject {
     @Published var settingTypes: [SettingType] = SettingType.allCases
     @Published var isViper: Bool = PurchaseManager.default.isVip
     
+    var appid = "6740220736"
     // todo.kailen-privacy
     func showPrivacy(parent: UIViewController) {
         let vc = TSBusinessWebVC(urlType: .privacy)
@@ -28,7 +29,7 @@ class TSSetingViewModel: ObservableObject {
 
     // todo.kailen-logo
     func shareApp(parent: UIViewController) {
-        let httpAppStoreLink = "https://apps.apple.com/app/id\(TSConfig.appid)"
+        let httpAppStoreLink = "https://apps.apple.com/app/id\(appid)"
         let text = ""
         let url = URL(string: httpAppStoreLink)!
         var image = UIImage()
@@ -61,7 +62,7 @@ class TSSetingViewModel: ObservableObject {
         /// 如果当前版本 弹过一次 那么下一次就走url
         /// 如果当前版本 没弹过,那就弹
         if count >= 4 {
-            let openStr = "itms-apps://itunes.apple.com/app/\(TSConfig.appid)?action=write-review"
+            let openStr = "itms-apps://itunes.apple.com/app/\(appid)?action=write-review"
             if let url = URL(string: openStr), UIApplication.shared.canOpenURL(url) {
                 if #available(iOS 10.0, *) {
                     UIApplication.shared.open(url, options: [:], completionHandler: nil)

+ 1 - 1
AIEmoji/Business/TSSetingVC/SetingVC/View/SettingPurchaseTopView.swift

@@ -32,7 +32,7 @@ struct SettingPurchaseTopView: View {
                         .font(.font(size: 14,weight: .medium))
                         .padding(EdgeInsets(top: 0, leading: 12, bottom: 0, trailing: 12))
                         .frame(height: 34)
-                        .foregroundColor("#010101".color.color)
+                        .foregroundColor("#010101".uiColor.color)
                         .background(UIColor.themeColor.color)
                         .cornerRadius(14.0)
                     

+ 6 - 6
AIEmoji/Business/TSSetingVC/SetingVC/View/TSSettingListView.swift

@@ -6,7 +6,6 @@
 //
 
 import SwiftUI
-import SwiftUIX
 
 struct TSSettingListView: View {
     @ObservedObject var viewModel: TSSetingViewModel
@@ -15,10 +14,10 @@ struct TSSettingListView: View {
         ZStack {
             Color.clear
             VStack(spacing: 0) {
-                Spacer().height(16)
+                Spacer().frame(height: 16)
                 
                 SettingPurchaseTopView(eventPublisher: publisher, isViper: $viewModel.isViper)
-                    .height(117*kDesignScale)
+                    .frame(height: 117*kDesignScale)
                     .onTapGesture {
                         if PurchaseManager.default.isVip {
                             return
@@ -27,7 +26,7 @@ struct TSSettingListView: View {
                     }
                 
                 ForEach(viewModel.settingTypes, id:\.self) { type in
-                    Spacer().height(16)
+                    Spacer().frame(height: 16)
                     SettingListItemView(type: type)
                     .onTapGesture {
                         publisher.settingPublisher.send(type)
@@ -60,7 +59,8 @@ struct SettingListItemView: View {
                 }
             }.padding(.horizontal)
         }
-        .height(64)
-        .cornerRadius(.allCorners, 16)
+        .frame(height: 64)
+        .cornerRadius(16)
+//        .cornerRadius(.allCorners, 16)
     }
 }

+ 1 - 1
AIEmoji/Business/TSSetingVC/TSBusinessWebVC/TSBusinessWebVC.swift

@@ -10,7 +10,7 @@ import WebKit
 class TSBusinessWebVC: TSBaseVC , WKNavigationDelegate {
     
     enum UrlType:String {
-        case privacy = "https://doc-hosting.flycricket.io/hahaemoji-privacy-policy/f260e05d-a029-435f-876e-4d6127f7ed25/privacy"
+        case privacy = "http://100yearslater.com/AI-Chat-Privacy-Policy.html"
         case terms = "https://doc-hosting.flycricket.io/hahaemoji-terms-of-use/7488c423-9bb6-480e-9a38-f52fba511335/terms"
 
         func getTitle() -> String {

+ 6 - 3
AIEmoji/Business/TSTabBarController/TSTabBarController.swift

@@ -24,16 +24,19 @@ class TSTabBarController: UITabBarController {
     }
 
     @objc private func setUpData() {
-        viewControllerArray = ["TSEmojisVC", "TSGenmojiVC", "TSWallpaperVC"]
-        titleArray = ["Emojis","Genmoji","Wallpaper"]
+//        viewControllerArray = ["TSEmojisVC", "TSGenmojiVC","TSAIChatContainerVC", "TSWallpaperVC"]
+        viewControllerArray = ["TSEmojisVC", "TSGenmojiVC","TSChatViewController", "TSWallpaperVC"]
+        titleArray = ["Emojis","Genmoji","AIChat","Wallpaper"]
         selectedImageArray = [
             "tabbar_select_emoji",
             "tabbar_select_aiemoji",
+            "tabbar_select_aichat",
             "tabbar_select_keyboard"
         ]
         unselectedImageArray = [
             "tabbar_unSelect_emoji",
             "tabbar_unSelect_aiemoji",
+            "tabbar_unSelect_aichat",
             "tabbar_unSelect_keyboard"
         ]
 
@@ -63,7 +66,7 @@ class TSTabBarController: UITabBarController {
         
         
         tabBar.isTranslucent = false
-        tabBar.backgroundColor = "#262626".color
+        tabBar.backgroundColor = "#262626".uiColor
         tabBar.backgroundImage = UIImage.colorFrom(color: tabBar.backgroundColor!, size: CGSize(width: k_ScreenWidth, height: k_TabBar_Height))
         
         // 自定义 TabBarItem 字体颜色和选中颜色

+ 2 - 2
AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Elemnet/DiyFixedTextElement.swift

@@ -39,7 +39,7 @@ class DiyFixedTextView: DiyElementBaseView {
     lazy var label: UILabel = {
         var label = UILabel()
         if let textElement = element as? DiyFixedTextElement{
-            label = UILabel.createLabel(text: textElement.text,font: .font(size: 16*kDiyDesignScaleW),textColor: textElement.textColor?.color,textAlignment: .center,numberOfLines: 0)
+            label = UILabel.createLabel(text: textElement.text,font: .font(size: 16*kDiyDesignScaleW),textColor: textElement.textColor?.uiColor,textAlignment: .center,numberOfLines: 0)
         }
         return label
     }()
@@ -60,7 +60,7 @@ class DiyFixedTextView: DiyElementBaseView {
         }
         
         if let textElement = element as? DiyFixedTextElement{
-            contentView.backgroundColor = textElement.backgroundColor?.color
+            contentView.backgroundColor = textElement.backgroundColor?.uiColor
        
         }
         updateContent()

+ 2 - 2
AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Elemnet/DiyTextElement.swift

@@ -83,14 +83,14 @@ class DiyTextView: DiyElementBaseView {
             style.lineSpacing = textElement.lineSpace
             style.alignment = textElement.alignment
             label.attributedText = NSAttributedString(string: textElement.text ?? "Add Text".localized, attributes: [
-                        .foregroundColor: textElement.textColor?.color ?? "#111111".color,
+                        .foregroundColor: textElement.textColor?.uiColor ?? "#111111".uiColor,
                         .font: UIFont.font(name: textElement.fontName, size: textElement.fontSize),
                         .paragraphStyle: style])
         } else {
             label.textAlignment = textElement.alignment
             label.text = textElement.text ?? "Add Text".localized
             label.font = UIFont.font(name: textElement.fontName, size: textElement.fontSize)
-            label.textColor = textElement.textColor?.color ?? "#111111".color
+            label.textColor = textElement.textColor?.uiColor ?? "#111111".uiColor
         }
         
         setNeedsDisplay()

+ 1 - 1
AIEmoji/Business/TSWallpaperVC/DiyWallpaper/Templates/DiyPaperTemplateBaseView.swift

@@ -87,7 +87,7 @@ class DiyPaperTemplateBaseView: UIView, DiyPaperTemplateBaseProtocol {
                 imageView.setAsyncImage(urlString: string)
             } else if string.hasPrefix("#") {
                 // 颜色
-                imageView.backgroundColor = string.color
+                imageView.backgroundColor = string.uiColor
             } else if let image = UIImage(named: string) {
                 imageView.image = image
             }

+ 2 - 2
AIEmoji/Business/TSWallpaperVC/TSDiyKeyboardVC/TSWallpaperVC.swift

@@ -36,7 +36,7 @@ class TSWallpaperVC: TSBaseVC {
         let segmentedView = JXSegmentedView()
         segmentedView.delegate = self
         segmentedView.dataSource = segmentedDataSource
-        segmentedView.backgroundColor = "#F1F4F8".color
+        segmentedView.backgroundColor = "#F1F4F8".uiColor
         segmentedView.cornerRadius = segmentedViewH/2.0
         //配置指示器
         let indicator = JXSegmentedIndicatorLineView()
@@ -181,7 +181,7 @@ extension TSWallpaperVC {
     
     func getCurrentDiyPreviewColor() -> UIColor? {
         if let model = viewModel.templateModels.safeObj(At: segmentedView.selectedIndex) {
-            return model.textColor?.color
+            return model.textColor?.uiColor
         }
         return nil
     }

+ 5 - 6
AIEmoji/Business/TSWallpaperVC/TSDiyKeyboardViewVC/View/TSKeyboardView.swift

@@ -6,7 +6,6 @@
 //
 
 import SwiftUI
-import SwiftUIX
 import Combine
 struct TSKeyboardView : View {
     
@@ -63,8 +62,8 @@ struct TSKeyboardItemView : View {
 
                 Spacer().frame(height: 2)
             }
-            .background("#F1F4F8".color.color)
-            .cornerRadius(.allCorners, 5)
+            .background("#F1F4F8".uiColor.color)
+            .cornerRadius(5)
             .shadow(color: .black.opacity(0.2), radius: 2, x: 0, y: 2) // 设置阴影
             
         }
@@ -76,13 +75,13 @@ struct TSKeyboardDeleteView : View {
     var model:DiyStickerModel
     var body: some View {
         return HStack {
-            Spacer().frame(width: 14).background(.red)
+            Spacer().frame(width: 14).background(Color.red)
             VStack {
                 Image(model.imageUrl ?? "").resizable().frame(width: 23, height: 17)
             }
             .frame(width: 42,height: 42)
-            .background("#F1F4F8".color.color)
-            .cornerRadius(.allCorners, 5)
+            .background("#F1F4F8".uiColor.color)
+            .cornerRadius(5)
             .shadow(color: .black.opacity(0.2), radius: 2, x: 0, y: 2) // 设置阴影
         }
     }

+ 45 - 0
AIEmoji/Business/VIewTool/TSPageNullView.swift

@@ -0,0 +1,45 @@
+//
+//  TSPageNullView.swift
+//  AIEmoji
+//
+//  Created by 100Years on 2025/2/13.
+//
+
+
+class TSPageNullView: TSBaseView {
+
+    lazy var imageView: UIImageView = {
+        return UIImageView.createImageView(imageName: "pageNull")
+    }()
+    
+    lazy var titleLabel: UILabel = {
+        let titleLabel = UILabel.createLabel(text:"No record".localized ,font:.font(size: 14.0),textColor: .white,textAlignment: .center,numberOfLines: 0)
+        return titleLabel
+    }()
+    
+    
+
+    override func creatUI() {
+        
+        self.frame = CGRectMake(0, 0, k_ScreenWidth, k_ScreenHeight - k_Nav_Height)
+        
+        contentView.addSubview(imageView)
+        imageView.snp.makeConstraints { make in
+            make.top.equalTo(151)
+            make.centerX.equalToSuperview()
+            make.width.height.equalTo(120)
+        }
+
+        contentView.addSubview(titleLabel)
+        titleLabel.snp.makeConstraints { make in
+            make.top.equalTo(imageView.snp.bottom)
+            make.leading.equalTo(16)
+            make.trailing.equalTo(-16)
+            make.height.equalTo(20)
+        }
+
+    }
+    
+    
+    
+}

+ 1 - 1
AIEmoji/Business/VIewTool/TSViewTool.swift

@@ -51,7 +51,7 @@ class TSSavePhotoSuccessTool {
     
     private lazy var textLabel:UILabel = {
         let textLabel = UILabel()
-        textLabel.textColor = "#4A5178".color
+        textLabel.textColor = "#4A5178".uiColor
         textLabel.text = "Save Successfully".localized
         textLabel.font = UIFont.font(size: 14)
         return textLabel

Some files were not shown because too many files changed in this diff