Commit 5484e5d6 authored by pete谢兆麟's avatar pete谢兆麟

Merge branch 'yinhe-live-1212' of...

Merge branch 'yinhe-live-1212' of http://gitlab.galaxy-immi.com/mobile-group/galaxy-iOS into yinhe-live-1212
parents b92e0670 c7a97787
...@@ -858,6 +858,8 @@ ...@@ -858,6 +858,8 @@
04F957452C1FEC4A003C631C /* YHMessageBellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F957442C1FEC4A003C631C /* YHMessageBellView.swift */; }; 04F957452C1FEC4A003C631C /* YHMessageBellView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F957442C1FEC4A003C631C /* YHMessageBellView.swift */; };
04F957472C203033003C631C /* YHMyGoodFriendsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F957462C203033003C631C /* YHMyGoodFriendsVC.swift */; }; 04F957472C203033003C631C /* YHMyGoodFriendsVC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F957462C203033003C631C /* YHMyGoodFriendsVC.swift */; };
04F9574B2C2032D8003C631C /* YHMyFriendsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9574A2C2032D8003C631C /* YHMyFriendsCell.swift */; }; 04F9574B2C2032D8003C631C /* YHMyFriendsCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F9574A2C2032D8003C631C /* YHMyFriendsCell.swift */; };
04F98A2F2D00543500B1CC0A /* YHLiveCreateOrderModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F98A2E2D00543500B1CC0A /* YHLiveCreateOrderModel.swift */; };
04F98A312D00889C00B1CC0A /* YHVideoProgressControl.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04F98A302D00889C00B1CC0A /* YHVideoProgressControl.swift */; };
04FA8B2B2C06F59D00ABE43F /* YHAppleLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FA8B2A2C06F59D00ABE43F /* YHAppleLoginManager.swift */; }; 04FA8B2B2C06F59D00ABE43F /* YHAppleLoginManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FA8B2A2C06F59D00ABE43F /* YHAppleLoginManager.swift */; };
04FA8B2E2C084C7E00ABE43F /* ATAuthSDK.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 04FA8B2D2C084C7E00ABE43F /* ATAuthSDK.bundle */; }; 04FA8B2E2C084C7E00ABE43F /* ATAuthSDK.bundle in Resources */ = {isa = PBXBuildFile; fileRef = 04FA8B2D2C084C7E00ABE43F /* ATAuthSDK.bundle */; };
04FA8B302C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FA8B2F2C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift */; }; 04FA8B302C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 04FA8B2F2C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift */; };
...@@ -1969,6 +1971,8 @@ ...@@ -1969,6 +1971,8 @@
04F957442C1FEC4A003C631C /* YHMessageBellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMessageBellView.swift; sourceTree = "<group>"; }; 04F957442C1FEC4A003C631C /* YHMessageBellView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMessageBellView.swift; sourceTree = "<group>"; };
04F957462C203033003C631C /* YHMyGoodFriendsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMyGoodFriendsVC.swift; sourceTree = "<group>"; }; 04F957462C203033003C631C /* YHMyGoodFriendsVC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMyGoodFriendsVC.swift; sourceTree = "<group>"; };
04F9574A2C2032D8003C631C /* YHMyFriendsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMyFriendsCell.swift; sourceTree = "<group>"; }; 04F9574A2C2032D8003C631C /* YHMyFriendsCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHMyFriendsCell.swift; sourceTree = "<group>"; };
04F98A2E2D00543500B1CC0A /* YHLiveCreateOrderModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHLiveCreateOrderModel.swift; sourceTree = "<group>"; };
04F98A302D00889C00B1CC0A /* YHVideoProgressControl.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHVideoProgressControl.swift; sourceTree = "<group>"; };
04FA8B2A2C06F59D00ABE43F /* YHAppleLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHAppleLoginManager.swift; sourceTree = "<group>"; }; 04FA8B2A2C06F59D00ABE43F /* YHAppleLoginManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHAppleLoginManager.swift; sourceTree = "<group>"; };
04FA8B2D2C084C7E00ABE43F /* ATAuthSDK.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = ATAuthSDK.bundle; path = "galaxy/Classes/Modules/AutoLogin(一键登录)/framework/ATAuthSDK_D.framework/ATAuthSDK.bundle"; sourceTree = "<group>"; }; 04FA8B2D2C084C7E00ABE43F /* ATAuthSDK.bundle */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.plug-in"; name = ATAuthSDK.bundle; path = "galaxy/Classes/Modules/AutoLogin(一键登录)/framework/ATAuthSDK_D.framework/ATAuthSDK.bundle"; sourceTree = "<group>"; };
04FA8B2F2C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHOneKeyLoginViewModel.swift; sourceTree = "<group>"; }; 04FA8B2F2C0874CA00ABE43F /* YHOneKeyLoginViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = YHOneKeyLoginViewModel.swift; sourceTree = "<group>"; };
...@@ -3077,6 +3081,7 @@ ...@@ -3077,6 +3081,7 @@
04013E412CFADF6B001A8E40 /* YHShareAlertView.swift */, 04013E412CFADF6B001A8E40 /* YHShareAlertView.swift */,
04013E452CFDA9AD001A8E40 /* YHLiveShopView.swift */, 04013E452CFDA9AD001A8E40 /* YHLiveShopView.swift */,
0413A9B02CFFDB9A00304BC6 /* YHCategoryDropdownView.swift */, 0413A9B02CFFDB9A00304BC6 /* YHCategoryDropdownView.swift */,
04F98A302D00889C00B1CC0A /* YHVideoProgressControl.swift */,
); );
path = V; path = V;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -3088,6 +3093,7 @@ ...@@ -3088,6 +3093,7 @@
04564D692CF6C0FC004456E4 /* YHLiveDetailModel.swift */, 04564D692CF6C0FC004456E4 /* YHLiveDetailModel.swift */,
04564D6D2CF6EB3D004456E4 /* YHHuanXinUserModel.swift */, 04564D6D2CF6EB3D004456E4 /* YHHuanXinUserModel.swift */,
04564D6F2CF6EC8A004456E4 /* YHRecordedDetailModel.swift */, 04564D6F2CF6EC8A004456E4 /* YHRecordedDetailModel.swift */,
04F98A2E2D00543500B1CC0A /* YHLiveCreateOrderModel.swift */,
); );
path = M; path = M;
sourceTree = "<group>"; sourceTree = "<group>";
...@@ -6385,6 +6391,7 @@ ...@@ -6385,6 +6391,7 @@
045EEEDF2B9F171A0022A143 /* YHCollegeSearchBar.swift in Sources */, 045EEEDF2B9F171A0022A143 /* YHCollegeSearchBar.swift in Sources */,
0471BF622CBA9046003B7942 /* YHHKVisaRenewalPersonType.swift in Sources */, 0471BF622CBA9046003B7942 /* YHHKVisaRenewalPersonType.swift in Sources */,
044D0BEE2C2019C100C5CF5E /* YHCommunityViewController.swift in Sources */, 044D0BEE2C2019C100C5CF5E /* YHCommunityViewController.swift in Sources */,
04F98A312D00889C00B1CC0A /* YHVideoProgressControl.swift in Sources */,
A567E5B52BD7643D00D5D5A0 /* YHSearchInfoBar.swift in Sources */, A567E5B52BD7643D00D5D5A0 /* YHSearchInfoBar.swift in Sources */,
044EE2462C93E22E00A2FE3A /* YHResignCertificateDetailHkViewController.swift in Sources */, 044EE2462C93E22E00A2FE3A /* YHResignCertificateDetailHkViewController.swift in Sources */,
A5ACE94B2B4564F7002C94D2 /* YHHUDRotatingImageView.swift in Sources */, A5ACE94B2B4564F7002C94D2 /* YHHUDRotatingImageView.swift in Sources */,
...@@ -6963,6 +6970,7 @@ ...@@ -6963,6 +6970,7 @@
045EEE872B9F171A0022A143 /* YHPreviewControllerHoldViewController.swift in Sources */, 045EEE872B9F171A0022A143 /* YHPreviewControllerHoldViewController.swift in Sources */,
0419A0A22C49099A00A5FCFA /* YHInvitationWithGiftsShareBottomView.swift in Sources */, 0419A0A22C49099A00A5FCFA /* YHInvitationWithGiftsShareBottomView.swift in Sources */,
04AAA8DC2BF5E5A200FE9FD5 /* YHEmptyDataView.swift in Sources */, 04AAA8DC2BF5E5A200FE9FD5 /* YHEmptyDataView.swift in Sources */,
04F98A2F2D00543500B1CC0A /* YHLiveCreateOrderModel.swift in Sources */,
044F39502CB80706007CA277 /* YHVisaRenewalPayStatusCell.swift in Sources */, 044F39502CB80706007CA277 /* YHVisaRenewalPayStatusCell.swift in Sources */,
A5A89FD72C256B94005A71DD /* YHHomeLastMessageModel.swift in Sources */, A5A89FD72C256B94005A71DD /* YHHomeLastMessageModel.swift in Sources */,
045EEF172B9F171A0022A143 /* YHSelectCountryViewController.swift in Sources */, 045EEF172B9F171A0022A143 /* YHSelectCountryViewController.swift in Sources */,
...@@ -7160,7 +7168,7 @@ ...@@ -7160,7 +7168,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxyTestEnv.entitlements; CODE_SIGN_ENTITLEMENTS = galaxy/galaxyTestEnv.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RXHYW88XR7; DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
...@@ -7302,7 +7310,7 @@ ...@@ -7302,7 +7310,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements; CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RXHYW88XR7; DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
...@@ -7507,7 +7515,7 @@ ...@@ -7507,7 +7515,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxyDebug.entitlements; CODE_SIGN_ENTITLEMENTS = galaxy/galaxyDebug.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RXHYW88XR7; DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
...@@ -7554,7 +7562,7 @@ ...@@ -7554,7 +7562,7 @@
CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements; CODE_SIGN_ENTITLEMENTS = galaxy/galaxy.entitlements;
CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 2; CURRENT_PROJECT_VERSION = 3;
DEVELOPMENT_TEAM = RXHYW88XR7; DEVELOPMENT_TEAM = RXHYW88XR7;
ENABLE_USER_SCRIPT_SANDBOXING = NO; ENABLE_USER_SCRIPT_SANDBOXING = NO;
FRAMEWORK_SEARCH_PATHS = ( FRAMEWORK_SEARCH_PATHS = (
......
...@@ -20,7 +20,7 @@ class YHAutoTextView: UITextView, UITextViewDelegate { ...@@ -20,7 +20,7 @@ class YHAutoTextView: UITextView, UITextViewDelegate {
} }
} }
let maxHeight = 60.0 let maxHeight = 80.0
static let verticalGap = 5.0 static let verticalGap = 5.0
var placeHolder: String = "" { var placeHolder: String = "" {
didSet { didSet {
......
...@@ -190,14 +190,19 @@ class YHAIMainChatViewController: YHBaseViewController { ...@@ -190,14 +190,19 @@ class YHAIMainChatViewController: YHBaseViewController {
} }
@objc func didCleanButtonClicked() { @objc func didCleanButtonClicked() {
self.manager.clearGlobalConverastionId()
self.viewModel.requestConversationId { sessionId in YHCommonAlertView.show("删除历史记录", "删除后记录无法恢复", "取消", "删除", fullGuestureEnable: false) {
if !sessionId.isEmpty {
self.conversationId = sessionId } callBack: {
self.manager.saveGlobalConverastionId(sessionId) self.manager.clearGlobalConverastionId()
self.messages.removeAll() self.viewModel.requestConversationId { sessionId in
self.tableView.reloadData() if !sessionId.isEmpty {
YHHUD.flash(message: "清除成功") self.conversationId = sessionId
self.manager.saveGlobalConverastionId(sessionId)
self.messages.removeAll()
self.tableView.reloadData()
YHHUD.flash(message: "清除成功")
}
} }
} }
} }
......
...@@ -221,11 +221,11 @@ class YHAIRequestManager: NSObject { ...@@ -221,11 +221,11 @@ class YHAIRequestManager: NSObject {
if sessionDone { if sessionDone {
print("SESSION DONE") print("SESSION DONE")
} }
if receiveMessage.isFinishd() { if receiveMessage.isCompleted() {
// 一段话结束需要重新生成uuid 来 // 一段话结束需要重新生成uuid 来
self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description self.uuid = UUID().uuidString + NSDate().timeIntervalSince1970.description
} }
if !receiveMessage.body.isStart() && !receiveMessage.isFinishd() { if !receiveMessage.isNotUserfulMessage() {
completion?(receiveMessage, sessionDone) completion?(receiveMessage, sessionDone)
} }
} }
......
...@@ -12,11 +12,13 @@ import JXSegmentedView ...@@ -12,11 +12,13 @@ import JXSegmentedView
class YHAIRobotChatViewController: YHBaseViewController { class YHAIRobotChatViewController: YHBaseViewController {
var myTitle: String = ""
var robotId: String = "" var robotId: String = ""
var conversationId: String = "" var conversationId: String = ""
var messages:[YHAIChatMessage] = [] var messages:[YHAIChatMessage] = []
var historyLastMessageId: String = "" var historyLastMessageId: String = ""
var isNeedShowBannerHeader: Bool = false
var robotType: Int = 0
let manager = YHAIRequestManager() let manager = YHAIRequestManager()
let viewModel = YHAIViewModel() let viewModel = YHAIViewModel()
...@@ -97,7 +99,7 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -97,7 +99,7 @@ class YHAIRobotChatViewController: YHBaseViewController {
view.backgroundColor = .clear view.backgroundColor = .clear
view.addSubview(bgImgView) view.addSubview(bgImgView)
gk_navTitle = "新港生活规划师" gk_navTitle = myTitle
gk_navTitleColor = .mainTextColor gk_navTitleColor = .mainTextColor
gk_navigationBar.addSubview(cleanBtn) gk_navigationBar.addSubview(cleanBtn)
...@@ -159,9 +161,13 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -159,9 +161,13 @@ class YHAIRobotChatViewController: YHBaseViewController {
// isPull 是否是下拉组件触发 // isPull 是否是下拉组件触发
func getHistoryMessages(_ isPull: Bool) { func getHistoryMessages(_ isPull: Bool) {
if !isPull { if !isPull {
YHHUD.show(.progress(message: "加载中...")) YHHUD.show(.progress(message: "加载中..."))
} else {
printLog("PULL MESSAGE HISTORY")
} }
viewModel.getHistoryChatMessages(botId: robotId, conversationId: conversationId, messageId: historyLastMessageId) { viewModel.getHistoryChatMessages(botId: robotId, conversationId: conversationId, messageId: historyLastMessageId) {
[weak self] success, error in [weak self] success, error in
YHHUD.hide() YHHUD.hide()
...@@ -206,17 +212,33 @@ class YHAIRobotChatViewController: YHBaseViewController { ...@@ -206,17 +212,33 @@ class YHAIRobotChatViewController: YHBaseViewController {
@objc func didCleanButtonClicked() { @objc func didCleanButtonClicked() {
self.manager.clearLobalSubRobotConversationIdForRobotId(robotId) YHCommonAlertView.show("删除历史记录", "删除后记录无法恢复", "取消", "删除", fullGuestureEnable: false) {
self.viewModel.requestConversationId { sessionId in
if !sessionId.isEmpty { } callBack: {
self.conversationId = sessionId self.manager.clearLobalSubRobotConversationIdForRobotId(self.robotId)
self.manager.saveLobalSubRobotConversationId(sessionId, robotId: self.robotId) self.viewModel.requestConversationId { sessionId in
self.messages.removeAll() if !sessionId.isEmpty {
self.tableView.reloadData() self.conversationId = sessionId
YHHUD.flash(message: "清除成功") self.manager.saveLobalSubRobotConversationId(sessionId, robotId: self.robotId)
self.messages.removeAll()
self.tableView.reloadData()
YHHUD.flash(message: "清除成功")
}
} }
} }
} }
func getFlowMessages() -> [String] {
if robotType == YHAYRobotType.education.rawValue {
return ["香港教育有哪些优势?", "去香港读书有哪些条件?", "申请香港学校费用是多少?", "了解银河教育插班服务流程", "了解银河教育插班录取率"]
} else if robotType == YHAYRobotType.sale.rawValue {
return ["优才学历加分要求是什么?", "优才现在要怎么申请?", "墨尔本大学是优才合资格大学吗?", "推荐一些优才产品"]
}
return []
}
} }
extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSource { extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSource {
...@@ -312,22 +334,61 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc ...@@ -312,22 +334,61 @@ extension YHAIRobotChatViewController: UITableViewDelegate, UITableViewDataSourc
} }
func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForHeaderInSection section: Int) -> UIView? {
if !isNeedShowBannerHeader {
return UIView()
}
let view = YHAIChatBannerView() let view = YHAIChatBannerView()
let arr:[YHBannerModel] = [YHBannerModel(), YHBannerModel(), YHBannerModel()] view.bannerArr = self.getBannerForRobotType(robotType)
view.dataArr = arr view.messages = getFlowMessages()
view.items = ["阿萨法吉林师范", "阿萨法阿基德拉屎就发了阿基德拉屎就发了阿基德拉屎就发了阿基德拉屎就发了阿基德拉屎就发了阿基德拉屎就发了阿基德拉屎就发了", "啊设计立卡", "啊积", "阿基德拉屎就发了", "asfasf", "阿萨法", "啊设计立卡", "啊积", "阿基德拉屎就发了"] view.selectFlowMsgBlock = {
[weak self] text in
guard let self = self else { return }
self.sendMessage(text)
}
view.selectBannerItemBlock = {
[weak self] model in
guard let self = self else { return }
var text = ""
if !model.msg.isEmpty {
text = model.msg
} else if !model.desc.isEmpty {
text = model.desc
}
self.sendMessage(text)
}
return view return view
} }
func getBannerForRobotType(_ robotType: Int) -> [YHAIChatBannerItem] {
if robotType == YHAYRobotType.sale.rawValue {
return [YHAIChatBannerItem(id: 0, title: "了解银河集团", desc: "香港身份生活一站式服务平台", msg: "银河集团,能够为我提供什么?"),
YHAIChatBannerItem(id: 1, title: "香港身份智能评估", desc: "60s快速评估,了解自身条件是否符合", msg: "开始身份办理评估"),
YHAIChatBannerItem(id: 2, title: "银河产品矩阵", desc: "香港身份、生活多样产品", msg: "介绍一下银河的产品"),]
}
if robotType == YHAYRobotType.education.rawValue {
return [YHAIChatBannerItem(id: 0, title: "幼中小学升学", desc: "去香港插班需要考核哪些"),
YHAIChatBannerItem(id: 1, title: "大学升学", desc: "DSE分数和Alevel的换算关系"),
YHAIChatBannerItem(id: 2, title: "银河教育服务", desc: "银河教育插班成功率如何?"),]
}
return []
}
func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? { func tableView(_ tableView: UITableView, viewForFooterInSection section: Int) -> UIView? {
let view = UIView() let view = UIView()
return view return view
} }
func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat { func tableView(_ tableView: UITableView, heightForHeaderInSection section: Int) -> CGFloat {
if !isNeedShowBannerHeader {
return 1.0
}
return 360.0 return 360.0
} }
} }
extension YHAIRobotChatViewController: JXSegmentedListContainerViewListDelegate { extension YHAIRobotChatViewController: JXSegmentedListContainerViewListDelegate {
......
...@@ -143,8 +143,11 @@ extension YHAIServiceListViewController: UICollectionViewDelegate, UICollectionV ...@@ -143,8 +143,11 @@ extension YHAIServiceListViewController: UICollectionViewDelegate, UICollectionV
self.manager.getSubRobotChatConversationId(robotId: model.botId) { self.manager.getSubRobotChatConversationId(robotId: model.botId) {
sesseionId in sesseionId in
let vc = YHAIRobotChatViewController() let vc = YHAIRobotChatViewController()
vc.isNeedShowBannerHeader = model.isNeedShowBannerHeader()
vc.myTitle = model.title
vc.robotId = model.botId vc.robotId = model.botId
vc.conversationId = sesseionId vc.conversationId = sesseionId
vc.robotType = model.botType
self.navigationController?.pushViewController(vc, animated: true) self.navigationController?.pushViewController(vc, animated: true)
} }
} }
......
...@@ -19,7 +19,7 @@ class YHAIHistoryMessage: SmartCodable { ...@@ -19,7 +19,7 @@ class YHAIHistoryMessage: SmartCodable {
var role: String = "" var role: String = ""
var type: String = "" var type: String = ""
var cardsInfo: YHAIListinfoModel? var cardsInfo: YHAIListInfoModel?
required init() { required init() {
...@@ -39,7 +39,7 @@ class YHAIHistoryMessage: SmartCodable { ...@@ -39,7 +39,7 @@ class YHAIHistoryMessage: SmartCodable {
body.type = self.type body.type = self.type
body.cardsInfo = self.cardsInfo body.cardsInfo = self.cardsInfo
msg.body = body msg.body = body
msg.setFinished() msg.setCompleted()
msg.updateBodyToData() msg.updateBodyToData()
return msg return msg
} }
......
...@@ -19,6 +19,12 @@ class YHAIListModel: SmartCodable { ...@@ -19,6 +19,12 @@ class YHAIListModel: SmartCodable {
} }
} }
enum YHAYRobotType: Int {
case house = 1
case education = 2
case sale = 3
}
class YHEntranceconfigModel: SmartCodable { class YHEntranceconfigModel: SmartCodable {
var id: Int = 0 var id: Int = 0
...@@ -29,8 +35,16 @@ class YHEntranceconfigModel: SmartCodable { ...@@ -29,8 +35,16 @@ class YHEntranceconfigModel: SmartCodable {
var btnText: String = "" var btnText: String = ""
var redirectMode: Int = 0 var redirectMode: Int = 0
var botId: String = "" var botId: String = ""
var botType: Int = 0 // 1:房产 2:教育 3:销售
var redirectPath: String = "" var redirectPath: String = ""
func isNeedShowBannerHeader() -> Bool {
if botType == YHAYRobotType.education.rawValue || botType == YHAYRobotType.sale.rawValue {
return true
}
return false
}
required init() { required init() {
} }
......
...@@ -30,27 +30,26 @@ enum YHAIMessageType: Int { ...@@ -30,27 +30,26 @@ enum YHAIMessageType: Int {
class YHAIChatMessage: CustomStringConvertible { class YHAIChatMessage: CustomStringConvertible {
let completeText = "conversation.chat.completed"
var id: String = "" var id: String = ""
var event: String = "" var event: String = ""
var data: String = "" var data: String = ""
var dataDict: [String : Any] = [:] var dataDict: [String : Any] = [:]
var messageId: String = "" var messageId: String = ""
var isSelf: Bool = false var isSelf: Bool = false
var isDone: Bool = false var isDone: Bool = false
var body = YHAIMessageBody() var body = YHAIMessageBody()
func isFinishd() -> Bool { func isCompleted() -> Bool {
if event.contains("conversation.message.completed") { if event.contains(completeText) {
return true return true
} }
return false return false
} }
func setFinished() { func setCompleted() {
event = "conversation.message.completed" event = self.completeText
} }
func isTextMessage() -> Bool { func isTextMessage() -> Bool {
...@@ -69,7 +68,7 @@ class YHAIChatMessage: CustomStringConvertible { ...@@ -69,7 +68,7 @@ class YHAIChatMessage: CustomStringConvertible {
let body = YHAIMessageBody() let body = YHAIMessageBody()
body.contentType = 1 body.contentType = 1
body.contentText = text body.contentText = text
question.setFinished() question.setCompleted()
question.body = body question.body = body
question.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description question.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
question.updateBodyToData() question.updateBodyToData()
...@@ -81,7 +80,7 @@ class YHAIChatMessage: CustomStringConvertible { ...@@ -81,7 +80,7 @@ class YHAIChatMessage: CustomStringConvertible {
message.isSelf = false message.isSelf = false
let body = YHAIMessageBody() let body = YHAIMessageBody()
body.contentType = YHAIMessageType.thinking.rawValue body.contentType = YHAIMessageType.thinking.rawValue
message.setFinished() message.setCompleted()
message.body = body message.body = body
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.updateBodyToData() message.updateBodyToData()
...@@ -95,7 +94,7 @@ class YHAIChatMessage: CustomStringConvertible { ...@@ -95,7 +94,7 @@ class YHAIChatMessage: CustomStringConvertible {
let body = YHAIMessageBody() let body = YHAIMessageBody()
body.contentType = YHAIMessageType.text.rawValue body.contentType = YHAIMessageType.text.rawValue
body.contentText = text body.contentText = text
message.setFinished() message.setCompleted()
message.body = body message.body = body
message.updateBodyToData() message.updateBodyToData()
return message return message
...@@ -106,7 +105,7 @@ class YHAIChatMessage: CustomStringConvertible { ...@@ -106,7 +105,7 @@ class YHAIChatMessage: CustomStringConvertible {
message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description message.messageId = UUID().uuidString + NSDate().timeIntervalSince1970.description
message.isSelf = false message.isSelf = false
let body = YHAIMessageBody() let body = YHAIMessageBody()
message.setFinished() message.setCompleted()
body.contentType = YHAIMessageType.picture.rawValue body.contentType = YHAIMessageType.picture.rawValue
let imgInfo = YHAIImageInfo() let imgInfo = YHAIImageInfo()
imgInfo.imageUrl = url imgInfo.imageUrl = url
...@@ -133,6 +132,14 @@ class YHAIChatMessage: CustomStringConvertible { ...@@ -133,6 +132,14 @@ class YHAIChatMessage: CustomStringConvertible {
return "" return ""
} }
//
func isNotUserfulMessage() -> Bool {
if !self.body.isStart() && !self.isCompleted() && !self.body.isDone() {
return true
}
return false
}
// 获取消息类型 // 获取消息类型
func getType() -> YHAIMessageType { func getType() -> YHAIMessageType {
if let contentType = dataDict["contentType"] as? Int { if let contentType = dataDict["contentType"] as? Int {
...@@ -187,7 +194,7 @@ class YHAIMessageBody: SmartCodable { ...@@ -187,7 +194,7 @@ class YHAIMessageBody: SmartCodable {
var botId: String = "" var botId: String = ""
var status: String = "" var status: String = ""
var type: String = "" var type: String = ""
var cardsInfo: YHAIListinfoModel? var cardsInfo: YHAIListInfoModel?
var imageInfo: YHAIImageInfo? var imageInfo: YHAIImageInfo?
func isStart() -> Bool { func isStart() -> Bool {
...@@ -213,17 +220,24 @@ class YHAIMessageBody: SmartCodable { ...@@ -213,17 +220,24 @@ class YHAIMessageBody: SmartCodable {
// } // }
} }
class YHAIListinfoModel: SmartCodable { enum YHAIJumpPageType: String {
case customerHeart = "customerVoice" // APP客户心声
case galaxySelect = "productList" // APP-首页银河甄选
}
class YHAIListInfoModel: SmartCodable {
var title: String = "" var title: String = ""
var icon: String = "" var icon: String = ""
var description: String = "" var description: String = ""
var btnText: String = "" var btnText: String = ""
var list: [YHAIListItemModel] = [] var list: [YHAIListItemModel] = []
var redirectMode: Int = 0 var redirectMode: Int = 0 // 跳转模式 0: 不跳转 1:web页面跳转 2:APP跳转 3:Agent跳转
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
var redirectPath: String = "" var redirectPath: String = ""
var businessType: String = "" var businessType: String = ""
// 是否是测评 // 是否是测评
func isEvaluation() -> Bool { func isEvaluation() -> Bool {
return businessType == "evaluation" return businessType == "evaluation"
...@@ -239,11 +253,12 @@ class YHAIListItemModel: SmartCodable { ...@@ -239,11 +253,12 @@ class YHAIListItemModel: SmartCodable {
var title: String = "" var title: String = ""
var cover: String = "" var cover: String = ""
var description: String = "" var description: String = ""
var redirectMode: Int = 0 var redirectMode: Int = 0 // 跳转模式 0: 不跳转 1:web页面跳转 2:APP跳转 3:Agent跳转
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
var redirectPath: String = "" var redirectPath: String = ""
var tags: [YHProductTag] = [] var tags: [YHProductTag] = []
var original_price: Int = 0 var originalPrice: Int = 0
var discount_price: Int = 0 var discountPrice: Int = 0
required init() { required init() {
......
...@@ -60,6 +60,9 @@ class YHAICardItemView: UIView { ...@@ -60,6 +60,9 @@ class YHAICardItemView: UIView {
func createUI() { func createUI() {
let tap = UITapGestureRecognizer(target: self, action: #selector(didItemViewClicked))
self.addGestureRecognizer(tap)
self.addSubview(lineView) self.addSubview(lineView)
self.addSubview(cardImgView) self.addSubview(cardImgView)
self.addSubview(cardTitleLabel) self.addSubview(cardTitleLabel)
...@@ -91,6 +94,12 @@ class YHAICardItemView: UIView { ...@@ -91,6 +94,12 @@ class YHAICardItemView: UIView {
make.right.equalTo(-16) make.right.equalTo(-16)
make.bottom.equalTo(-16) make.bottom.equalTo(-16)
} }
}
@objc func didItemViewClicked() {
YHAIJumpPageTool.jumpPageWithType(0, mode: cardModel.redirectMode, path: cardModel.redirectPath) {
dict in
}
} }
} }
...@@ -12,6 +12,15 @@ class YHAIChatBannerCollectionCell: UICollectionViewCell { ...@@ -12,6 +12,15 @@ class YHAIChatBannerCollectionCell: UICollectionViewCell {
static let cellReuseIdentifier = "YHAIChatBannerCollectionCell" static let cellReuseIdentifier = "YHAIChatBannerCollectionCell"
lazy var bgView: UIView = {
let v = UIView()
v.backgroundColor = .white
v.backgroundColor = .white
v.layer.cornerRadius = 6.0
v.clipsToBounds = true
return v
}()
lazy var titleLabel: UILabel = { lazy var titleLabel: UILabel = {
var label = UILabel() var label = UILabel()
label.font = .PFSC_R(ofSize: 12) label.font = .PFSC_R(ofSize: 12)
...@@ -36,11 +45,13 @@ class YHAIChatBannerCollectionCell: UICollectionViewCell { ...@@ -36,11 +45,13 @@ class YHAIChatBannerCollectionCell: UICollectionViewCell {
private func setupUI() { private func setupUI() {
contentView.backgroundColor = .white
contentView.layer.cornerRadius = 6.0
contentView.addSubview(shadowView) contentView.addSubview(shadowView)
contentView.addSubview(titleLabel) contentView.addSubview(bgView)
bgView.addSubview(titleLabel)
bgView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
shadowView.snp.makeConstraints { make in shadowView.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
......
...@@ -12,6 +12,13 @@ import VisualEffectView ...@@ -12,6 +12,13 @@ import VisualEffectView
class YHAIChatBannerItemCell: FSPagerViewCell { class YHAIChatBannerItemCell: FSPagerViewCell {
var model = YHAIChatBannerItem() {
didSet {
titleLabel.text = model.title
subtitleLabel.text = model.desc
}
}
static let cellReuseIdentifier = "YHAIChatBannerItemCell" static let cellReuseIdentifier = "YHAIChatBannerItemCell"
lazy var effectView:VisualEffectView = { lazy var effectView:VisualEffectView = {
...@@ -22,14 +29,6 @@ class YHAIChatBannerItemCell: FSPagerViewCell { ...@@ -22,14 +29,6 @@ class YHAIChatBannerItemCell: FSPagerViewCell {
return visualEffectView return visualEffectView
}() }()
lazy var bannerImagV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "global_default_image")
return imagV
}()
lazy var titleLabel: UILabel = { lazy var titleLabel: UILabel = {
let lable = UILabel() let lable = UILabel()
lable.font = UIFont.PFSC_B(ofSize: 14) lable.font = UIFont.PFSC_B(ofSize: 14)
...@@ -62,11 +61,6 @@ class YHAIChatBannerItemCell: FSPagerViewCell { ...@@ -62,11 +61,6 @@ class YHAIChatBannerItemCell: FSPagerViewCell {
contentView.layer.shadowOpacity = 0 contentView.layer.shadowOpacity = 0
contentView.layer.shadowOffset = .zero contentView.layer.shadowOffset = .zero
contentView.addSubview(bannerImagV)
bannerImagV.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
contentView.addSubview(effectView) contentView.addSubview(effectView)
effectView.snp.makeConstraints { make in effectView.snp.makeConstraints { make in
make.bottom.left.right.equalToSuperview() make.bottom.left.right.equalToSuperview()
......
...@@ -9,23 +9,63 @@ ...@@ -9,23 +9,63 @@
import UIKit import UIKit
import FSPagerView import FSPagerView
class YHAIChatBannerItem {
var id: Int = 0
var title: String = ""
var desc: String = ""
var msg: String = ""
required init(id: Int = 0, title: String = "", desc: String = "", msg: String = "") {
self.id = id
self.title = title
self.desc = desc
self.msg = msg
}
}
class YHAIChatBannerView: UIView { class YHAIChatBannerView: UIView {
static let bannerHeight = 242.0 static let bannersHeight = 95.0
let cellHeight: CGFloat = 33.0 // 单元格的固定高度 let cellHeight: CGFloat = 33.0 // 单元格的固定高度
var selectFlowMsgBlock:((String)->())?
var selectBannerItemBlock:((YHAIChatBannerItem)->())?
var items:[String] = [] { var messages:[String] = [] {
didSet { didSet {
layout.dataSource = items layout.dataSource = messages
collectionView.reloadData() collectionView.reloadData()
} }
} }
var dataArr: [YHBannerModel] = [] { lazy var bgImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_header_bg")
return imagV
}()
lazy var rolerImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_header_role")
return imagV
}()
lazy var arrowImgV: UIImageView = {
let imagV : UIImageView = UIImageView()
imagV.contentMode = .scaleAspectFill
imagV.clipsToBounds = true
imagV.image = UIImage(named: "ai_chat_heaer_banner_arrow")
return imagV
}()
var bannerArr: [YHAIChatBannerItem] = [] {
didSet { didSet {
// 设置为0是先停掉自动滑动定时器 // 设置为0是先停掉自动滑动定时器
bannerView.automaticSlidingInterval = 0 bannerView.automaticSlidingInterval = 0
self.indicatorView.indicatorItems = self.dataArr.count self.indicatorView.indicatorItems = self.bannerArr.count
bannerView.reloadData() bannerView.reloadData()
// 指定指示器为第一个 // 指定指示器为第一个
self.indicatorView.curIndicatorIndex = 0 self.indicatorView.curIndicatorIndex = 0
...@@ -46,12 +86,16 @@ class YHAIChatBannerView: UIView { ...@@ -46,12 +86,16 @@ class YHAIChatBannerView: UIView {
view.isInfinite = true view.isInfinite = true
view.automaticSlidingInterval = bannerSildingInterval view.automaticSlidingInterval = bannerSildingInterval
view.register(YHAIChatBannerItemCell.self, forCellWithReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier) view.register(YHAIChatBannerItemCell.self, forCellWithReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier)
view.itemSize = CGSizeMake(KScreenWidth-40.0, YHAIChatBannerView.bannerHeight)//FSPagerView.automaticSize view.itemSize = CGSizeMake(KScreenWidth-40.0, YHAIChatBannerView.bannersHeight)//FSPagerView.automaticSize
view.layer.cornerRadius = 4.0
view.clipsToBounds = true
return view return view
}() }()
lazy var indicatorView : YHHomeBannerIndicatorView = { lazy var indicatorView : YHHomeBannerIndicatorView = {
let view = YHHomeBannerIndicatorView() let view = YHHomeBannerIndicatorView()
view.normalColor = .init(hex: 0x6D788A)
view.selectedColor = .brandMainColor
return view return view
}() }()
...@@ -84,15 +128,34 @@ class YHAIChatBannerView: UIView { ...@@ -84,15 +128,34 @@ class YHAIChatBannerView: UIView {
} }
func createUI() { func createUI() {
addSubview(bgImgV)
addSubview(rolerImgV)
addSubview(bannerView) addSubview(bannerView)
bannerView.snp.makeConstraints { make in bannerView.addSubview(indicatorView)
make.top.equalTo(10) bannerView.addSubview(arrowImgV)
bgImgV.snp.makeConstraints { make in
make.top.equalTo(0)
make.left.equalTo(20) make.left.equalTo(20)
make.right.equalTo(-20) make.right.equalTo(-20)
make.height.equalTo(YHAIChatBannerView.bannerHeight) make.height.equalTo(242)
}
rolerImgV.snp.makeConstraints { make in
make.top.equalTo(0)
make.right.equalTo(-10)
make.width.equalTo(200)
make.height.equalTo(180)
}
bannerView.snp.makeConstraints { make in
make.bottom.equalTo(bgImgV)
make.left.equalTo(22)
make.right.equalTo(-22)
make.height.equalTo(YHAIChatBannerView.bannersHeight)
} }
bannerView.addSubview(indicatorView)
indicatorView.snp.makeConstraints { make in indicatorView.snp.makeConstraints { make in
make.left.right.equalToSuperview() make.left.right.equalToSuperview()
make.height.equalTo(2) make.height.equalTo(2)
...@@ -100,6 +163,13 @@ class YHAIChatBannerView: UIView { ...@@ -100,6 +163,13 @@ class YHAIChatBannerView: UIView {
} }
bannerView.reloadData() bannerView.reloadData()
arrowImgV.snp.makeConstraints { make in
make.right.equalTo(-20)
make.width.equalTo(12)
make.height.equalTo(4)
make.bottom.equalTo(-40)
}
addSubview(collectionView) addSubview(collectionView)
collectionView.snp.makeConstraints { make in collectionView.snp.makeConstraints { make in
make.left.equalTo(20) make.left.equalTo(20)
...@@ -123,12 +193,14 @@ class YHAIChatBannerView: UIView { ...@@ -123,12 +193,14 @@ class YHAIChatBannerView: UIView {
extension YHAIChatBannerView: FSPagerViewDataSource, FSPagerViewDelegate { extension YHAIChatBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
public func numberOfItems(in pagerView: FSPagerView) -> Int { public func numberOfItems(in pagerView: FSPagerView) -> Int {
return self.dataArr.count return self.bannerArr.count
} }
public func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell { public func pagerView(_ pagerView: FSPagerView, cellForItemAt index: Int) -> FSPagerViewCell {
let cell = pagerView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier, at: index) as! YHAIChatBannerItemCell let cell = pagerView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerItemCell.cellReuseIdentifier, at: index) as! YHAIChatBannerItemCell
if index < dataArr.count { if index < bannerArr.count {
let model = bannerArr[index]
cell.model = model
} }
return cell return cell
} }
...@@ -137,7 +209,9 @@ extension YHAIChatBannerView: FSPagerViewDataSource, FSPagerViewDelegate { ...@@ -137,7 +209,9 @@ extension YHAIChatBannerView: FSPagerViewDataSource, FSPagerViewDelegate {
func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) { func pagerView(_ pagerView: FSPagerView, didSelectItemAt index: Int) {
pagerView.deselectItem(at: index, animated: true) pagerView.deselectItem(at: index, animated: true)
pagerView.scrollToItem(at: index, animated: true) pagerView.scrollToItem(at: index, animated: true)
if index >= dataArr.count { if 0 <= index, index < bannerArr.count {
let model = bannerArr[index]
selectBannerItemBlock?(model)
return return
} }
} }
...@@ -156,7 +230,7 @@ extension YHAIChatBannerView: UICollectionViewDelegate, UICollectionViewDataSour ...@@ -156,7 +230,7 @@ extension YHAIChatBannerView: UICollectionViewDelegate, UICollectionViewDataSour
// 返回单元格数量 // 返回单元格数量
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int { func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return items.count return messages.count
} }
// 返回每个单元格的大小 // 返回每个单元格的大小
...@@ -168,15 +242,16 @@ extension YHAIChatBannerView: UICollectionViewDelegate, UICollectionViewDataSour ...@@ -168,15 +242,16 @@ extension YHAIChatBannerView: UICollectionViewDelegate, UICollectionViewDataSour
// 返回自定义单元格 // 返回自定义单元格
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell { func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerCollectionCell.cellReuseIdentifier, for: indexPath) as! YHAIChatBannerCollectionCell let cell = collectionView.dequeueReusableCell(withReuseIdentifier: YHAIChatBannerCollectionCell.cellReuseIdentifier, for: indexPath) as! YHAIChatBannerCollectionCell
if 0 <= indexPath.item && indexPath.item < items.count { if 0 <= indexPath.item && indexPath.item < messages.count {
cell.titleLabel.text = items[indexPath.item] cell.titleLabel.text = messages[indexPath.item]
} }
return cell return cell
} }
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) { func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
if 0 <= indexPath.item && indexPath.item < items.count { if 0 <= indexPath.item && indexPath.item < messages.count {
let text = items[indexPath.item] let text = messages[indexPath.item]
selectFlowMsgBlock?(text)
} }
} }
} }
...@@ -7,6 +7,7 @@ ...@@ -7,6 +7,7 @@
// //
import UIKit import UIKit
import Lottie
class YHAITextMessageCell: UITableViewCell { class YHAITextMessageCell: UITableViewCell {
...@@ -39,6 +40,7 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -39,6 +40,7 @@ class YHAITextMessageCell: UITableViewCell {
} }
isNeedShowCopyView = false isNeedShowCopyView = false
isNeedShowLoadingView = false
} else { } else {
...@@ -62,9 +64,12 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -62,9 +64,12 @@ class YHAITextMessageCell: UITableViewCell {
if message.getType() == .recommendText { if message.getType() == .recommendText {
isNeedShowCopyView = false isNeedShowCopyView = false
isNeedShowLoadingView = false
} else { } else {
isNeedShowCopyView = message.isDone isNeedShowCopyView = message.isDone
let complete = message.isCompleted() || message.isDone
isNeedShowLoadingView = !complete
} }
} }
...@@ -79,13 +84,25 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -79,13 +84,25 @@ class YHAITextMessageCell: UITableViewCell {
copyContentView.snp.remakeConstraints { make in copyContentView.snp.remakeConstraints { make in
make.left.equalTo(0) make.left.equalTo(0)
make.right.equalTo(0) make.right.equalTo(0)
make.top.equalTo(messageLabel.snp.bottom).offset(16) make.top.equalTo(loadingImgView.snp.bottom).offset(16)
make.height.equalTo(isNeedShowCopyView ? 37.0 : 0.0) make.height.equalTo(isNeedShowCopyView ? 37.0 : 0.0)
make.bottom.equalTo(0) make.bottom.equalTo(0)
} }
} }
} }
var isNeedShowLoadingView: Bool = false {
didSet {
loadingImgView.isHidden = !isNeedShowLoadingView
loadingImgView.snp.remakeConstraints { make in
make.right.equalTo(-16)
make.width.equalTo(26)
make.height.equalTo(isNeedShowLoadingView ? 18 : 0)
make.top.equalTo(messageLabel.snp.bottom).offset(isNeedShowLoadingView ? 6 : 0)
}
}
}
lazy var whiteContentView: UIView = { lazy var whiteContentView: UIView = {
let v = UIView() let v = UIView()
v.backgroundColor = .white v.backgroundColor = .white
...@@ -156,6 +173,14 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -156,6 +173,14 @@ class YHAITextMessageCell: UITableViewCell {
return v return v
}() }()
let loadingImgView: LottieAnimationView! = {
let lottieView = LottieAnimationView(name: "ai_chat_loading")
lottieView.loopMode = .loop
lottieView.contentMode = .scaleAspectFit
lottieView.play()
return lottieView
}()
@objc func didCopyTextButtonClicked() { @objc func didCopyTextButtonClicked() {
let text = message.body.contentText let text = message.body.contentText
let pasteBoard = UIPasteboard.general let pasteBoard = UIPasteboard.general
...@@ -186,6 +211,7 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -186,6 +211,7 @@ class YHAITextMessageCell: UITableViewCell {
contentView.addSubview(rightAngleView) contentView.addSubview(rightAngleView)
contentView.addSubview(whiteContentView) contentView.addSubview(whiteContentView)
whiteContentView.addSubview(messageLabel) whiteContentView.addSubview(messageLabel)
whiteContentView.addSubview(loadingImgView)
whiteContentView.addSubview(copyContentView) whiteContentView.addSubview(copyContentView)
shadowView.snp.makeConstraints { make in shadowView.snp.makeConstraints { make in
...@@ -210,9 +236,16 @@ class YHAITextMessageCell: UITableViewCell { ...@@ -210,9 +236,16 @@ class YHAITextMessageCell: UITableViewCell {
make.top.equalTo(16) make.top.equalTo(16)
} }
loadingImgView.snp.makeConstraints { make in
make.right.equalTo(-16)
make.width.equalTo(26)
make.height.equalTo(18)
make.top.equalTo(messageLabel.snp.bottom).offset(6)
}
copyContentView.snp.makeConstraints { make in copyContentView.snp.makeConstraints { make in
make.left.equalTo(0) make.left.equalTo(0)
make.top.equalTo(messageLabel.snp.bottom).offset(16) make.top.equalTo(loadingImgView.snp.bottom).offset(16)
make.height.equalTo(0) make.height.equalTo(0)
make.bottom.equalTo(0) make.bottom.equalTo(0)
} }
......
...@@ -8,13 +8,56 @@ ...@@ -8,13 +8,56 @@
import UIKit import UIKit
class YHAIJumpPageTool {
// type = 0 // 0 普通 1 测评
static func jumpPageWithType(_ type:Int = 0, mode: Int, path: String, block:(([String:Any])->())?) {
printLog("type:\(type) mdde:\(mode) path:\(path)")
if mode == 1 { //
if !path.isEmpty {
let vc = YHH5WebViewVC()
vc.url = path
if type == 1 {
vc.isPushed = false
vc.isFullScreenFlag = true
vc.isHideNavigationBar = true
vc.evaluationResultCallback = {
dict in
block?(dict)
UIViewController.current?.present(vc, animated: true)
}
} else {
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
}
}
} else if mode == 2 {
// customerVoice -> APP客户心声 productList -> APP-首页银河甄选
if path == YHAIJumpPageType.customerHeart.rawValue {
//客户心声
let vc = YHOtherServiceViewController()
vc.classID = 5
UIViewController.current?.navigationController?.pushViewController(vc)
} else if path == YHAIJumpPageType.galaxySelect.rawValue {
// 银河甄选
let vc = YHSelectViewController()
vc.hideFlag = false
UIViewController.current?.navigationController?.pushViewController(vc, animated: true)
}
}
}
}
class YHCardMessageCell: UITableViewCell { class YHCardMessageCell: UITableViewCell {
static let cellReuseIdentifier = "YHCardMessageCell" static let cellReuseIdentifier = "YHCardMessageCell"
var evaluationResultCallback: ((Dictionary<String, Any>)->()) = { dic in } var evaluationResultCallback: ((Dictionary<String, Any>)->()) = { dic in }
var cardListModel = YHAIListinfoModel() { var cardListModel = YHAIListInfoModel() {
didSet { didSet {
iconImgView.sd_setImage(with: URL(string: cardListModel.icon)) iconImgView.sd_setImage(with: URL(string: cardListModel.icon))
titleLabel.text = cardListModel.title titleLabel.text = cardListModel.title
...@@ -99,17 +142,14 @@ class YHCardMessageCell: UITableViewCell { ...@@ -99,17 +142,14 @@ class YHCardMessageCell: UITableViewCell {
}() }()
@objc func didBottomButtonClicked() { @objc func didBottomButtonClicked() {
if !cardListModel.redirectPath.isEmpty {
let vc = YHH5WebViewVC() var type = 0
vc.isFullScreenFlag = true if cardListModel.isEvaluation() {
vc.isHideNavigationBar = true type = 1
vc.url = cardListModel.redirectPath }
vc.evaluationResultCallback = { YHAIJumpPageTool.jumpPageWithType(type, mode: cardListModel.redirectMode, path: cardListModel.redirectPath) {
[weak self] dict in dict in
guard let self = self else { return } self.evaluationResultCallback(dict)
self.evaluationResultCallback(dict)
}
UIViewController.current?.present(vc, animated: true)
} }
} }
......
...@@ -11,7 +11,7 @@ class YHFixProductListMessageCell: UITableViewCell { ...@@ -11,7 +11,7 @@ class YHFixProductListMessageCell: UITableViewCell {
static let cellReuseIdentifier = "YHFixProductMessageCell" static let cellReuseIdentifier = "YHFixProductMessageCell"
var listModel = YHAIListinfoModel() { var listModel = YHAIListInfoModel() {
didSet { didSet {
iconImgView.sd_setImage(with: URL(string: listModel.icon)) iconImgView.sd_setImage(with: URL(string: listModel.icon))
titleLabel.text = listModel.title titleLabel.text = listModel.title
......
...@@ -16,9 +16,9 @@ class YHProductItemView: UIView { ...@@ -16,9 +16,9 @@ class YHProductItemView: UIView {
iconImgView.sd_setImage(with: URL(string: productModel.cover), placeholderImage: UIImage(named: "global_default_image")) iconImgView.sd_setImage(with: URL(string: productModel.cover), placeholderImage: UIImage(named: "global_default_image"))
titleLabel.text = productModel.title titleLabel.text = productModel.title
let aa: ASAttributedString = .init("¥", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.mainTextColor)) let aa: ASAttributedString = .init("¥", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.mainTextColor))
let bb: ASAttributedString = .init("\(productModel.discount_price)", .font(UIFont(name: "DINAlternate-Bold", size: 20)!),.foreground(UIColor.mainTextColor)) let bb: ASAttributedString = .init("\(productModel.discountPrice)", .font(UIFont(name: "DINAlternate-Bold", size: 20)!),.foreground(UIColor.mainTextColor))
let cc: ASAttributedString = .init(" ", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.init(hex: 0xB9C1CC))) let cc: ASAttributedString = .init(" ", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.init(hex: 0xB9C1CC)))
let dd: ASAttributedString = .init("¥\(productModel.original_price)", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.init(hex: 0xB9C1CC)), .strikethrough(.single)) let dd: ASAttributedString = .init("¥\(productModel.originalPrice)", .font(UIFont(name: "DINAlternate-Bold", size: 14)!),.foreground(UIColor.init(hex: 0xB9C1CC)), .strikethrough(.single))
priceLabel.attributed.text = aa+bb+cc+dd priceLabel.attributed.text = aa+bb+cc+dd
tagContentView.removeSubviews() tagContentView.removeSubviews()
...@@ -93,6 +93,10 @@ class YHProductItemView: UIView { ...@@ -93,6 +93,10 @@ class YHProductItemView: UIView {
vc.url = productModel.redirectPath vc.url = productModel.redirectPath
UIViewController.current?.navigationController?.pushViewController(vc) UIViewController.current?.navigationController?.pushViewController(vc)
} }
YHAIJumpPageTool.jumpPageWithType(0, mode: productModel.redirectMode, path: productModel.redirectPath) {
dict in
}
} }
func createUI() { func createUI() {
......
...@@ -12,7 +12,7 @@ class YHProductListMessageCell: UITableViewCell { ...@@ -12,7 +12,7 @@ class YHProductListMessageCell: UITableViewCell {
static let cellReuseIdentifier = "YHProductListMessageCell" static let cellReuseIdentifier = "YHProductListMessageCell"
var listModel = YHAIListinfoModel() { var listModel = YHAIListInfoModel() {
didSet { didSet {
iconImgView.sd_setImage(with: URL(string: listModel.icon)) iconImgView.sd_setImage(with: URL(string: listModel.icon))
titleLabel.text = listModel.title titleLabel.text = listModel.title
...@@ -111,11 +111,8 @@ class YHProductListMessageCell: UITableViewCell { ...@@ -111,11 +111,8 @@ class YHProductListMessageCell: UITableViewCell {
@objc func didMoreButtonClicked() { @objc func didMoreButtonClicked() {
if !listModel.redirectPath.isEmpty { YHAIJumpPageTool.jumpPageWithType(0, mode: listModel.redirectMode, path: listModel.redirectPath) {
let vc = YHH5WebViewVC() dict in
vc.isFullScreenFlag = false
vc.url = listModel.redirectPath
UIViewController.current?.navigationController?.pushViewController(vc)
} }
} }
......
...@@ -31,8 +31,8 @@ class YHHomeBannerIndicatorView: UIView { ...@@ -31,8 +31,8 @@ class YHHomeBannerIndicatorView: UIView {
} }
private var arrViews : [UIView] = [] private var arrViews : [UIView] = []
private var normalColor : UIColor = UIColor(hex: 0xffffff, alpha: 0.3) var normalColor : UIColor = UIColor(hex: 0xffffff, alpha: 0.3)
private var selectedColor : UIColor = UIColor(hex: 0xffffff) var selectedColor : UIColor = UIColor(hex: 0xffffff)
lazy var subHoldView: UIView = { lazy var subHoldView: UIView = {
let view = UIView() let view = UIView()
......
...@@ -120,6 +120,9 @@ class YHH5WebViewVC: YHBaseViewController, WKUIDelegate, WKNavigationDelegate { ...@@ -120,6 +120,9 @@ class YHH5WebViewVC: YHBaseViewController, WKUIDelegate, WKNavigationDelegate {
//6、是否展示 导航栏上的title //6、是否展示 导航栏上的title
var showNavigationTitleFlag : Bool = true var showNavigationTitleFlag : Bool = true
// 是否push进来
var isPushed : Bool = true
//22、禁用全局手势 //22、禁用全局手势
private var disableFullScreenGestureFlag : Bool = false private var disableFullScreenGestureFlag : Bool = false
...@@ -519,11 +522,13 @@ extension YHH5WebViewVC { ...@@ -519,11 +522,13 @@ extension YHH5WebViewVC {
printLog("web evaluation:\n") printLog("web evaluation:\n")
printLog("\(dic)") printLog("\(dic)")
self.evaluationResultCallback?(dic) self.evaluationResultCallback?(dic)
self.dismiss(animated: true)
} else {
self.dismiss(animated: true)
} }
if isPushed {
self.navigationController?.popViewController(animated: true)
} else {
self.dismiss(animated: true)
}
} }
//22、禁用全局手势返回 //22、禁用全局手势返回
......
...@@ -32,12 +32,6 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -32,12 +32,6 @@ class YHBasePlayerViewController: YHBaseViewController {
return view return view
}() }()
private(set) lazy var controlView: YHPlayerControlView = {
let view = YHPlayerControlView()
view.delegate = self
return view
}()
lazy var topBarView: YHPlayerTopBarView = { lazy var topBarView: YHPlayerTopBarView = {
let view = YHPlayerTopBarView(frame: CGRect(x: 0, y: 0, width: KScreenWidth, height: k_Height_NavigationtBarAndStatuBar)) let view = YHPlayerTopBarView(frame: CGRect(x: 0, y: 0, width: KScreenWidth, height: k_Height_NavigationtBarAndStatuBar))
return view return view
...@@ -51,13 +45,12 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -51,13 +45,12 @@ class YHBasePlayerViewController: YHBaseViewController {
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
setupUI() setupUI()
setupGestures() //setupGestures()
setupNotifications() setupNotifications()
} }
override func viewWillAppear(_ animated: Bool) { override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated) super.viewWillAppear(animated)
// navigationController?.setNavigationBarHidden(true, animated: animated)
gk_navBarAlpha = 0 gk_navBarAlpha = 0
gk_navigationBar.isHidden = true gk_navigationBar.isHidden = true
view.backgroundColor = .black view.backgroundColor = .black
...@@ -65,7 +58,6 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -65,7 +58,6 @@ class YHBasePlayerViewController: YHBaseViewController {
override func viewWillDisappear(_ animated: Bool) { override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated) super.viewWillDisappear(animated)
// navigationController?.setNavigationBarHidden(false, animated: animated)
gk_navBarAlpha = 1 gk_navBarAlpha = 1
gk_navigationBar.isHidden = false gk_navigationBar.isHidden = false
view.backgroundColor = .black view.backgroundColor = .black
...@@ -84,7 +76,6 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -84,7 +76,6 @@ class YHBasePlayerViewController: YHBaseViewController {
view.backgroundColor = .black view.backgroundColor = .black
view.addSubview(containerView) view.addSubview(containerView)
containerView.addSubview(playerView) containerView.addSubview(playerView)
containerView.addSubview(controlView)
containerView.addSubview(topBarView) containerView.addSubview(topBarView)
setupConstraints() setupConstraints()
} }
...@@ -98,10 +89,6 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -98,10 +89,6 @@ class YHBasePlayerViewController: YHBaseViewController {
make.edges.equalToSuperview() make.edges.equalToSuperview()
} }
controlView.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
topBarView.snp.makeConstraints { make in topBarView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview() make.top.left.right.equalToSuperview()
make.height.equalTo(k_Height_NavigationtBarAndStatuBar) make.height.equalTo(k_Height_NavigationtBarAndStatuBar)
...@@ -120,7 +107,7 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -120,7 +107,7 @@ class YHBasePlayerViewController: YHBaseViewController {
private func toggleControls() { private func toggleControls() {
isControlsVisible.toggle() isControlsVisible.toggle()
controlView.showControls(isControlsVisible) //controlView.showControls(isControlsVisible)
resetControlsAutoHideTimer() resetControlsAutoHideTimer()
} }
...@@ -135,49 +122,10 @@ class YHBasePlayerViewController: YHBaseViewController { ...@@ -135,49 +122,10 @@ class YHBasePlayerViewController: YHBaseViewController {
private func hideControls() { private func hideControls() {
isControlsVisible = false isControlsVisible = false
controlView.showControls(false) //controlView.showControls(false)
} }
} }
// MARK: - YHPlayerControlViewDelegate
extension YHBasePlayerViewController: YHPlayerControlViewDelegate {
func playerControlView(_ view: YHPlayerControlView, didTapBack button: UIButton) {
if let navigationController = navigationController {
navigationController.popViewController(animated: true)
} else {
dismiss(animated: true)
}
}
func playerControlView(_ view: YHPlayerControlView, didTapPlay button: UIButton) {
if player?.getPlayState() == .playing {
YHPlayerManager.shared.pause()
} else {
YHPlayerManager.shared.resume()
}
}
func playerControlView(_ view: YHPlayerControlView, didSeekTo position: Float) {
guard let player = player else { return }
let duration = player.getDuration()
let targetPosition = Int(Float(duration) * position)
player.playerKit?.seek(toPosition: targetPosition)
}
func playerControlView(_ view: YHPlayerControlView, didChangeQuality quality: YHVideoQuality) {
// 由子类实现
}
func playerControlView(_ view: YHPlayerControlView, didTapFullscreen button: UIButton) {
if UIDevice.current.orientation.isPortrait {
let value = UIInterfaceOrientation.landscapeRight.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
} else {
let value = UIInterfaceOrientation.portrait.rawValue
UIDevice.current.setValue(value, forKey: "orientation")
}
}
}
// MARK: - Notifications // MARK: - Notifications
extension YHBasePlayerViewController { extension YHBasePlayerViewController {
......
...@@ -6,12 +6,13 @@ ...@@ -6,12 +6,13 @@
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved. // Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
// //
import HyphenateChat
import AgoraRtcKit import AgoraRtcKit
import HyphenateChat
import UIKit import UIKit
class YHLivePlayerViewController: YHBasePlayerViewController { class YHLivePlayerViewController: YHBasePlayerViewController {
// MARK: - Properties // MARK: - Properties
private let liveId: Int private let liveId: Int
private var roomId: String? private var roomId: String?
private let messageQueue = DispatchQueue(label: "com.livePlayerRoom.messageQueue") private let messageQueue = DispatchQueue(label: "com.livePlayerRoom.messageQueue")
...@@ -22,8 +23,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -22,8 +23,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
private var listMaxWidth: CGFloat { private var listMaxWidth: CGFloat {
return KScreenWidth * 248.0 / 375.0 return KScreenWidth * 248.0 / 375.0
} }
// MARK: - UI Components // MARK: - UI Components
private lazy var bottomInputBar: YHInputBottomBar = { private lazy var bottomInputBar: YHInputBottomBar = {
let view = YHInputBottomBar() let view = YHInputBottomBar()
view.textViewTappedEvent = { [weak self] in view.textViewTappedEvent = { [weak self] in
...@@ -31,7 +33,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -31,7 +33,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
return view return view
}() }()
private lazy var inputVC: YHMessageInputViewController = { private lazy var inputVC: YHMessageInputViewController = {
let ctl = YHMessageInputViewController() let ctl = YHMessageInputViewController()
ctl.inputCallback = { [weak self] controller, text in ctl.inputCallback = { [weak self] controller, text in
...@@ -39,22 +41,22 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -39,22 +41,22 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
return ctl return ctl
}() }()
private lazy var messageListView: YHLiveMessageListView = { private lazy var messageListView: YHLiveMessageListView = {
let view = YHLiveMessageListView(frame: CGRect(x: 0, y: 0, width: listMaxWidth, height: listMaxWidth)) let view = YHLiveMessageListView(frame: CGRect(x: 0, y: 0, width: listMaxWidth, height: listMaxWidth))
return view return view
}() }()
private lazy var liveStateVC: YHLiveStateViewController = { private lazy var liveStateVC: YHLiveStateViewController = {
let vc = YHLiveStateViewController() let vc = YHLiveStateViewController()
vc.shareEvent = { [weak self] in vc.shareEvent = { [weak self] in
self?.shareLive() self?.shareLive()
} }
vc.closeEvent = { [weak self] in vc.closeEvent = { [weak self] in
self?.closeLive() self?.closeLive()
} }
vc.backHomeEvent = { [weak self] in vc.backHomeEvent = { [weak self] in
self?.closeLive() self?.closeLive()
UIViewController.current?.navigationController?.popToRootViewController(animated: false) UIViewController.current?.navigationController?.popToRootViewController(animated: false)
...@@ -62,25 +64,27 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -62,25 +64,27 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
return vc return vc
}() }()
// MARK: - Initialization // MARK: - Initialization
init(id: Int, url: String? = nil, title: String? = nil, roomId: String? = nil) { init(id: Int, url: String? = nil, title: String? = nil, roomId: String? = nil) {
self.liveId = id liveId = id
self.roomId = roomId self.roomId = roomId
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
// 隐藏播控UI // 隐藏播控UI
controlView.isHidden = true // controlView.isHidden = true
player?.delegate = self // 设置播放器代理 player?.delegate = self // 设置播放器代理
if let roomId = roomId { if let roomId = roomId {
setupChatRoom(roomId: roomId) setupChatRoom(roomId: roomId)
} }
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
modalPresentationStyle = .fullScreen modalPresentationStyle = .fullScreen
...@@ -91,53 +95,53 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -91,53 +95,53 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
joinLiveRoom(id: liveId, callback: { _, _ in }) joinLiveRoom(id: liveId, callback: { _, _ in })
} }
} }
deinit { deinit {
NotificationCenter.default.removeObserver(self) NotificationCenter.default.removeObserver(self)
quitChatRoom() quitChatRoom()
} }
// MARK: - Setup // MARK: - Setup
private func setupLiveUI() { private func setupLiveUI() {
containerView.addSubview(bottomInputBar) containerView.addSubview(bottomInputBar)
containerView.addSubview(messageListView) containerView.addSubview(messageListView)
bottomInputBar.snp.makeConstraints { make in bottomInputBar.snp.makeConstraints { make in
make.left.right.bottom.equalToSuperview() make.left.right.bottom.equalToSuperview()
make.top.equalTo(view.safeAreaLayoutGuide.snp.bottom) make.top.equalTo(view.safeAreaLayoutGuide.snp.bottom)
.offset(-YHInputBottomBar.inputActionHeight) .offset(-YHInputBottomBar.inputActionHeight)
} }
messageListView.snp.makeConstraints { make in messageListView.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16) make.left.equalToSuperview().offset(16)
make.bottom.equalTo(bottomInputBar.snp.top).offset(-14) make.bottom.equalTo(bottomInputBar.snp.top).offset(-14)
make.height.width.equalTo(listMaxWidth) make.height.width.equalTo(listMaxWidth)
} }
topBarView.closeButtonClickEvent = { [weak self] in topBarView.closeButtonClickEvent = { [weak self] in
self?.closeLive() self?.closeLive()
} }
topBarView.zoomButtonClickEvent = { [weak self] in topBarView.zoomButtonClickEvent = { [weak self] in
self?.enterFloating() self?.enterFloating()
} }
topBarView.shareButtonClickEvent = { [weak self] in topBarView.shareButtonClickEvent = { [weak self] in
self?.shareLive() self?.shareLive()
} }
bottomInputBar.giftButtonClickEvent = { [weak self] in bottomInputBar.giftButtonClickEvent = { [weak self] in
self?.showGoods() self?.showGoods()
} }
} }
private func setupStateViewController() { private func setupStateViewController() {
isLiveStateOn = true isLiveStateOn = true
// 1. 添加子控制器 // 1. 添加子控制器
// addChild(liveStateVC) // addChild(liveStateVC)
view.addSubview(liveStateVC.view) view.addSubview(liveStateVC.view)
view.bringSubviewToFront(liveStateVC.view) view.bringSubviewToFront(liveStateVC.view)
liveStateVC.view.isUserInteractionEnabled = true
// 2. 使用SnapKit设置约束 // 2. 使用SnapKit设置约束
liveStateVC.view.snp.makeConstraints { make in liveStateVC.view.snp.makeConstraints { make in
make.edges.equalToSuperview() make.edges.equalToSuperview()
...@@ -153,11 +157,11 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -153,11 +157,11 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
liveStateVC.view.removeFromSuperview() liveStateVC.view.removeFromSuperview()
// liveStateVC.removeFromParent() // liveStateVC.removeFromParent()
} }
private func setupData() { private func setupData() {
viewModel.getLiveDetail(id: liveId) { [weak self] liveDetail, error in viewModel.getLiveDetail(id: liveId) { [weak self] liveDetail, error in
guard let self = self else { return } guard let self = self else { return }
if let liveDetail = liveDetail { if let liveDetail = liveDetail {
self.handleLiveDetailSuccess(liveDetail) self.handleLiveDetailSuccess(liveDetail)
} else { } else {
...@@ -169,7 +173,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -169,7 +173,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func handleLiveDetailSuccess(_ liveDetail: YHLiveDetailModel, needJoinIMRoom: Bool = true, needJoinLiveChannel: Bool = true) { private func handleLiveDetailSuccess(_ liveDetail: YHLiveDetailModel, needJoinIMRoom: Bool = true, needJoinLiveChannel: Bool = true) {
// 更新顶部栏信息 // 更新顶部栏信息
topBarView.setupTopBarView( topBarView.setupTopBarView(
...@@ -179,6 +183,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -179,6 +183,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
) )
playbackInfo?.channelId = liveDetail.rtmp_channel playbackInfo?.channelId = liveDetail.rtmp_channel
playbackInfo?.token = liveDetail.token playbackInfo?.token = liveDetail.token
playbackInfo?.title = liveDetail.live_title
if needJoinLiveChannel { if needJoinLiveChannel {
if !liveDetail.rtmp_channel.isEmpty, !liveDetail.token.isEmpty, let uid = playbackInfo?.uid, let player = player { if !liveDetail.rtmp_channel.isEmpty, !liveDetail.token.isEmpty, let uid = playbackInfo?.uid, let player = player {
player.setPlayView(playerView) player.setPlayView(playerView)
...@@ -202,12 +207,12 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -202,12 +207,12 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
removeStateViewController() removeStateViewController()
} }
} }
private func setupChatRoom(roomId: String) { private func setupChatRoom(roomId: String) {
// 初始化聊天室 // 初始化聊天室
joinChatRoom(roomId: roomId) joinChatRoom(roomId: roomId)
} }
private func setupLiveNotifications() { private func setupLiveNotifications() {
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
...@@ -215,47 +220,48 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -215,47 +220,48 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
name: YHIMHelper.didChatManagerReceiveMessages, name: YHIMHelper.didChatManagerReceiveMessages,
object: nil object: nil
) )
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(didLoginEaseIMSuccess), selector: #selector(didLoginEaseIMSuccess),
name: YHIMHelper.didLoginEaseIMSuccess, name: YHIMHelper.didLoginEaseIMSuccess,
object: nil object: nil
) )
NotificationCenter.default.addObserver( NotificationCenter.default.addObserver(
self, self,
selector: #selector(didLogOutEaseIM), selector: #selector(didLogOutEaseIM),
name: YHIMHelper.didLogOutEaseIM, name: YHIMHelper.didLogOutEaseIM,
object: nil object: nil
) )
NotificationCenter.default.addObserver(self, selector: #selector(didLoginYH), name: YhConstant.YhNotification.didLoginSuccessNotifiction, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didLoginYH), name: YhConstant.YhNotification.didLoginSuccessNotifiction, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didLogOutYH), name: YhConstant.YhNotification.didLogoutSuccessNotifiction, object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didLogOutYH), name: YhConstant.YhNotification.didLogoutSuccessNotifiction, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(didJoinedOfUid), name: Notification.Name("com.YHPlayerManager.didJoinedOfUid"), object: nil) NotificationCenter.default.addObserver(self, selector: #selector(didJoinedOfUid), name: Notification.Name("com.YHPlayerManager.didJoinedOfUid"), object: nil)
} }
// MARK: - Public Methods // MARK: - Public Methods
func play(url: String, title: String? = nil) { func play(url: String, title: String? = nil) {
currentPlayingURL = url currentPlayingURL = url
currentVideoTitle = title currentVideoTitle = title
controlView.setTitle(title ?? "") // controlView.setTitle(title ?? "")
YHPlayerManager.shared.play(url: url, inView: playerView, title: title) YHPlayerManager.shared.play(url: url, inView: playerView, title: title)
} }
func enterFloating() { func enterFloating() {
guard let playbackInfo = playbackInfo else { guard let playbackInfo = playbackInfo else {
return return
} }
//player?.setPlayViewNull()
YHPlayerManager.shared.enterFloating(from: self, playbackInfo: playbackInfo) YHPlayerManager.shared.enterFloating(from: self, playbackInfo: playbackInfo)
} }
// MARK: - Chat Room Methods // MARK: - Chat Room Methods
private func joinChatRoom(roomId: String) { private func joinChatRoom(roomId: String) {
YHIMHelper.shared.joinChatRoom(roomID: roomId, leaveOtherRooms: true) { [weak self] error in YHIMHelper.shared.joinChatRoom(roomID: roomId, leaveOtherRooms: true) { [weak self] error in
guard let self = self else { return } guard let self = self else { return }
if let error = error { if let error = error {
printLog("joinChatRoom: \(error)") printLog("joinChatRoom: \(error)")
} else { } else {
...@@ -263,15 +269,18 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -263,15 +269,18 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func loadHistoryMessages(roomId: String) { private func loadHistoryMessages(roomId: String) {
YHIMHelper.shared.fetchHistoryMessage(roomID: roomId) { [weak self] list, error in YHIMHelper.shared.fetchHistoryMessage(roomID: roomId) { [weak self] list, _ in
guard let self = self, let messages = list else { return } guard let self = self, let messages = list else { return }
self.messageListView.clearMessages() self.messageListView.clearMessages()
if let tips = self.viewModel.liveDetailModel?.tips, tips.count > 0 {
self.appendTipsMessage(tips)
}
self.appendHistoryMessages(messages) self.appendHistoryMessages(messages)
} }
} }
private func leaveLiveRoom() { private func leaveLiveRoom() {
guard YHLoginManager.shared.isLogin() else { guard YHLoginManager.shared.isLogin() else {
return return
...@@ -284,10 +293,10 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -284,10 +293,10 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func quitChatRoom() { private func quitChatRoom() {
guard let roomId = roomId else { return } guard let roomId = roomId else { return }
YHIMHelper.shared.quitChatRoom(roomID: roomId) { error in YHIMHelper.shared.quitChatRoom(roomID: roomId) { error in
if let error = error { if let error = error {
printLog("quitChatRoom: \(error)") printLog("quitChatRoom: \(error)")
...@@ -296,24 +305,24 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -296,24 +305,24 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func closeLive() { private func closeLive() {
quitChatRoom() quitChatRoom()
leaveLiveRoom() leaveLiveRoom()
//YHPlayerManager.shared.stop(type: .main) // YHPlayerManager.shared.stop(type: .main)
if let player = player { if let player = player {
YHPlayerManager.shared.leaveChannel(for: player) YHPlayerManager.shared.leaveChannel(for: player)
} }
dismiss(animated: true) dismiss(animated: true)
} }
private func shareLive() { private func shareLive() {
guard let liveModel = viewModel.liveDetailModel else { guard let liveModel = viewModel.liveDetailModel else {
return return
} }
YHShareAlertView.show(image: liveModel.live_image, title: "@" + liveModel.account, subMessage: liveModel.live_title, linkUrl: liveModel.live_h5_url, isLive: true) YHShareAlertView.show(image: liveModel.live_image, title: "@" + liveModel.account, subMessage: liveModel.live_title, linkUrl: liveModel.live_h5_url, isLive: true)
} }
private func showGoods() { private func showGoods() {
let list = viewModel.liveDetailModel?.goods ?? [] let list = viewModel.liveDetailModel?.goods ?? []
let view = YHLiveShopView.show { [weak self] index in let view = YHLiveShopView.show { [weak self] index in
...@@ -327,9 +336,54 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -327,9 +336,54 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
view.closeEvent = { [weak self] in view.closeEvent = { [weak self] in
self?.goodsListView = nil self?.goodsListView = nil
} }
view.buyData = { [weak self] index in
guard list.count > index else {
return
}
let model = list[index]
self?.createOrder(id: model.id)
}
goodsListView = view goodsListView = view
} }
private func createOrder(id: Int) {
YHHUD.show(.progress(message: "加载中..."))
viewModel.createOrder(source: 1, sourceId: liveId, productId: id) { [weak self] orderModel, error in
YHHUD.hide()
guard let self = self else {
return
}
guard let orderModel = orderModel else {
if let errorMsg = error?.errorMsg, errorMsg.count > 0 {
YHHUD.flash(message: errorMsg)
}
return
}
var url = YHBaseUrlManager.shared.curH5URL() + "superAppBridge.html#/order/order-confirm" + "?id=\(orderModel.id)"
if YHLoginManager.shared.isLogin() {
let token = YHLoginManager.shared.h5Token
let urlHasParam = String.hasQueryParameters(urlString: url)
if urlHasParam {
url = url + "&param=" + token
} else {
url = url + "?param=" + token
}
}
var tUrl = url
if !url.contains("navigationH=") {
tUrl = url + "?navigationH=\(k_Height_NavigationtBarAndStatuBar)"
if url.contains("?") {
tUrl = url + "&navigationH=\(k_Height_NavigationtBarAndStatuBar)"
}
}
let vc = YHH5WebViewVC()
vc.url = tUrl
vc.isHideNavigationBar = false
goodsListView?.dismiss()
navigationController?.pushViewController(vc)
}
}
private func gotoH5GoodsDetail(id: Int) { private func gotoH5GoodsDetail(id: Int) {
var url = YHBaseUrlManager.shared.curH5URL() + "superAppBridge.html#/goods/sales-detail" + "?id=\(id)" var url = YHBaseUrlManager.shared.curH5URL() + "superAppBridge.html#/goods/sales-detail" + "?id=\(id)"
if YHLoginManager.shared.isLogin() { if YHLoginManager.shared.isLogin() {
...@@ -354,15 +408,16 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -354,15 +408,16 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
goodsListView?.dismiss() goodsListView?.dismiss()
navigationController?.pushViewController(vc) navigationController?.pushViewController(vc)
} }
// MARK: - Message Handling // MARK: - Message Handling
private func handleMessageInput(text: String, controller: YHMessageInputViewController) { private func handleMessageInput(text: String, controller: YHMessageInputViewController) {
guard checkLogin(), guard checkLogin(),
let roomId = roomId else { return } let roomId = roomId else { return }
YHIMHelper.shared.sendMessage(roomID: roomId, sendText: text) { [weak self] message, error in YHIMHelper.shared.sendMessage(roomID: roomId, sendText: text) { [weak self] message, error in
guard let self = self else { return } guard let self = self else { return }
if let message = message { if let message = message {
controller.updateText("") controller.updateText("")
controller.closeKeyboard(nil) controller.closeKeyboard(nil)
...@@ -393,7 +448,18 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -393,7 +448,18 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func appendTipsMessage(_ tips: String) {
messageQueue.async {
let body = EMCustomMessageBody(event: YHChatRoomCustomLocal.tipsEvent, customExt: [YHChatRoomCustomLocal.tipsKey: tips])
let message = EMChatMessage(conversationID: self.roomId ?? "", body: body, ext: nil)
message.chatType = .chatRoom
DispatchQueue.main.async {
self.messageListView.addMessages([message])
}
}
}
private func checkLogin() -> Bool { private func checkLogin() -> Bool {
if !YHLoginManager.shared.isLogin() { if !YHLoginManager.shared.isLogin() {
YHOneKeyLoginManager.shared.oneKeyLogin() YHOneKeyLoginManager.shared.oneKeyLogin()
...@@ -401,9 +467,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -401,9 +467,9 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
return true return true
} }
// MARK: - Notification Handlers // MARK: - Notification Handlers
@objc private func didJoinedOfUid() { @objc private func didJoinedOfUid() {
let videoCanvas = AgoraRtcVideoCanvas() let videoCanvas = AgoraRtcVideoCanvas()
videoCanvas.uid = playbackInfo?.uid ?? 0 videoCanvas.uid = playbackInfo?.uid ?? 0
...@@ -413,7 +479,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -413,7 +479,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
let ret = YHPlayerManager.shared.agoraKit.setupRemoteVideo(videoCanvas) let ret = YHPlayerManager.shared.agoraKit.setupRemoteVideo(videoCanvas)
printLog(ret) printLog(ret)
} }
@objc private func didChatManagerReceiveMessages(_ note: Notification) { @objc private func didChatManagerReceiveMessages(_ note: Notification) {
guard let messages = note.object as? [EMChatMessage], guard let messages = note.object as? [EMChatMessage],
let message = messages.first, let message = messages.first,
...@@ -429,7 +495,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -429,7 +495,7 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
private func updateLiveDetailWith(event: YHChatRoomCustomEvent) { private func updateLiveDetailWith(event: YHChatRoomCustomEvent) {
viewModel.getLiveDetail(id: liveId) { [weak self] liveDetail, error in viewModel.getLiveDetail(id: liveId) { [weak self] liveDetail, error in
guard let self = self else { return } guard let self = self else { return }
...@@ -456,40 +522,45 @@ class YHLivePlayerViewController: YHBasePlayerViewController { ...@@ -456,40 +522,45 @@ class YHLivePlayerViewController: YHBasePlayerViewController {
} }
} }
} }
@objc private func didLoginYH() { @objc private func didLoginYH() {
if YHLoginManager.shared.isLogin() { if YHLoginManager.shared.isLogin() {
joinLiveRoom(id: liveId, callback: { _, _ in }) joinLiveRoom(id: liveId, callback: { _, _ in })
} }
} }
@objc private func didLogOutYH() { @objc private func didLogOutYH() {
// //
} }
@objc private func didLoginEaseIMSuccess() { @objc private func didLoginEaseIMSuccess() {
if let roomId = roomId { if let roomId = roomId {
joinChatRoom(roomId: roomId) joinChatRoom(roomId: roomId)
} }
} }
@objc private func didLogOutEaseIM() { @objc private func didLogOutEaseIM() {
quitChatRoom() quitChatRoom()
} }
} }
// MARK: - YHPlayerDelegate // MARK: - YHPlayerDelegate
extension YHLivePlayerViewController: YHPlayerDelegate { extension YHLivePlayerViewController: YHPlayerDelegate {
func player(_ player: YHPlayer, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval) {
DispatchQueue.main.async {
//
}
}
func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) { func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) {
DispatchQueue.main.async { DispatchQueue.main.async {
// 更新通用UI状态 // 更新通用UI状态
switch state { switch state {
case .playing: case .playing:
// controlView.updatePlayButton(isPlaying: true)
// 直播开始时的特殊处理 // 直播开始时的特殊处理
break break
case .paused, .stopped: case .paused, .stopped:
// controlView.updatePlayButton(isPlaying: false)
// 直播开始时的特殊处理 // 直播开始时的特殊处理
break break
case .failed: case .failed:
...@@ -498,22 +569,11 @@ extension YHLivePlayerViewController: YHPlayerDelegate { ...@@ -498,22 +569,11 @@ extension YHLivePlayerViewController: YHPlayerDelegate {
break break
} }
} }
} }
func player(_ player: YHPlayer, didChangedToPosition position: Int) { func player(_ player: YHPlayer, didChangedToPosition position: Int) {
let duration = player.getDuration() }
guard duration > 0 else { return }
let progress = Float(position) / Float(duration)
let currentTime = formatTime(position)
let totalTime = formatTime(duration)
controlView.updateProgress(progress,
currentTime: currentTime,
totalTime: totalTime)
}
func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) { func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) {
// 处理视频尺寸变化,如果需要的话 // 处理视频尺寸变化,如果需要的话
} }
......
...@@ -6,119 +6,6 @@ ...@@ -6,119 +6,6 @@
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved. // Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
// //
//import AgoraRtcKit
//import Foundation
//
//// MARK: - 播放器类型
//
//enum YHPlayerType {
// case main
// case secondary
//}
//
//protocol YHPlayerDelegate: AnyObject {
// func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason)
// func player(_ player: YHPlayer, didChangedToPosition position: Int)
// func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize)
//}
//
//// MARK: - 播放器实例封装
//
//class YHPlayer {
// weak var delegate: YHPlayerDelegate?
// let type: YHPlayerType
// var playerKit: AgoraRtcMediaPlayerProtocol?
// weak var agoraKit: AgoraRtcEngineKit?
//
// private(set) var currentURL: String?
// weak var currentPlayView: UIView?
// private(set) var currentTitle: String?
// private(set) var currentChannelId: String?
// private(set) var currentUid: UInt?
// private(set) var isJoined: Bool = false
//
// var isMuted: Bool {
// get { playerKit?.getMute() ?? false }
// set { playerKit?.mute(newValue) }
// }
//
// init(type: YHPlayerType, playerKit: AgoraRtcMediaPlayerProtocol?, agoraKit: AgoraRtcEngineKit?) {
// self.type = type
// self.playerKit = playerKit
// self.agoraKit = agoraKit
// // 基础设置
// playerKit?.setLoopCount(-1)
// }
//
// func setPlayView(_ view: UIView?) {
// currentPlayView = view
// playerKit?.setRenderMode(.fit)
// playerKit?.setView(view)
// }
//
// func setJoinInfo(channelId: String, uid: UInt) {
// currentChannelId = channelId
// currentUid = uid
// isJoined = true
// }
//
// func clearJoinInfo() {
// currentChannelId = nil
// currentUid = nil
// isJoined = false
// }
//
// func play(url: String, title: String? = nil) {
// currentURL = url
// currentTitle = title
// let mediaSource = AgoraMediaSource()
// mediaSource.url = url
// mediaSource.autoPlay = true
// let result = playerKit?.open(with: mediaSource)
// if result == 0 {
// playerKit?.play()
// }
// }
//
// func stop() {
// playerKit?.stop()
// currentPlayView = nil
// currentURL = nil
// currentTitle = nil
// }
//
// func pause() {
// playerKit?.pause()
// }
//
// func resume() {
// playerKit?.play()
// }
//
// func reset() {
// stop()
// setPlayView(nil)
// delegate = nil
// }
//
// func releasePlayer() {
// reset()
// playerKit = nil
// }
//
// func getPosition() -> Int {
// return playerKit?.getPosition() ?? 0
// }
//
// func getDuration() -> Int {
// return playerKit?.getDuration() ?? 0
// }
//
// func getPlayState() -> AgoraMediaPlayerState {
// return playerKit?.getPlayerState() ?? .idle
// }
//}
import AgoraRtcKit import AgoraRtcKit
import Foundation import Foundation
...@@ -132,6 +19,7 @@ protocol YHPlayerDelegate: AnyObject { ...@@ -132,6 +19,7 @@ protocol YHPlayerDelegate: AnyObject {
func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason)
func player(_ player: YHPlayer, didChangedToPosition position: Int) func player(_ player: YHPlayer, didChangedToPosition position: Int)
func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize)
func player(_ player: YHPlayer, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval)
} }
// MARK: - 播放器实例封装 // MARK: - 播放器实例封装
...@@ -234,10 +122,13 @@ class YHPlayer { ...@@ -234,10 +122,13 @@ class YHPlayer {
currentTitle = nil currentTitle = nil
} }
func play(url: String, title: String? = nil) { func play(url: String, title: String? = nil, view: UIView?) {
currentURL = url currentURL = url
currentTitle = title currentTitle = title
lastPlaybackInfo = (url, title) // 保存播放信息 lastPlaybackInfo = (url, title) // 保存播放信息
currentPlayView = view
playerKit?.setRenderMode(.fit)
playerKit?.setView(view)
let mediaSource = AgoraMediaSource() let mediaSource = AgoraMediaSource()
mediaSource.url = url mediaSource.url = url
...@@ -271,7 +162,7 @@ class YHPlayer { ...@@ -271,7 +162,7 @@ class YHPlayer {
func resume(withNewView view: UIView) { func resume(withNewView view: UIView) {
// 设置新的播放视图 // 设置新的播放视图
setPlayView(view) //setPlayView(view)
// 如果有频道信息,通知 Manager 重新加入频道 // 如果有频道信息,通知 Manager 重新加入频道
if let token = currentToken, if let token = currentToken,
...@@ -286,7 +177,7 @@ class YHPlayer { ...@@ -286,7 +177,7 @@ class YHPlayer {
// 如果有上次的播放信息,重新播放 // 如果有上次的播放信息,重新播放
if let lastInfo = lastPlaybackInfo, if let lastInfo = lastPlaybackInfo,
let url = lastInfo.url { let url = lastInfo.url {
play(url: url, title: lastInfo.title) play(url: url, title: lastInfo.title, view: view)
} }
} }
......
...@@ -106,7 +106,7 @@ class YHPlayerManager: NSObject { ...@@ -106,7 +106,7 @@ class YHPlayerManager: NSObject {
if let view = view { if let view = view {
player.setPlayView(view) player.setPlayView(view)
} }
player.play(url: url, title: title) player.play(url: url, title: title, view: view)
} }
func pause(type: YHPlayerType = .main) { func pause(type: YHPlayerType = .main) {
...@@ -292,7 +292,7 @@ class YHPlayerManager: NSObject { ...@@ -292,7 +292,7 @@ class YHPlayerManager: NSObject {
exitFloating() exitFloating()
if let url = playbackInfo.url { if let url = playbackInfo.url {
player.play(url: url, title: playbackInfo.title) player.play(url: url, title: playbackInfo.title, view: playerVC.playerView)
} }
present(playerVC, from: sourceView) present(playerVC, from: sourceView)
...@@ -329,7 +329,7 @@ class YHPlayerManager: NSObject { ...@@ -329,7 +329,7 @@ class YHPlayerManager: NSObject {
let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty { let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid) joinChannel(for: player, token: token, channelId: channelId, uid: uid)
} else if let url = playbackInfo.url, url.count > 0 { } else if let url = playbackInfo.url, url.count > 0 {
player.play(url: url, title: playbackInfo.title) player.play(url: url, title: playbackInfo.title, view: playerVC.playerView)
} }
present(navVC, from: sourceView) present(navVC, from: sourceView)
...@@ -339,32 +339,13 @@ class YHPlayerManager: NSObject { ...@@ -339,32 +339,13 @@ class YHPlayerManager: NSObject {
guard let window = UIApplication.shared.yhKeyWindow() else { return } guard let window = UIApplication.shared.yhKeyWindow() else { return }
let playerType = determinePlayerType(for: .floating) let playerType = determinePlayerType(for: .floating)
let player: YHPlayer// = player(for: playerType) let player: YHPlayer = player(for: playerType)
var updatedInfo = playbackInfo var updatedInfo = playbackInfo
updatedInfo.scene = .floating updatedInfo.scene = .floating
updatedInfo.playerType = playerType updatedInfo.playerType = playerType
currentPlaybackInfo[playerType] = updatedInfo currentPlaybackInfo[playerType] = updatedInfo
// 检查是否存在当前播放器
if let existingPlayer = activePlayers[playerType],
currentPlaybackInfo[playerType] == playbackInfo {
// 使用现有播放器
player = existingPlayer
} else {
// 创建新播放器
player = self.player(for: playerType)
// 加入频道
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId,
let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title)
}
}
// // 加入频道 // // 加入频道
// if let token = playbackInfo.token, // if let token = playbackInfo.token,
// let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty { // let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
...@@ -392,6 +373,15 @@ class YHPlayerManager: NSObject { ...@@ -392,6 +373,15 @@ class YHPlayerManager: NSObject {
// 计算目标位置 // 计算目标位置
let targetFrame = floatingWindow.calculateInitialFrame() let targetFrame = floatingWindow.calculateInitialFrame()
// 检查是否存在当前播放器
// 加入频道
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId,
let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title, view: floatingWindow.contentView)
}
// 执行动画 // 执行动画
let showFloatingWindow = { let showFloatingWindow = {
...@@ -417,6 +407,14 @@ class YHPlayerManager: NSObject { ...@@ -417,6 +407,14 @@ class YHPlayerManager: NSObject {
floatingWindow.player = player floatingWindow.player = player
floatingWindow.playbackInfo = updatedInfo floatingWindow.playbackInfo = updatedInfo
self.floatingWindow = floatingWindow self.floatingWindow = floatingWindow
// 检查是否存在当前播放器
if let token = playbackInfo.token,
let channelId = playbackInfo.channelId,
let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid)
} else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title, view: floatingWindow.contentView)
}
let showFloatingWindow = { let showFloatingWindow = {
floatingWindow.show(in: window) floatingWindow.show(in: window)
...@@ -448,7 +446,7 @@ class YHPlayerManager: NSObject { ...@@ -448,7 +446,7 @@ class YHPlayerManager: NSObject {
let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty { let channelId = playbackInfo.channelId, let uid = playbackInfo.uid, !token.isEmpty, !channelId.isEmpty {
joinChannel(for: player, token: token, channelId: channelId, uid: uid) joinChannel(for: player, token: token, channelId: channelId, uid: uid)
} else if let url = playbackInfo.url, !url.isEmpty { } else if let url = playbackInfo.url, !url.isEmpty {
player.play(url: url, title: playbackInfo.title) player.play(url: url, title: playbackInfo.title, view: view)
} }
} }
...@@ -487,6 +485,12 @@ extension YHPlayerManager: AgoraRtcMediaPlayerDelegate { ...@@ -487,6 +485,12 @@ extension YHPlayerManager: AgoraRtcMediaPlayerDelegate {
player.delegate?.player(player, didChangedToPosition: position) player.delegate?.player(player, didChangedToPosition: position)
} }
} }
func AgoraRtcMediaPlayer(_ playerKit: any AgoraRtcMediaPlayerProtocol, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval) {
if let player = activePlayers.values.first(where: { $0.playerKit === playerKit }) {
player.delegate?.player(player, didChangedTo: positionMs, atTimestamp: timestampMs)
}
}
func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol, func AgoraRtcMediaPlayer(_ playerKit: AgoraRtcMediaPlayerProtocol,
infoUpdated info: AgoraMediaPlayerUpdatedInfo) { infoUpdated info: AgoraMediaPlayerUpdatedInfo) {
......
...@@ -6,27 +6,26 @@ ...@@ -6,27 +6,26 @@
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved. // Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
// //
// YHVODPlayerViewController.swift
import UIKit
import AgoraRtcKit import AgoraRtcKit
import UIKit
class YHVODPlayerViewController: YHBasePlayerViewController { class YHVODPlayerViewController: YHBasePlayerViewController {
// MARK: - Properties // MARK: - Properties
private let vodId: Int private let vodId: Int
var startPosition: Int = 0 var startPosition: Int = 0
private let viewModel = YHLiveSalesViewModel() private let viewModel = YHLiveSalesViewModel()
private var goodsListView: YHLiveShopView?
private lazy var dropdownView: YHCategoryDropdownView = {
let view = YHCategoryDropdownView() private lazy var progressControl: YHVideoProgressControl = {
view.onSelect = { category in let view = YHVideoProgressControl()
print("Selected: \(category)")
}
return view return view
}() }()
// MARK: - Initialization // MARK: - Initialization
init(id: Int, url: String? = nil, title: String? = nil) { init(id: Int, url: String? = nil, title: String? = nil) {
self.vodId = id vodId = id
super.init(nibName: nil, bundle: nil) super.init(nibName: nil, bundle: nil)
player?.delegate = self player?.delegate = self
currentPlayingURL = url currentPlayingURL = url
...@@ -35,42 +34,74 @@ class YHVODPlayerViewController: YHBasePlayerViewController { ...@@ -35,42 +34,74 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
play(url: url, title: title) play(url: url, title: title)
} }
} }
required init?(coder: NSCoder) { required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented") fatalError("init(coder:) has not been implemented")
} }
func play(url: String, title: String? = nil) { func play(url: String, title: String? = nil) {
currentPlayingURL = url currentPlayingURL = url
currentVideoTitle = title currentVideoTitle = title
controlView.setTitle(title ?? "") // controlView.setTitle(title ?? "")
YHPlayerManager.shared.play(url: url, inView: playerView, title: title) YHPlayerManager.shared.play(url: url, inView: playerView, title: title)
} }
// MARK: - Lifecycle // MARK: - Lifecycle
override func viewDidLoad() { override func viewDidLoad() {
super.viewDidLoad() super.viewDidLoad()
//setupUI() setupUI()
loadVideoDetail() loadVideoDetail()
controlView.isHidden = true // controlView.isHidden = true
} }
// MARK: - SetupView // MARK: - SetupView
private func setupUI() { private func setupUI() {
containerView.addSubview(dropdownView) containerView.addSubview(progressControl)
dropdownView.snp.makeConstraints { make in progressControl.snp.makeConstraints { make in
make.top.equalTo(topBarView.snp.bottom).offset(24) make.left.right.bottom.equalToSuperview()
make.left.equalToSuperview().offset(16) make.top.equalTo(view.safeAreaLayoutGuide.snp.bottom).offset(-102)
make.width.equalTo(56) }
topBarView.closeButtonClickEvent = { [weak self] in
self?.closeLive()
}
topBarView.zoomButtonClickEvent = { [weak self] in
self?.enterFloating()
}
topBarView.shareButtonClickEvent = { [weak self] in
self?.shareLive()
}
}
func enterFloating() {
guard let playbackInfo = playbackInfo else {
return
}
YHPlayerManager.shared.enterFloating(from: self, playbackInfo: playbackInfo)
}
private func closeLive() {
YHPlayerManager.shared.stop(type: .main)
dismiss(animated: true)
}
private func shareLive() {
guard let recordedDetailModel = viewModel.recordedDetailModel else {
return
} }
YHShareAlertView.show(image: recordedDetailModel.recorded_image, title: "@" + recordedDetailModel.account, subMessage: recordedDetailModel.recorded_title, linkUrl: recordedDetailModel.recorded_h5_url, isLive: false)
} }
// MARK: - Data Loading // MARK: - Data Loading
private func loadVideoDetail() { private func loadVideoDetail() {
viewModel.getRecordedDetail(id: vodId) { [weak self] recordedDetail, error in viewModel.getRecordedDetail(id: vodId) { [weak self] recordedDetail, error in
guard let self = self else { return } guard let self = self else { return }
if let recordedDetail = recordedDetail { if let recordedDetail = recordedDetail {
self.handleVideoDetailSuccess(recordedDetail) self.handleVideoDetailSuccess(recordedDetail)
} else { } else {
...@@ -79,9 +110,9 @@ class YHVODPlayerViewController: YHBasePlayerViewController { ...@@ -79,9 +110,9 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
YHHUD.flash(message: errorMsg) YHHUD.flash(message: errorMsg)
} }
} }
} }
} }
private func handleVideoDetailSuccess(_ detail: YHRecordedDetailModel) { private func handleVideoDetailSuccess(_ detail: YHRecordedDetailModel) {
// 更新顶部栏信息 // 更新顶部栏信息
topBarView.setupTopBarView( topBarView.setupTopBarView(
...@@ -90,60 +121,198 @@ class YHVODPlayerViewController: YHBasePlayerViewController { ...@@ -90,60 +121,198 @@ class YHVODPlayerViewController: YHBasePlayerViewController {
count: detail.access_num count: detail.access_num
) )
// 如果没有预设URL,使用接口返回的URL播放 // 如果没有预设URL,使用接口返回的URL播放
if currentPlayingURL == nil { if currentPlayingURL == nil, detail.recorded_url.count > 0 {
play(url: detail.recorded_url, title: nil) playbackInfo?.url = detail.recorded_url
playbackInfo?.title = detail.recorded_title
play(url: detail.recorded_url, title: detail.recorded_title)
}
progressControl.configure(with: detail.recordedVideoSlice.compactMap {
$0.tag
})
progressControl.onPlayStatusChanged = { [weak self] isPlaying in
printLog("播放状态:\(isPlaying)")
if isPlaying {
self?.player?.resume()
} else {
self?.player?.pause()
}
}
// 选择片段回调
progressControl.onSegmentSelected = { [weak self] index in
printLog("选择片段:\(index)")
guard detail.recordedVideoSlice.count > index else {
return
}
let item = detail.recordedVideoSlice[index]
self?.player?.seek(to: item.start_second * 1000)
}
// 时间改变回调
progressControl.onTimeChanged = { [weak self] time in
printLog("跳转到:\(time)秒")
self?.player?.seek(to: Int(time * 1000.0))
}
progressControl.updateGiftCount(detail.goods.count)
progressControl.giftButtonClickEvent = { [weak self] in
self?.showGoods()
} }
dropdownView.configure(with: detail.recordedVideoCate.compactMap { $0.name }) }
private func showGoods() {
let list = viewModel.recordedDetailModel?.goods ?? []
let view = YHLiveShopView.show { [weak self] index in
guard list.count > index else {
return
}
let model = list[index]
self?.gotoH5GoodsDetail(id: model.id)
}
view.dataSource = list
view.closeEvent = { [weak self] in
self?.goodsListView = nil
}
view.buyData = { [weak self] index in
guard list.count > index else {
return
}
let model = list[index]
self?.createOrder(id: model.id)
}
goodsListView = view
}
private func createOrder(id: Int) {
YHHUD.show(.progress(message: "加载中..."))
viewModel.createOrder(source: 2, sourceId: vodId, productId: id) { [weak self] orderModel, error in
YHHUD.hide()
guard let self = self else {
return
}
guard let orderModel = orderModel else {
if let errorMsg = error?.errorMsg, errorMsg.count > 0 {
YHHUD.flash(message: errorMsg)
}
return
}
var url = YHBaseUrlManager.shared.curH5URL() + "superAppBridge.html#/order/order-confirm" + "?id=\(orderModel.id)"
if YHLoginManager.shared.isLogin() {
let token = YHLoginManager.shared.h5Token
let urlHasParam = String.hasQueryParameters(urlString: url)
if urlHasParam {
url = url + "&param=" + token
} else {
url = url + "?param=" + token
}
}
var tUrl = url
if !url.contains("navigationH=") {
tUrl = url + "?navigationH=\(k_Height_NavigationtBarAndStatuBar)"
if url.contains("?") {
tUrl = url + "&navigationH=\(k_Height_NavigationtBarAndStatuBar)"
}
}
let vc = YHH5WebViewVC()
vc.url = tUrl
vc.isHideNavigationBar = false
goodsListView?.dismiss()
navigationController?.pushViewController(vc)
}
}
private func gotoH5GoodsDetail(id: Int) {
var url = YHBaseUrlManager.shared.curH5URL() + "superAppBridge.html#/goods/sales-detail" + "?id=\(id)"
if YHLoginManager.shared.isLogin() {
let token = YHLoginManager.shared.h5Token
let urlHasParam = String.hasQueryParameters(urlString: url)
if urlHasParam {
url = url + "&param=" + token
} else {
url = url + "?param=" + token
}
}
var tUrl = url
if !url.contains("navigationH=") {
tUrl = url + "?navigationH=\(k_Height_NavigationtBarAndStatuBar)"
if url.contains("?") {
tUrl = url + "&navigationH=\(k_Height_NavigationtBarAndStatuBar)"
}
}
let vc = YHH5WebViewVC()
vc.url = tUrl
vc.isHideNavigationBar = false
goodsListView?.dismiss()
navigationController?.pushViewController(vc)
} }
} }
// MARK: - YHPlayerDelegate // MARK: - YHPlayerDelegate
extension YHVODPlayerViewController: YHPlayerDelegate { extension YHVODPlayerViewController: YHPlayerDelegate {
func player(_ player: YHPlayer, didChangedTo positionMs: Int, atTimestamp timestampMs: TimeInterval) {
DispatchQueue.main.async {
self.updateProgressControl(player, position: positionMs)
}
}
func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) { func player(_ player: YHPlayer, didChangedToState state: AgoraMediaPlayerState, reason: AgoraMediaPlayerReason) {
switch state { DispatchQueue.main.async {
case .playing: switch state {
controlView.updatePlayButton(isPlaying: true) case .opening:
updatePlayCount() printLog("####.opening: \(player.getDuration())")
case .openCompleted:
case .paused, .stopped: self.updateProgressControl(player, position: player.getPosition())
controlView.updatePlayButton(isPlaying: false) printLog("####.openCompleted: \(player.getDuration())")
case .playing:
case .failed: self.progressControl.setPlaying(true)
showAlert(message: "播放失败,错误原因:\(reason.rawValue)") printLog("####.playing: \(player.getDuration())")
case .paused, .stopped:
default: self.progressControl.setPlaying(false)
break case .failed:
} self.showAlert(message: "播放失败,错误原因:\(reason.rawValue)")
} default:
break
}
}
}
func player(_ player: YHPlayer, didChangedToPosition position: Int) { func player(_ player: YHPlayer, didChangedToPosition position: Int) {
let duration = player.getDuration() // playerKit.getDuration() DispatchQueue.main.async {
guard duration > 0 else { return } self.updateProgressControl(player, position: position)
}
let progress = Float(position) / Float(duration) }
let currentTime = formatTime(position)
let totalTime = formatTime(duration)
controlView.updateProgress(progress,
currentTime: currentTime,
totalTime: totalTime)
}
func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) { func player(_ player: YHPlayer, didReceiveVideoSize size: CGSize) {
// 处理视频尺寸变化,如有需要 // 处理视频尺寸变化,如有需要
DispatchQueue.main.async {
//
}
} }
}
// MARK: - Helper Methods private func updateProgressControl(_ player: YHPlayer, position: Int) {
private extension YHVODPlayerViewController { let duration = player.getDuration()
func updatePlayCount() { guard duration > 0 else { return }
// YHAPIService.shared.updateVideoPlayCount(id: vodId) { [weak self] result in var currentIndex: Int?
// switch result { if let recordedVideoSlice = viewModel.recordedDetailModel?.recordedVideoSlice, recordedVideoSlice.count > 0 {
// case .success: let lastIndex = recordedVideoSlice.count - 1
// break recordedVideoSlice.enumerated().forEach { index, item in
// case .failure(let error): if position >= item.start_second * 1000, position <= item.end_second * 1000 {
// printLog("更新播放次数失败: \(error)") currentIndex = index
// } return
// } } else if index == lastIndex, position >= item.end_second * 1000 {
currentIndex = index
return
} else if position > item.end_second * 1000, index + 1 < lastIndex {
let next = recordedVideoSlice[index + 1]
if position < next.start_second * 1000 {
currentIndex = index
return
}
} else if position <= recordedVideoSlice[0].start_second * 1000 {
currentIndex = 0
return
}
}
}
progressControl.updateTime(Double(position) / 1000.0, duration: Double(duration) / 1000.0, currentIndex: currentIndex)
} }
} }
...@@ -22,6 +22,11 @@ enum YHChatRoomCustomEvent: String { ...@@ -22,6 +22,11 @@ enum YHChatRoomCustomEvent: String {
case liveGoodsRefresh = "chatRoomCustomEventLiveGoodsRefresh" case liveGoodsRefresh = "chatRoomCustomEventLiveGoodsRefresh"
} }
struct YHChatRoomCustomLocal {
static let tipsEvent = "YHLiveRoomTips"
static let tipsKey = "tips"
}
class YHIMHelper: NSObject { class YHIMHelper: NSObject {
static let didLoginEaseIMSuccess = Notification.Name("com.YHIMHelper.didLoginEaseIMSuccess") static let didLoginEaseIMSuccess = Notification.Name("com.YHIMHelper.didLoginEaseIMSuccess")
static let didLogOutEaseIM = Notification.Name("com.YHIMHelper.didLogOutEaseIM") static let didLogOutEaseIM = Notification.Name("com.YHIMHelper.didLogOutEaseIM")
......
//
// YHLiveCreateOrderModel.swift
// galaxy
//
// Created by alexzzw on 2024/12/4.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import Foundation
import SmartCodable
class YHLiveCreateOrderModel: SmartCodable {
var id: Int = 0
required init() {
}
}
...@@ -75,6 +75,7 @@ class YHLiveGoodsItem: SmartCodable { ...@@ -75,6 +75,7 @@ class YHLiveGoodsItem: SmartCodable {
var linePrice: String = "" var linePrice: String = ""
var sn: String = "" var sn: String = ""
var banner: String = "" var banner: String = ""
var flag: Int = 0
required init() { required init() {
} }
......
...@@ -14,9 +14,11 @@ class YHRecordedDetailModel: SmartCodable { ...@@ -14,9 +14,11 @@ class YHRecordedDetailModel: SmartCodable {
var avatar: String = "" var avatar: String = ""
var access_num: Int = 0 var access_num: Int = 0
var tips: String = "" var tips: String = ""
var subject: String = ""
var recorded_url: String = "" var recorded_url: String = ""
var recorded_cover: String = "" var recorded_image: String = ""
var recorded_title: String = ""
var recorded_h5_url: String = ""
var goods: [YHLiveGoodsItem] = [] var goods: [YHLiveGoodsItem] = []
var recordedVideoCate: [YHRecordedVideoCategoryItem] = [] var recordedVideoCate: [YHRecordedVideoCategoryItem] = []
var recordedVideoSlice: [YHRecordedVideoSliceItem] = [] var recordedVideoSlice: [YHRecordedVideoSliceItem] = []
...@@ -34,8 +36,8 @@ class YHRecordedVideoCategoryItem: SmartCodable { ...@@ -34,8 +36,8 @@ class YHRecordedVideoCategoryItem: SmartCodable {
} }
class YHRecordedVideoSliceItem: SmartCodable { class YHRecordedVideoSliceItem: SmartCodable {
var startSecond: String = "" var start_second: Int = 0
var endSecond: String = "" var end_second: Int = 0
var tag: String = "" var tag: String = ""
var index: Int = 0 var index: Int = 0
......
...@@ -67,4 +67,10 @@ class YHLiveMessageCell: UITableViewCell { ...@@ -67,4 +67,10 @@ class YHLiveMessageCell: UITableViewCell {
let contentAtt = ASAttributedString(string: content, .foreground(UIColor.white), .font(UIFont.PFSC_R(ofSize: 13))) let contentAtt = ASAttributedString(string: content, .foreground(UIColor.white), .font(UIFont.PFSC_R(ofSize: 13)))
contentLabel.attributed.text = nickAtt + contentAtt contentLabel.attributed.text = nickAtt + contentAtt
} }
func configureTipsMessage(_ tips: String) {
let tipsIcon = ASAttributedString.init(.image(UIImage(named: "live_msg_tips_icon") ?? UIImage(), .custom(.offset(CGPoint(x: 0, y: -4.0)), size: .init(width: 32, height: 18))))
let contentAtt = (tipsIcon + ASAttributedString(string: tips, .foreground(UIColor.white), .font(UIFont.PFSC_R(ofSize: 13))))
contentLabel.attributed.text = contentAtt
}
} }
...@@ -107,8 +107,11 @@ extension YHLiveMessageListView: UITableViewDelegate, UITableViewDataSource { ...@@ -107,8 +107,11 @@ extension YHLiveMessageListView: UITableViewDelegate, UITableViewDataSource {
printLog(body.text) printLog(body.text)
printLog("\(nickName) : \(body.text)") printLog("\(nickName) : \(body.text)")
content = body.text content = body.text
cell.configureNormalMessage(nickName, content)
} else if let body = message.body as? EMCustomMessageBody, body.event == YHChatRoomCustomLocal.tipsEvent, let customExt = body.customExt, let tips = customExt[YHChatRoomCustomLocal.tipsKey] {
cell.configureTipsMessage(tips)
} }
cell.configureNormalMessage(nickName, content)
return cell return cell
} }
} }
...@@ -12,6 +12,7 @@ import AttributedString ...@@ -12,6 +12,7 @@ import AttributedString
class YHLiveShopView: UIView { class YHLiveShopView: UIView {
var closeEvent: (() -> Void)? var closeEvent: (() -> Void)?
var backData: ((Int) -> Void)? var backData: ((Int) -> Void)?
var buyData: ((Int) -> Void)?
var centerView: UIView! var centerView: UIView!
var titleLabel: UILabel! var titleLabel: UILabel!
var subTitleLabel: UILabel! var subTitleLabel: UILabel!
...@@ -175,7 +176,11 @@ extension YHLiveShopView: UITableViewDelegate, UITableViewDataSource { ...@@ -175,7 +176,11 @@ extension YHLiveShopView: UITableViewDelegate, UITableViewDataSource {
} }
let model = dataSource[indexPath.row] let model = dataSource[indexPath.row]
let cell = tableView.dequeueReusableCell(withClass: YHLiveShopViewCell.self) let cell = tableView.dequeueReusableCell(withClass: YHLiveShopViewCell.self)
model.flag = indexPath.row + 1
cell.dataSource = model cell.dataSource = model
cell.buyEvent = { [weak self] in
self?.buyData?(indexPath.row)
}
return cell return cell
} }
...@@ -218,7 +223,10 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -218,7 +223,10 @@ class YHLiveShopViewCell: UITableViewCell {
var subPriceLabel: UILabel! var subPriceLabel: UILabel!
var flagLabel: UILabel! var flagLabel: UILabel!
var buyButton: UIButton! var buyButton: UIButton!
var addButton: UIButton! //var addButton: UIButton!
// var addEvent: (() -> Void)?
var buyEvent: (() -> Void)?
var dataSource: YHLiveGoodsItem = YHLiveGoodsItem() { var dataSource: YHLiveGoodsItem = YHLiveGoodsItem() {
didSet { didSet {
...@@ -234,6 +242,7 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -234,6 +242,7 @@ class YHLiveShopViewCell: UITableViewCell {
priceLabel.attributed.text = a + b priceLabel.attributed.text = a + b
let c: ASAttributedString = .init("\(dataSource.price)", .font(UIFont.PFSC_R(ofSize: 14)),.foreground(UIColor(hex:0x8993a2)), .strikethrough(.single)) let c: ASAttributedString = .init("\(dataSource.price)", .font(UIFont.PFSC_R(ofSize: 14)),.foreground(UIColor(hex:0x8993a2)), .strikethrough(.single))
subPriceLabel.attributed.text = c subPriceLabel.attributed.text = c
flagLabel.text = "\(dataSource.flag)"
} }
} }
override func awakeFromNib() { override func awakeFromNib() {
...@@ -305,9 +314,6 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -305,9 +314,6 @@ class YHLiveShopViewCell: UITableViewCell {
priceLabel = { priceLabel = {
let view = UILabel() let view = UILabel()
// let a: ASAttributedString = .init("¥", .font(UIFont.PFSC_R(ofSize: 14)),.foreground(UIColor.mainTextColor))
// let b: ASAttributedString = .init("61000", .font(UIFont.PFSC_R(ofSize: 20)),.foreground(UIColor.mainTextColor))
// view.attributed.text = a + b
return view return view
}() }()
contentView.addSubview(priceLabel) contentView.addSubview(priceLabel)
...@@ -320,8 +326,6 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -320,8 +326,6 @@ class YHLiveShopViewCell: UITableViewCell {
subPriceLabel = { subPriceLabel = {
let view = UILabel() let view = UILabel()
// let c: ASAttributedString = .init("¥61000", .font(UIFont.PFSC_R(ofSize: 14)),.foreground(UIColor(hex:0x8993a2)), .strikethrough(.single))
// view.attributed.text = c
return view return view
}() }()
contentView.addSubview(subPriceLabel) contentView.addSubview(subPriceLabel)
...@@ -334,7 +338,6 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -334,7 +338,6 @@ class YHLiveShopViewCell: UITableViewCell {
flagLabel = { flagLabel = {
let label = UILabel() let label = UILabel()
label.text = "1"
label.textAlignment = .center label.textAlignment = .center
label.textColor = .white label.textColor = .white
label.font = UIFont.PFSC_M(ofSize: 10) label.font = UIFont.PFSC_M(ofSize: 10)
...@@ -366,28 +369,30 @@ class YHLiveShopViewCell: UITableViewCell { ...@@ -366,28 +369,30 @@ class YHLiveShopViewCell: UITableViewCell {
make.height.equalTo(28) make.height.equalTo(28)
} }
addButton = { // addButton = {
let button = UIButton(type: .custom) // let button = UIButton(type: .custom)
button.backgroundColor = UIColor(hex: 0xebf0f9) // button.backgroundColor = UIColor(hex: 0xebf0f9)
button.setImage(UIImage(named: "live_shop_add"), for: .normal) // button.setImage(UIImage(named: "live_shop_add"), for: .normal)
button.contentHorizontalAlignment = .center // button.contentHorizontalAlignment = .center
button.addTarget(self, action: #selector(add), for: .touchUpInside) // button.addTarget(self, action: #selector(add), for: .touchUpInside)
return button // return button
}() // }()
contentView.addSubview(addButton) // contentView.addSubview(addButton)
addButton.snp.makeConstraints { make in // addButton.snp.makeConstraints { make in
make.right.equalTo(-88) // make.right.equalTo(-88)
make.width.equalTo(46) // make.width.equalTo(46)
make.bottom.equalTo(centerImageView.snp.bottom) // make.bottom.equalTo(centerImageView.snp.bottom)
make.height.equalTo(28) // make.height.equalTo(28)
} // }
} }
@objc func add() { // @objc func add() {
//YHHUD.flash(message: "已加入购物车") // addEvent?()
} // //YHHUD.flash(message: "已加入购物车")
// }
@objc func buy() { @objc func buy() {
buyEvent?()
//YHHUD.flash(message: "已买") //YHHUD.flash(message: "已买")
} }
} }
//
// YHVideoProgressControl.swift
// galaxy
//
// Created by alexzzw on 2024/12/4.
// Copyright © 2024 https://www.galaxy-immi.com. All rights reserved.
//
import UIKit
class YHVideoProgressControl: UIView {
// MARK: - UI Components
private lazy var segmentCollection: UICollectionView = {
let layout = UICollectionViewFlowLayout()
layout.scrollDirection = .horizontal
layout.minimumLineSpacing = 4
layout.minimumInteritemSpacing = 4
let collection = UICollectionView(frame: .zero, collectionViewLayout: layout)
collection.backgroundColor = .clear
collection.showsHorizontalScrollIndicator = false
collection.register(SegmentCell.self, forCellWithReuseIdentifier: "SegmentCell")
collection.delegate = self
collection.dataSource = self
collection.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)
collection.allowsSelection = true
collection.delaysContentTouches = false
return collection
}()
private lazy var progressSlider: UISlider = {
let slider = UISlider()
slider.minimumTrackTintColor = .white
slider.maximumTrackTintColor = .white.withAlphaComponent(0.24)
slider.setThumbImage(UIImage(named: "live_slider_thumb"), for: .normal)
slider.addTarget(self, action: #selector(sliderValueChanged), for: .valueChanged)
return slider
}()
private lazy var playButton: UIButton = {
let button = UIButton(type: .custom)
button.setImage(UIImage(named: "live_icon_play"), for: .normal)
button.setImage(UIImage(named: "live_icon_pause"), for: .selected)
button.addTarget(self, action: #selector(playButtonTapped), for: .touchUpInside)
return button
}()
private lazy var currentTimeLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .PFSC_R(ofSize: 12)
return label
}()
private lazy var timeSeparator: UIImageView = {
let imageView = UIImageView(image: UIImage(named: "live_time_separator"))
return imageView
}()
private lazy var totalTimeLabel: UILabel = {
let label = UILabel()
label.textColor = .white.withAlphaComponent(0.70)
label.font = .PFSC_R(ofSize: 12)
return label
}()
private lazy var giftButton: UIButton = {
let button = UIButton(type: .custom)
button.setBackgroundImage(UIImage(named: "live_room_msg_gift"), for: .normal)
button.setTitle("0", for: .normal)
button.setTitleColor(UIColor.white, for: .normal)
button.titleLabel?.font = .PFSC_M(ofSize: 10)
button.addTarget(self, action: #selector(giftButtonTapped), for: .touchUpInside)
button.titleEdgeInsets = UIEdgeInsets(top: 5, left: 0, bottom: -5, right: 0)
return button
}()
private lazy var bottomVisualEffectView: UIVisualEffectView = {
let effect = UIBlurEffect(style: .dark)
let view = UIVisualEffectView(effect: effect)
view.alpha = 0.9
return view
}()
// MARK: - Properties
private var segments: [String] = []
private var currentSegmentIndex = 0
private var isPlaying = false
private var currentTime: TimeInterval = 0
private var duration: TimeInterval = 0
var onPlayStatusChanged: ((Bool) -> Void)?
var onSegmentSelected: ((Int) -> Void)?
var onTimeChanged: ((TimeInterval) -> Void)?
var giftButtonClickEvent: (() -> Void)?
// MARK: - Initialization
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - UI Setup
private func setupUI() {
addSubview(bottomVisualEffectView)
addSubview(segmentCollection)
addSubview(progressSlider)
addSubview(playButton)
addSubview(currentTimeLabel)
addSubview(timeSeparator)
addSubview(totalTimeLabel)
addSubview(giftButton)
bottomVisualEffectView.frame = CGRect(x: 0, y: 0, width: bounds.size.width, height: bounds.size.height)
bottomVisualEffectView.snp.makeConstraints { make in
make.top.left.right.equalToSuperview()
make.height.equalToSuperview()
}
// 渐变效果
let gradinetLayer = CAGradientLayer()
gradinetLayer.colors = [UIColor.black.withAlphaComponent(0).cgColor, UIColor.black.withAlphaComponent(0).cgColor, UIColor.black.withAlphaComponent(0.16).cgColor, UIColor.black.withAlphaComponent(0.16).cgColor]
gradinetLayer.startPoint = CGPoint(x: 0.5, y: 0)
gradinetLayer.endPoint = CGPoint(x: 0.5, y: 1)
gradinetLayer.frame = bottomVisualEffectView.bounds
bottomVisualEffectView.layer.mask = gradinetLayer
segmentCollection.snp.makeConstraints { make in
make.top.equalToSuperview().offset(16)
make.left.equalToSuperview().offset(16)
make.height.equalTo(20)
make.right.equalToSuperview().offset(-4)
}
progressSlider.snp.makeConstraints { make in
make.left.equalToSuperview().offset(16)
make.right.equalToSuperview().offset(-16)
make.top.equalTo(segmentCollection.snp.bottom).offset(8)
}
playButton.contentEdgeInsets = UIEdgeInsets(top: 8, left: 8, bottom: 8, right: 8)
playButton.snp.makeConstraints { make in
make.left.equalToSuperview().offset(8)
make.top.equalTo(progressSlider.snp.bottom).offset(12)
make.size.equalTo(CGSize(width: 32, height: 32))
}
currentTimeLabel.snp.makeConstraints { make in
make.left.equalTo(playButton.snp.right).offset(5)
make.centerY.equalTo(playButton)
}
timeSeparator.snp.makeConstraints { make in
make.centerY.equalTo(playButton)
make.left.equalTo(currentTimeLabel.snp.right).offset(8)
make.size.equalTo(CGSize(width: 4, height: 11))
}
totalTimeLabel.snp.makeConstraints { make in
make.left.equalTo(timeSeparator.snp.right).offset(8)
make.centerY.equalTo(playButton)
}
giftButton.snp.makeConstraints { make in
make.right.equalToSuperview().offset(-16)
make.centerY.equalTo(playButton)
make.width.height.equalTo(38)
}
}
// MARK: - Public Methods
func configure(with segments: [String]) {
self.segments = segments
segmentCollection.reloadData()
}
func updateTime(_ current: TimeInterval, duration: TimeInterval, currentIndex: Int? = nil) {
self.currentTime = current
self.duration = duration
progressSlider.value = Float(current / duration)
currentTimeLabel.text = String(format: "%02d:%02d", Int(current) / 60, Int(current) % 60)
totalTimeLabel.text = String(format: "%02d:%02d", Int(duration) / 60, Int(duration) % 60)
if let currentIndex = currentIndex, currentIndex != currentSegmentIndex {
currentSegmentIndex = currentIndex
if segmentCollection.numberOfSections > 0, segmentCollection.numberOfItems(inSection: 0) > currentIndex {
segmentCollection.scrollToItem(at: IndexPath(item: currentIndex, section: 0), at: .centeredHorizontally, animated: true)
segmentCollection.reloadData()
}
}
}
func setPlaying(_ playing: Bool) {
isPlaying = playing
playButton.isSelected = playing
}
func updateGiftCount(_ count: Int) {
guard count > 0 else {
giftButton.isHidden = true
giftButton.setTitle("0", for: .normal)
return
}
giftButton.isHidden = false
if count > 99 {
giftButton.setTitle("99+", for: .normal)
} else {
giftButton.setTitle("\(count)", for: .normal)
}
}
// MARK: - Actions
@objc private func playButtonTapped() {
isPlaying.toggle()
playButton.isSelected = isPlaying
onPlayStatusChanged?(isPlaying)
}
@objc private func sliderValueChanged() {
let time = TimeInterval(progressSlider.value) * duration
onTimeChanged?(time)
}
@objc private func giftButtonTapped() {
giftButtonClickEvent?()
}
}
// MARK: - UICollectionView DataSource & Delegate
extension YHVideoProgressControl: UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout {
func collectionView(_ collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return segments.count
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: "SegmentCell", for: indexPath) as! SegmentCell
cell.configure(with: segments[indexPath.item], isSelected: indexPath.item == currentSegmentIndex)
return cell
}
func collectionView(_ collectionView: UICollectionView, layout collectionViewLayout: UICollectionViewLayout, sizeForItemAt indexPath: IndexPath) -> CGSize {
let text = segments[indexPath.item]
let attr = NSAttributedString(string: text, attributes: [.font: UIFont.systemFont(ofSize: 8)])
let width = attr.yh_width(containerHeight: 20) + 8
return CGSize(width: width, height: 20)
}
func collectionView(_ collectionView: UICollectionView, didSelectItemAt indexPath: IndexPath) {
currentSegmentIndex = indexPath.item
collectionView.scrollToItem(at: indexPath, at: .centeredHorizontally, animated: true)
collectionView.reloadData()
onSegmentSelected?(indexPath.item)
}
}
// MARK: - SegmentCell
class SegmentCell: UICollectionViewCell {
private let titleLabel: UILabel = {
let label = UILabel()
label.textColor = .white
label.font = .systemFont(ofSize: 8)
label.textAlignment = .center
return label
}()
override init(frame: CGRect) {
super.init(frame: frame)
setupUI()
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// super.touchesBegan(touches, with: event)
// print("Cell touched")
// }
private func setupUI() {
backgroundColor = .black.withAlphaComponent(0.3)
layer.cornerRadius = 2
contentView.addSubview(titleLabel)
titleLabel.snp.makeConstraints { make in
make.edges.equalToSuperview()
}
}
func configure(with title: String, isSelected: Bool) {
titleLabel.text = title
titleLabel.textColor = isSelected ? .mainTextColor : .white
backgroundColor = isSelected ? .white : .black.withAlphaComponent(0.3)
}
}
...@@ -19,6 +19,8 @@ class YHLiveSalesViewModel: YHBaseViewModel { ...@@ -19,6 +19,8 @@ class YHLiveSalesViewModel: YHBaseViewModel {
// 获取环信Token信息 // 获取环信Token信息
var huanXinTokenModel: YHHuanXinTokenModel? var huanXinTokenModel: YHHuanXinTokenModel?
// 快速生成订单
var orderModel: YHLiveCreateOrderModel?
} }
extension YHLiveSalesViewModel { extension YHLiveSalesViewModel {
...@@ -175,30 +177,35 @@ extension YHLiveSalesViewModel { ...@@ -175,30 +177,35 @@ extension YHLiveSalesViewModel {
callback(false, err) callback(false, err)
} }
} }
func generateToken(channelName: String, uid: UInt = 0, success: @escaping (String?) -> Void) { /*
guard let certificate = YhConstant.AgoraRtcKit.certificate, !certificate.isEmpty else { 注册环信用户 "source": 0, // 1直播 2 录播
success(nil) "source_id": 0, // 直播或录播id
return "product": {
} "id": 0
let params = ["appCertificate": certificate, }
"appId": YhConstant.AgoraRtcKit.appId, */
"channelName": channelName, func createOrder(source: Int, sourceId: Int, productId: Int, callBackBlock: @escaping (_ orderModel: YHLiveCreateOrderModel?, _ error: YHErrorModel?) -> Void) {
"expire": 900, let strUrl = YHBaseUrlManager.shared.curURL() + YHAllApiName.LiveSales.createOrder
"src": "iOS", let product: [String: Int] = ["id": productId]
"ts": "".timeStamp, let params: [String: Any] = ["source": source, "source_id": sourceId, "product": product]
"type": 1, _ = YHNetRequest.postRequest(url: strUrl, params: params) { [weak self] json, _ in
"uid": "\(uid)"] as [String: Any]
let strUrl = "https://toolbox.bj2.agoralab.co/v1/token/generate"
_ = YHNetRequest.postRequest(url: strUrl, params: params) { response, code in
// 1. json字符串 转 对象 // 1. json字符串 转 对象
if let dict = response.data?.peel as? [String: String] { if json.code == 200 {
let token = dict["token"] guard let dic = json.data?.peel as? [String: Any], let resultModel = YHLiveCreateOrderModel.deserialize(from: dic) else {
success(token) let err = YHErrorModel(errorCode: YHErrorCode.dictParseError.rawValue, errorMsg: YHErrorCode.dictParseError.description())
callBackBlock(nil, err)
return
}
self?.orderModel = resultModel
callBackBlock(resultModel, nil)
} else {
let err = YHErrorModel(errorCode: Int32(json.code), errorMsg: json.msg.isEmpty ? "" : json.msg)
callBackBlock(nil, err)
} }
success(nil)
} failBlock: { err in } failBlock: { err in
success(nil) callBackBlock(nil, err)
} }
} }
} }
......
...@@ -275,7 +275,6 @@ extension YhConstant { ...@@ -275,7 +275,6 @@ extension YhConstant {
struct AgoraRtcKit { struct AgoraRtcKit {
static let appId: String = "f1da9c5b9fb946148761278273f43a14" static let appId: String = "f1da9c5b9fb946148761278273f43a14"
static let certificate: String? = "1776134c2dcb4d60bc3d1983fef02212"
} }
// MARK: - 通知相关 名称 // MARK: - 通知相关 名称
......
...@@ -680,6 +680,8 @@ class YHAllApiName { ...@@ -680,6 +680,8 @@ class YHAllApiName {
static let leaveLiveRoom = "super-app/live/app-live-exit" static let leaveLiveRoom = "super-app/live/app-live-exit"
// 进入直播间--需求登录 // 进入直播间--需求登录
static let joinLiveRoom = "super-app/live/app-live-join" static let joinLiveRoom = "super-app/live/app-live-join"
// 创建订单-立即购买
static let createOrder = "super-app/presale/app/order/create"
} }
struct AIChat { struct AIChat {
......
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Mask group@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Mask group@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Remove-bg.ai_1732537885367 1.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Remove-bg.ai_1732537885367 1@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "Vector 6602@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "Vector 6602@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
...@@ -5,12 +5,12 @@ ...@@ -5,12 +5,12 @@
"scale" : "1x" "scale" : "1x"
}, },
{ {
"filename" : "Frame.png", "filename" : "Frame@2x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "2x" "scale" : "2x"
}, },
{ {
"filename" : "Frame-1.png", "filename" : "Frame@3x.png",
"idiom" : "universal", "idiom" : "universal",
"scale" : "3x" "scale" : "3x"
} }
......
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_icon_pause@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_icon_pause@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_icon_play@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_icon_play@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_msg_tips_icon@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_msg_tips_icon@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_slider_thumb@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_slider_thumb@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
{
"images" : [
{
"idiom" : "universal",
"scale" : "1x"
},
{
"filename" : "live_time_separator@2x.png",
"idiom" : "universal",
"scale" : "2x"
},
{
"filename" : "live_time_separator@3x.png",
"idiom" : "universal",
"scale" : "3x"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment