// // HomePageRootView.swift // Crush // // Created by Leon on 2025/7/22. // import UIKit class HomePageRootView: UIView { var emptyView: UIView! private var cardContainer: UIView! private var bgTopIv: UIImageView! private var dragCardContainer: MeetDragCardContainer! private var dataArray: [MeetCard] = [] // Flag var swipeCardCount: Int = 0 // 计数 weak var viewModel: MeetViewModel! weak var weakGiftSheet: GiftAIRoleSheet? override init(frame: CGRect) { super.init(frame: frame) setupViews() setupData() setupEvent() } required init?(coder: NSCoder) { fatalError("init(coder:) has not been implemented") } private func setupViews() { emptyView = { let v = UIView() v.showEmpty(text: "暂无卡片") addSubview(v) v.snp.makeConstraints { make in make.leading.trailing.equalToSuperview() make.bottom.equalToSuperview().offset(-UIWindow.tabBarTotalHeight) make.top.equalToSuperview().offset(UIWindow.navBarTotalHeight) } return v }() cardContainer = { let v = UIView() addSubview(v) v.snp.makeConstraints { make in make.edges.equalToSuperview() } return v }() // 创建拖拽卡片容器 bgTopIv = { let v = UIImageView() v.image = UIImage(named: "meet_top_bg") cardContainer.addSubview(v) v.snp.makeConstraints { make in make.top.leading.trailing.equalToSuperview() make.height.equalTo(v.snp.width).multipliedBy(200/393.0) } return v }() let cardWidth = EncounterAICardView.cardWidth let cardHeight = EncounterAICardView.cardHeight dragCardContainer = MeetDragCardContainer(frame: CGRect(x: 0, y: 0, width: cardWidth, height: cardHeight)) cardContainer.addSubview(dragCardContainer) dragCardContainer.snp.makeConstraints { make in make.centerX.equalToSuperview() make.top.equalToSuperview().offset(UIWindow.navBarTotalHeight + 12) make.width.equalTo(cardWidth) make.height.equalTo(cardHeight) } // 设置数据源和代理 dragCardContainer.dataSource = self dragCardContainer.delegate = self // 初始状态都隐藏 emptyView.isHidden = true //cardContainer.isHidden = true } private func setupData(){ setupDragCardContainer() } private func setupEvent(){ } private func setupDragCardContainer() { // 重新加载数据 dragCardContainer.reloadData() } // MARK: - mark func setupViewModel(vm: MeetViewModel){ viewModel = vm // 绑定ViewModel回调 viewModel?.onCardsLoaded = { [weak self] cards in self?.dataArray = cards self?.dragCardContainer.reloadData() if self?.viewModel.requestParams.pn == 1{ self?.showEmptyPlaceholder(empty: cards.count == 0) } } viewModel?.onCardsMoreLoaded = { [weak self] cards in if cards.count == 0 { self?.showEmptyPlaceholder(empty: true) } } viewModel?.onLoadError = { error in dlog("Meet加载卡片失败: \(error)") } } // MARK: - Functions private func doReportLike(card: MeetCard?){ guard let aiId = card?.aiId else {return} swipeCardCount += 1 var params = [String:Any]() params.updateValue(aiId, forKey: "aiId") params.updateValue(true, forKey: "lk") DiscoverProvider.request(.meetLikeOrReport(params: params), modelType: MeetCardReportResponse.self) {[weak self] result in switch result { case .success(let model): self?.dealLikeAfter(aiId: aiId,card: card, obj: model) case .failure: break } } } private func doReportDislike(card: MeetCard?){ guard let aiId = card?.aiId else {return} swipeCardCount += 1 var params = [String:Any]() params.updateValue(aiId, forKey: "aiId") params.updateValue(true, forKey: "lk") DiscoverProvider.request(.meetLikeOrReport(params: params), modelType: MeetCardReportResponse.self) {result in switch result { case .success: break // do nothing case .failure: break } } } private func dealLikeAfter(aiId: Int, card: MeetCard?, obj:MeetCardReportResponse?){ guard let response = obj else {return} // ... if response.bd.boolValue { // #warning("test") // let vc = MeetMatchedController() // vc.card = card! //// vc.modalPresentationStyle = .fullScreen // let navc = CLNavigationController(rootViewController: vc) //// self?.viewController()?.present(navc, animated: true) // UIWindow.getTopViewController()?.present(navc, animated: true) // return // 调用绑定 DiscoverProvider.request(.meetReportBind(aiId: aiId), modelType: MeetMatchInfo.self) { result in switch result { case .success(let model): if let resultObj = model{ let vc = MeetMatchedController() vc.info = resultObj let navc = CLNavigationController(rootViewController: vc) UIWindow.getTopViewController()?.present(navc, animated: true) }else{ dlog("❌meetReportBind result is nil") let vc = MeetMatchedController() vc.card = card! let navc = CLNavigationController(rootViewController: vc) // self?.viewController()?.present(navc, animated: true) UIWindow.getTopViewController()?.present(navc, animated: true) } case .failure: break } } } if response.rc.boolValue{ DiscoverProvider.request(.meetRcAndRetrunBlurAlbum, modelType: MeetReportAdmirerReturnAlbum.self) {[weak self] result in switch result { case .success(let model): self?.doShowSecretAdmirerAlert(aiId: aiId, photo: model) case .failure: break } } } } private func doShowSecretAdmirerAlert(aiId: Int?, photo: MeetReportAdmirerReturnAlbum?){ guard let thePhoto = photo else {return} let content = "Someone is secretly in love with you\n" let alert = Alert(title: "Secret Admirer", text: content, image: UIImage(named: "heart_meet_48")!) alert.masTopToImageBgForImageView?.update(offset: 42) alert.setupContentStackSpacing(14) alert.setupBottomBlurImageUrl(url: thePhoto.img1) //let coin = Coin(cents: thePhoto.unlockPrice) let coin = Coin(cents: 5000) // 50 coin let string = "\(coin.formatted) unlock" let aString = NSAttributedString.getIconTitleAttributeByWords(words: string, iconImage: UIImage.icon32Diamond, iconSize: CGSize(width: 24, height: 24), textFont: .t.tll, textColor: .white) let confirmCoin = AlertAction(attributedTitle: aString, actionStyle: .confirm) {[weak self] in // 解锁,🔓 self?.doUnlockLikeYou(aiId: aiId, albumId: photo?.albumId) } let cancel = AlertAction(title: "Cancel", actionStyle: .cancel, block: nil) alert.addAction(confirmCoin) alert.addAction(cancel) alert.show() alert.layoutIfNeeded() } private func doUnlockLikeYou(aiId: Int? , albumId: Int?){ guard let theAIid = aiId, let theAlbumId = albumId else { return } Hud.showIndicator() DiscoverProvider.request(.meetUnlockLikeYou(aiId: theAIid, albumId: theAlbumId), modelType: AIAlbumUnlockImages.self) {[weak self] result in switch result { case .success: self?.doGetLikeYouAICardInfo(aiId: theAIid) case .failure: Hud.hideIndicator() } } } func doGetLikeYouAICardInfo(aiId: Int?){ guard let theAiId = aiId else{ Hud.hideIndicator() return } DiscoverProvider.request(.meetCardDetail(aiId: theAiId), modelType: MeetCard.self) {[weak self] result in Hud.hideIndicator() switch result { case .success(let model): model?.secretAdmirer = true self?.doRestoreCard(card: model) case .failure: break } } } private func doRestoreCard(card : MeetCard?){ guard let data = card else {return} if let tmpStoreNopeCardView = dragCardContainer.tmpStoreNopeCardView as? EncounterAICardView{ tmpStoreNopeCardView.config(data) dragCardContainer.addCardView(tmpStoreNopeCardView, fromDirection: .default) }else{ dlog("❌ none tmpStoreNopeCardView") } } // MARK: - Helper func showEmptyPlaceholder(empty: Bool){ emptyView.isHidden = !empty cardContainer.isHidden = empty } // MARK: - Test func testFunction(){ doGetLikeYouAICardInfo(aiId: 444190968774657) } } // MARK: - EncounterAICardViewDelegate extension HomePageRootView: EncounterAICardViewDelegate { func cardViewTapLike(_ obj: MeetCard?) { print("点击了喜欢按钮: \(String(describing: obj?.nickname))") if UserCore.shared.isLogin() == false && swipeCardCount >= 3{ UserCore.shared.checkUserLoginIfNotPushUserToLogin() return } dragCardContainer.removeCardViewForDirection(.right) doReportLike(card: obj) } func cardViewTapGift(_ obj: MeetCard?) { // print("点击了礼物按钮: \(String(describing: obj?.nickname))") guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{ return } let vc = GiftAIRoleSheet() vc.giftView.delegate = self vc.giftView.targetHeartbeatValue = obj?.heartbeatVal ?? 0 weakGiftSheet = vc vc.show() } func cardViewTapDislike(_ obj: MeetCard?) { print("点击了不喜欢按钮: \(String(describing: obj?.nickname))") if UserCore.shared.isLogin() == false && swipeCardCount >= 3{ UserCore.shared.checkUserLoginIfNotPushUserToLogin() return } dragCardContainer.removeCardViewForDirection(.left) doReportDislike(card: obj) } func cardViewTapHeader(_ obj: MeetCard?, cardView: EncounterAICardView?){ guard let data = obj, let albumList = data.albumList, let aiId = obj?.aiId else{return} let screenRect = cardView?.screenRect ?? .zero let currentImageUrl = obj?.imageUrl ?? "" var photoModels = [PhotoBrowserModel]() var browseIndex = 0 for (index, per) in albumList.enumerated() { let model = PhotoBrowserModel() model.aiAlbum = per model.aiId = aiId model.imageUrl = per.getImgUrl() model.image = cardView?.header.imageView.image ?? UIImage() model.sourceRect = screenRect if model.imageUrl == currentImageUrl{ browseIndex = index } photoModels.append(model) } ImageBrowser.show(models: photoModels, index: browseIndex, type: .roleOthersInAlbum) } } extension HomePageRootView: GiftGridSendViewDelegate{ func giftSend(giftId: Int, gift: GiftDictModel, num: Int){ guard let card = dragCardContainer.getCurrentShowCardView() as? EncounterAICardView, let info = card.card else{ return } guard let aiId = info.aiId else{ return } #warning("test") weakGiftSheet?.dismiss() dragCardContainer.removeCardViewForDirection(.right) let vc = MeetMatchedController() vc.card = info vc.gift = gift vc.giftCount = num let navc = CLNavigationController(rootViewController: vc) UIWindow.getTopViewController()?.present(navc, animated: true) return // var params = [String: Any]() // // params.updateValue(aiId, forKey: "aiId") // params.updateValue(giftId, forKey: "giftId") // params.updateValue(num, forKey: "num") // params.updateValue("HOME", forKey: "scene") // // Hud.showIndicator() // ChatProvider.request(.aiUserGiftSend(params: params), modelType: EmptyModel.self) {[weak self] result in // Hud.hideIndicator() // switch result { // case .success: // self?.weakGiftSheet?.dismiss() // // self?.dragCardContainer.removeCardViewForDirection(.right) // let vc = MeetMatchedController() // vc.card = info // vc.gift = gift // vc.giftCount = num // let navc = CLNavigationController(rootViewController: vc) // UIWindow.getTopViewController()?.present(navc, animated: true) // case .failure: // break // } // } } func giftGoHeartbeatPage(){ guard let card = dragCardContainer.getCurrentShowCardView() as? EncounterAICardView, let info = card.card else{ return } guard let aiId = info.aiId else{ return } weakGiftSheet?.dismiss() AppRouter.goAIHeartBeatLevelPage(aiId: aiId) } func giftGoVIPCenter(){ weakGiftSheet?.dismiss() AppRouter.goVIPCenter() } func giftNumPick(num: Int){ guard let window = UIWindow.getTopDisplayWindow(), let sheet = weakGiftSheet else { return } // 🔥 Assuming topDisplayWindow is a custom UIView extension method guard let giftSendView = sheet.giftView else{ return } let rect1 = giftSendView.countImg.convert(giftSendView.countImg.frame, from: giftSendView.countImg.superview) let rect2 = giftSendView.countImg.convert(rect1, to: window) let vc = GiftCountChooseController(sourceRect: rect2, selectedValue: "\(num)") vc.chooseBlock = { [weak self] count in // ... Handle count selection self?.weakGiftSheet?.giftView.num = Int(count) ?? 1 } vc.popoverDismiss = { [weak self] in // ... Handle popover dismissal self?.weakGiftSheet?.giftView.countImg.transform = .identity } viewController()?.navigationController?.addChild(vc) sheet.addSubview(vc.view) UIView.animate(withDuration: 0.2) { [weak self] in self?.weakGiftSheet?.giftView.countImg.transform = CGAffineTransform(rotationAngle: .pi) } } } extension HomePageRootView :MeetDragCardContainerDataSource { func numberOfRowsInYFLDragCardContainer(_ container: MeetDragCardContainer) -> Int { return dataArray.count } func container(_ container: MeetDragCardContainer, viewForRowsAt index: Int) -> MeetDragCardView { dlog("🐟index:\(index)") let cardView = EncounterAICardView() let card = dataArray[index] cardView.config(card) cardView.delegate = self return cardView } } // MARK: - MeetDragCardContainerDelegate extension HomePageRootView: MeetDragCardContainerDelegate { func container(_ container: MeetDragCardContainer, didSelectRowAt index: Int) { print("「Meet」点击了整体卡片: \(index)") } func container(_ container: MeetDragCardContainer, dataSourceIsEmpty isEmpty: Bool) { if isEmpty { print("「Meet」数据源为空,可以加载更多数据") // 这里可以加载更多数据 if viewModel.cards.count > 0{ // 说明是后续加载 loadMoreData() // showEmptyPlaceholder(empty: true) } } } func container(_ container: MeetDragCardContainer, canDragForCardView cardView: MeetDragCardView) -> Bool { return true } func container(_ container: MeetDragCardContainer, dargingForCardView cardView: MeetDragCardView, direction: ContainerDragDirection, widthRate: CGFloat, heightRate: CGFloat) { // 拖拽过程中的回调 if direction == .left { container.showNopeLogo(true, widthRate: CGFloat(abs(widthRate))) print("🐟左滑ing \(widthRate)") } else if direction == .right { container.showLikeLogo(true, widthRate: CGFloat(abs(widthRate))) print("🐟右滑ing \(widthRate)") } else { container.showNopeLogo(false, widthRate: 0) container.showLikeLogo(false, widthRate: 0) } } func container(_ container: MeetDragCardContainer, canDragFinishForDirection direction: ContainerDragDirection, forCardView cardView: MeetDragCardView) -> Bool { if UserCore.shared.isLogin(){ return true } if swipeCardCount >= 3{ return UserCore.shared.checkUserLoginIfNotPushUserToLogin() } return true } func container(_ container: MeetDragCardContainer, dragDidFinshForDirection direction: ContainerDragDirection, forCardView cardView: MeetDragCardView) { print("🐟卡片拖拽完成,方向: \(direction)") let aiCardView = cardView as! EncounterAICardView // 显示相应的动画 switch direction { case .left: //container.showNopeLottie() container.showNopeLogo() doReportDislike(card: aiCardView.card) case .right: //container.showLikeLottie() container.showLikeLogo() doReportLike(card: aiCardView.card) default: break } // 检查是否需要预加载 checkAndPreloadIfNeeded() } func container(_ container: MeetDragCardContainer, lookingBack direction: ContainerDragDirection, forCardView cardView: MeetDragCardView) { print("🐟卡片回看,方向: \(direction)") } func container(_ container: MeetDragCardContainer, enterSmallCardMode smallCardMode: Bool, forCardView cardView: MeetDragCardView) { print("🐟进入小卡片模式: \(smallCardMode)") } private func loadMoreData() { dlog("🐟try load more datas") // 加载更多数据 viewModel?.loadMoreCards() } // 检查并预加载数据 private func checkAndPreloadIfNeeded() { guard let viewModel = viewModel else { return } // 计算剩余卡片数量 let remainingCount = dataArray.count - dragCardContainer.getCurrentShowCardViewIndex() // 如果剩余卡片数量少于等于5张,且不在加载中,则预加载 if viewModel.shouldPreload(remainingCount: remainingCount) { dlog("🐟预加载数据,剩余卡片数量: \(remainingCount)") loadMoreData() } } }