Visual_Novel_iOS/crush/Crush/Src/Modules/Home/View/HomePageRootView.swift

575 lines
20 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// 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()
}
}
}