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

575 lines
20 KiB
Swift
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//
// 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()
}
}
}