// // PhotoBrowserControllerExtRole.swift // Crush // // Created by Leon on 2025/7/27. // extension PhotoBrowserController { // MARK: - 🚩My Role func createOperateViewOfRoleMine() { bottomGradientContainer?.isHidden = false rolePhotoUnlockEntry = { let v = RolePhotoUnlockEntryView() bottomGradientOperateStackV.addArrangedSubview(v) return v }() setDefaultEntry = { let v = RolePhotoSetDefaultEntryView() bottomGradientOperateStackV.addArrangedSubview(v) return v }() moreButton = { let v = EPIconGhostButton(radius: .none, iconSize: .medium, iconCode: .more) v.addTarget(self, action: #selector(tapNaviMoreInMyAIAlbum(sender:)), for: .touchUpInside) titleView.rightStackH.addArrangedSubview(v) titleView.paddingRightForRightStack = 16 v.snp.makeConstraints { make in make.size.equalTo(v.bgImageSize()) } return v }() setupRoleEvents() } func setupRoleEvents() { rolePhotoUnlockEntry.editUnlockWayAction = { [weak self] in self?.tapToSetupUnlockWay() } setDefaultEntry.setButton.addTarget(self, action: #selector(tapSetRoleDefaultButton), for: .touchUpInside) } // MARK: Action func tapToSetupUnlockWay() { let vc = RolePhotoUnlockWaySetController() // 获取当前item let imageModel = imageModels[currentIndex] vc.browseModel = imageModel vc.priceUpdateAction = { [weak self] price in let unlockPrice = price ?? 0 imageModel.aiAlbum.unlockPrice = unlockPrice if unlockPrice > 0 { imageModel.aiAlbum.isDefault = false } self?.reloadStatesByModel(model: imageModel) } navigationController?.pushViewController(vc, animated: true) } @objc func tapNaviMoreInMyAIAlbum(sender: UIButton) { let pop = CLPopoverListView() let rect = view.convert(sender.frame, from: sender.superview) pop.setupDeletePopover(rect.insetBy(dx: 0.0, dy: -6.0), inView: view) { [weak self] in self?.deleteAlbumOfMyAIRoleTapAction() } } @objc private func tapSetRoleDefaultButton() { let imageModel = imageModels[currentIndex] let unlockPrice = imageModel.aiAlbum.unlockPrice if unlockPrice > 0{ let alert = Alert(title: "默认图片", text: "设置为默认图片后,图片的解锁方式只能为“免费”") let action1 = AlertAction(title: "Confirm", actionStyle: .confirm) {[weak self] in self?.doRequestSetRoleDefaultAlbum() } let action2 = AlertAction(title: "Cancel", actionStyle: .cancel) alert.addAction(action1) alert.addAction(action2) alert.show() }else{ doRequestSetRoleDefaultAlbum() } } // MARK: Functions private func doRequestSetRoleDefaultAlbum(){ let imageModel = imageModels[currentIndex] let aiUid = imageModel.aiId let albumId = imageModel.aiAlbum.albumId Hud.showIndicator() AIRoleProvider.request(.aiRolePhotoDefaultSet(aiUid: aiUid, albumId: albumId), modelType: AnyCodable.self) { [weak self] result in Hud.hideIndicator() switch result { case .success: // dlog("set default results") imageModel.aiAlbum.isDefault = true self?.reloadStatesByModel(model: imageModel) self?.setDefaultEntry.setupIsDefault(true) NotificationCenter.post(name: .aiRoleAlbumPhotoInfoChanged) NotificationCenter.post(name: .aiRoleInfoChanged) // 主页背景要更新 case .failure: break } } } @objc func deleteAlbumOfMyAIRoleTapAction() { guard currentIndex >= 0, currentIndex < imageModels.count else { return } let model = imageModels[currentIndex] if model.aiAlbum.isDefault.boolValue{ let alert = Alert(title: "删除图片", text: "不可删除封面默认图片,该图片会作为在个人主页头图,卡片主图,聊天背景") let action1 = AlertAction(title: "Got it", actionStyle: .confirm) alert .addAction(action1) alert.show() return } let alert = Alert(title: "删除图片", text: "图片删除后不可恢复。已经付费解锁过该图片的用户依然可以在角色的相册中看到该图片。") let action1 = AlertAction(title: "Delete", actionStyle: .destructive) {[weak self] in self?.deleteTapActionDone() } let action2 = AlertAction(title: "Cancel", actionStyle: .cancel) alert.addAction(action1) alert.addAction(action2) alert.show() } private func deleteTapActionDone() { let model = imageModels[currentIndex] model.deleteTapBlock?(model) { [weak self] isSuccess in guard let self = self else { return } if isSuccess { self.imageModels.remove(at: self.currentIndex) if self.imageModels.isEmpty { PhotoBrowserManager.shared.hidePhotoBrowser() } else { if self.currentIndex >= self.imageModels.count { self.currentIndex -= 1 } self.setupViews() self.reloadCountLabel(hidden: false) } } } } // MARK: - 🚩Other's Role func createOperateViewOfOthers() { roleOthersContainer = { let v = SelectiveDeliveryEventsView() v.backgroundColor = .clear view.insertSubview(v, belowSubview: titleView) v.snp.makeConstraints { make in make.edges.equalToSuperview() } return v }() // roleOthersContainer?.isUserInteractionEnabled = false roleOthersCenterLock = { let v = UIImageView() v.image = MWIconFont.image(fromIcon: .iconPrivate, size: CGSize(width: 40, height: 40), color: .white) roleOthersContainer?.addSubview(v) v.snp.makeConstraints { make in make.center.equalToSuperview() } return v }() let bottomStackH = { let v = UIStackView() v.spacing = 48 v.alignment = .center roleOthersContainer?.addSubview(v) v.snp.makeConstraints { make in make.height.equalTo(48) make.bottom.equalToSuperview().offset(-16 - UIWindow.safeAreaBottom * 0.5) make.trailing.equalToSuperview().offset(-24) } return v }() iconUnlockButton = { let v = StyleButton() v.setContentHuggingPriority(UILayoutPriority(rawValue: 246), for: .horizontal) v.primary(size: .large) v.addTarget(self, action: #selector(tapUnlockPhoto), for: .touchUpInside) bottomStackH.addArrangedSubview(v) return v }() iconLabel = { let v = CLIconLabel() v.iconImageView.image = UIImage.icon32Diamond v.iconSize = CGSize(width: 20, height: 20) roleOthersContainer?.addSubview(v) v.snp.makeConstraints { make in make.centerY.equalTo(bottomStackH) make.leading.equalToSuperview().offset(24) make.trailing.lessThanOrEqualTo(bottomStackH.snp.leading).offset(-8) } return v }() iconLabel?.contentLabel.text = "1234" let attributeString = StyleButton.getUnlockAttributeTitleByCoin(coin: 123, string: "unlock") iconUnlockButton?.setAttributedTitle(attributeString, for: .normal) } @objc func tapUnlockPhoto() { guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{ PhotoBrowserManager.shared.hidePhotoBrowser() return } let photoModel = imageModels[currentIndex] var params = [String: Any]() if let album = photoModel.aiAlbum { if let aiId = photoModel.aiId { params.updateValue(aiId, forKey: "aiId") } let albumId = album.albumId params.updateValue(albumId, forKey: "albumId") } else if let model = photoModel.sessionModel { if let aiId = photoModel.aiId { params.updateValue(aiId, forKey: "aiId") } if let albumId = model.baseRemoteInfo?.customAttachment?.albumId { params.updateValue(albumId, forKey: "albumId") } if let messageServerId = model.v2msg?.messageServerId { params.updateValue(messageServerId, forKey: "messageServerId") } } else { return } // 测试解锁 let imgTest = "https://hhb.crushlevel.ai/dev/role/17581021489486439.jpg" // if var album = photoModel.aiAlbum{ // // album.lockStatus = .unlock // album.imgUrl = nil // album.img1 = "https://sub.crushlevel.ai/files/b/test/439076604280833/0aec65fdd92c404a9e3f8acac8e2c1ba.jpg?x-oss-process=image/resize,w_468,h_600&v=1&Policy=eyJTdGF0ZW1lbnQiOlt7IlJlc291cmNlIjoiaHR0cHM6Ly9zdWIuY3J1c2hsZXZlbC5haS9maWxlcy9iL3Rlc3QvNDM5MDc2NjA0MjgwODMzLzBhZWM2NWZkZDkyYzQwNGE5ZTNmOGFjYWM4ZTJjMWJhLmpwZz94LW9zcy1wcm9jZXNzPWltYWdlL3Jlc2l6ZSx3XzQ2OCxoXzYwMCZ2PTEiLCJDb25kaXRpb24iOnsiRGF0ZUxlc3NUaGFuIjp7IkFXUzpFcG9jaFRpbWUiOjE3NTgyMjgzNjd9fX1dfQ__&Signature=Jv5jE4d072OkFyYq5dxa3unXVaLWSNRddJEIIm42j4zmg36Yq8HGxVXz4X954ERmujm8zCluiVGMbIx-EH9JlQ1s~zbhgOo-lliaDGl977EsCVLBPsf639WFpdCXqnf3QAM7LwhuCbmdi~PBybJhKS31nEKoS53idOB4JyEXXp9wv3RpVjDoL4JF8C22H8k5gJ0hpt3NhfZiTSbsSRJBApNHfCKAFm6PEKQ8dAQ6f7rRxHwPTkbhHi~fRUahuJI10Ti-8t2Hem4akTV4plmgz9chsLCfHBNh5WDrwzoCZRDDljpaNcwpkZ5Mew23seKphb5iLxHgVP3WGAwXb0BoRA__&Key-Pair-Id=K35ZQ246Y37QV8" // photoModel.imageUrl = album.img1 // album.unlockPrice = 0 // photoModel.aiAlbum = album // self.reloadStates(index: self.currentIndex) // self.reloadCurrentZoomImage() // } // return // #warning("test") // if let model = photoModel.sessionModel { // Hud.hideInidcator() // model.baseRemoteInfo?.customAttachment?.unlockPrice = 0 // model.baseRemoteInfo?.customAttachment?.url = imgTest // // photoModel.imageUrl = imgTest // photoModel.sessionModel = model // // // Reload // self.reloadStates(index: self.currentIndex) // self.reloadCurrentZoomImage() // } // return Hud.showIndicator() ChatProvider.request(.aiUnlockAlbumImg(params: params), modelType: AIAlbumUnlockImages.self) { [weak self] result in Hud.hideIndicator() switch result { case let .success(unlockData): guard let img1 = unlockData?.img1 else { PhotoBrowserManager.shared.hidePhotoBrowser() return } // 🚩"1.发送通知、2.更新当前大图UI" // 更新临时数据 guard let self = self else { return } if let album = photoModel.aiAlbum { album.lockStatus = .unlock album.img1 = img1 album.imgUrl = img1 album.unlockPrice = 0 photoModel.imageUrl = album.img1 photoModel.aiAlbum = album // Reload self.reloadStates(index: self.currentIndex) self.reloadCurrentZoomImage() NotificationCenter.post(name: .aiRoleAlbumPhotoInfoChanged, object: album) }else if let model = photoModel.sessionModel { model.baseRemoteInfo?.customAttachment?.unlockPrice = 0 model.baseRemoteInfo?.customAttachment?.url = img1 photoModel.imageUrl = img1 photoModel.sessionModel = model // Reload self.reloadStates(index: self.currentIndex) self.reloadCurrentZoomImage() NotificationCenter.post(name: .aiRoleAlbumPhotoInfoChanged, object: model) }else{ assert(false, "need update") } WalletCore.shared.refreshWallet() case .failure: break } } } @objc func tapOperateChipButton(_ sender: EPChipContrastButton) { guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{ PhotoBrowserManager.shared.hidePhotoBrowser() return } } @objc func tapLikeButton() { guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{ PhotoBrowserManager.shared.hidePhotoBrowser() return } let photoModel = imageModels[currentIndex] guard let likeView = likeView else { return } // 在主页、IM中,这里是 点赞 if let album = photoModel.aiAlbum { let albumId = album.albumId if album.likedStatus == .liked { // 取消点赞 album.likedStatus = .cancel album.likedCount = max((album.likedCount ?? 0) - 1, 0) PhotosViewModel.shared.album = album likeView.bind(aiAlbum: album) AIRoleProvider.request(.aiRolePhotoLikeOrNo(albumId: albumId, likedStatus: .cancel), modelType: EmptyModel.self) { [weak self] result in self?.handleLikeRequestResult(result: result, albumId: albumId, isLike: false) } } else { // 点赞 likeView.playLotteLike { _ in guard var album = photoModel.aiAlbum else { return } album.likedStatus = .liked album.likedCount = (album.likedCount ?? 0) + 1 PhotosViewModel.shared.album = album likeView.bind(aiAlbum: album) } AIRoleProvider.request(.aiRolePhotoLikeOrNo(albumId: albumId, likedStatus: .liked), modelType: EmptyModel.self) { [weak self] result in self?.handleLikeRequestResult(result: result, albumId: albumId, isLike: true) } } } } // 统一处理请求结果 private func handleLikeRequestResult(result: Result, albumId: Int, isLike: Bool) { isRequesting = false let photoModel = imageModels[currentIndex] guard let likeView = likeView, let album = photoModel.aiAlbum else { return } likeView.likeButton.isEnabled = true switch result { case .success: // 请求成功,无需额外处理 break case .failure: // 请求失败,恢复状态 if isLike { // 点赞失败,恢复为未点赞状态 album.likedStatus = .cancel album.likedCount = max((album.likedCount ?? 0) - 1, 0) } else { // 取消点赞失败,恢复为点赞状态 album.likedStatus = .liked album.likedCount = (album.likedCount ?? 0) + 1 } PhotosViewModel.shared.album = album likeView.bind(aiAlbum: album) } } // MARK: - Helper /// like : 💎 20 unlock func getUnlockAttributeTitleByCoin(coin: Int) -> NSAttributedString { let text = " \(coin) unlock" let attributedString = NSMutableAttributedString() if let iconImage = UIImage(named: "icon_32_diamond") { // 替换为你的图标名称 let attachment = NSTextAttachment() attachment.image = iconImage let iconSize = CGSize(width: 24, height: 24) // 调整为你需要的大小 attachment.bounds = CGRect(origin: .init(x: 0, y: -4), size: iconSize) let iconAttributedString = NSAttributedString(attachment: attachment) attributedString.append(iconAttributedString) } // 添加文字并设置样式 let textAttributedString = text.withAttributes([ .font(.t.tll), // 设置字体 .textColor(.white), ]) attributedString.append(textAttributedString) return attributedString } }