Visual_Novel_iOS/crush/Crush/Src/Modules/Role/Home/RoleHomePagerController.swift

447 lines
16 KiB
Swift
Raw Normal View History

2025-10-09 10:29:35 +00:00
//
// RoleHomePagerController.swift
// Crush
//
// Created by Leon on 2025/7/24.
//
import Combine
import UIKit
class RoleHomePagerController: CLViewController<RoleHomePagerView> {
var navRightButton: EPIconGhostButton!
var likeView : HeartLikeCountView!
var aiId: Int = 0
@Published var info: AIRoleInfo?
///
var avatarModel: UploadPhotoM?
//
private var isRequesting = false
private var cancellables = Set<AnyCancellable>()
override func viewDidLoad() {
super.viewDidLoad()
setupViews()
setupDats()
setupEvents()
}
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
disabledFullScreenPan()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
enabledFullScreenPan()
}
private func setupViews() {
navigationView.alpha0Title = "User Name"
navigationView.bgView.alpha = 0
}
private func setupDats() {
loadAIInfo(block: nil)
container.aboutVc.aiId = aiId
container.albumVc.aiId = aiId
}
private func loadAIInfo(block: (() -> Void)?) {
Hud.showIndicator()
AIRoleProvider.request(.queryAIBaseInfo(aiId: aiId), modelType: AIRoleInfo.self) { [weak self] result in
Hud.hideIndicator()
switch result {
case let .success(success):
self?.info = success
case let .failure(failure):
switch failure {
case let .serviceError(code, _):
if code == .aiRoleNotExist{
let alert = Alert(title: "The Role has been deleted", text: "The role has been deleted and cannot be accessed and interacted.")
let action1 = AlertAction(title: "Got it", actionStyle: .confirm) {[weak self] in
self?.close()
}
alert.addAction(action1)
alert.show()
}
default:
break
}
}
block?()
}
}
private func setupEvents() {
NotificationCenter.default.addObserver(self, selector: #selector(notificationAIRoleInfoUpdated(noti:)), name: AppNotificationName.aiRoleInfoChanged.notificationName, object: nil)
container.createButton.addTarget(self, action: #selector(tapCreateButton), for: .touchUpInside)
container.headerView.avatarIv.tapAction = {[weak self] in
self?.tapEditAvatar()
}
container.headerView.tapChatAction = { aiId in
AppRouter.goChatVC(aiId: aiId)
}
container.headerView.tapEditAction = {[weak self] in
self?.tapEditAI()
}
$info.sink { [weak self] result in
self?.navigationView.alpha0Title = result?.nickname ?? ""
self?.container.config(info: result)
self?.container.headerView.config(info: result)
self?.container.aboutVc.config(info: result)
if let user = result{
let uid = user.userId
self?.setupNaviRightViews(UserCore.shared.user?.userId == uid)
}
if self?.likeView != nil{
self?.likeView.isLike = result?.liked ?? false
}
}.store(in: &cancellables)
}
// MARK: - Helper
private func setupNaviRightViews(_ isSelf: Bool = false){
// if navRightButton != nil{
// return
// }
navigationView.rightStackH.removeSubviews()
navigationView.paddingRightForRightStack = 16
likeView = {
let v = HeartLikeCountView(viewSize: .xl)
v.purIconStyle()
v.likeButton.addTarget(self, action: #selector(tapLikeButton), for: .touchUpInside)
navigationView.rightStackH.addArrangedSubview(v)
return v
}()
if let user = info {
likeView.isLike = user.liked ?? false
}
if isSelf{
navRightButton = {
let v = EPIconGhostButton(radius: .none, iconSize: .medium, iconCode: .more)
v.addTarget(self, action: #selector(tapNaviMore(_:)), for: .touchUpInside)
navigationView.rightStackH.addArrangedSubview(v)
v.snp.makeConstraints { make in
make.size.equalTo(v.bgImageSize())
}
return v
}()
}else{
navRightButton = {
let v = EPIconGhostButton(radius: .none, iconSize: .medium, iconCode: .shareBorder)
v.addTarget(self, action: #selector(tapShare), for: .touchUpInside)
navigationView.rightStackH.addArrangedSubview(v)
v.snp.makeConstraints { make in
make.size.equalTo(v.bgImageSize())
}
return v
}()
}
}
// MARK: - Action
@objc private func tapNaviMore(_ sender: UIButton) {
// let sheet = Sheet()
// let share = SheetAction(title: "Share", autoDismiss: true) { [weak self] in
// self?.tapShare()
// }
// let delete = SheetAction(title: "Delete", autoDismiss: true) { [weak self] in
// self?.alertDeleteRole()
// }
// sheet.addAction(share)
// sheet.addAction(delete)
// sheet.show()
let pop = CLPopoverListView()
let rect = view.convert(sender.frame, from: sender.superview)
var items = [CLPopoverListTextItem]()
do {
let listItem = CLPopoverListTextItem()
listItem.title = "Share"
listItem.image = MWIconFont.image(fromIcon: .shareBorder, size: CGSize(width: 20, height: 20), color: .text)
listItem.updateLayout()
listItem.selectedHandler = {[weak self] _ in
self?.tapShare()
}
items.append(listItem)
}
do {
let listItem = CLPopoverListTextItem()
listItem.title = "Delete"
listItem.image = MWIconFont.image(fromIcon: .iconDelete, size: CGSize(width: 20, height: 20), color: .text)
listItem.updateLayout()
listItem.selectedHandler = {[weak self] _ in
self?.alertDeleteRole()
}
items.append(listItem)
}
pop.setupCommonPopover(rect, inView: view, items: items, block: nil)
}
@objc private func tapShare(){
guard let aiId = info?.aiId else{return}
let content = "Come to Crushlevel for chat, Crush, and AI - chat."
let urlString = "\(AppConst.h5urlRoot)/@\(aiId)"
guard let url = URL(string: urlString) else { return }
// +
let items: [Any] = [url, content]
let activityVC = UIActivityViewController(activityItems: items,
applicationActivities: nil)
// iPad
if let popover = activityVC.popoverPresentationController {
popover.sourceView = view
popover.sourceRect = CGRect(x: view.bounds.midX,
y: view.bounds.midY,
width: 0, height: 0)
popover.permittedArrowDirections = []
}
present(activityVC, animated: true, completion: nil)
}
@objc private func tapLikeButton(){
guard UserCore.shared.checkUserLoginIfNotPushUserToLogin() else{return}
//
guard !isRequesting else { return }
guard let user = info, let aiId = user.aiId else { return }
let isliked = user.liked.boolValue
let likedCount = user.likedNum ?? 0
isRequesting = true
//
likeView.likeButton.isEnabled = false
if isliked {
//
let likeNewStatus = LikeOrCancelStatus.cancel
let newLikedCount = max(likedCount - 1, 0)
// UI
info?.liked = false
info?.likedNum = newLikedCount
likeView.isLike = false
likeView.countLabel.text = String.displayNumber(NSNumber(value: newLikedCount), scale: 1)
AIRoleProvider.request(.aiUserLikeOrCancel(aiId: aiId, likedStatus: likeNewStatus), modelType: EmptyModel.self) { [weak self] result in
self?.handleLikeRequestResult(result: result, aiId: aiId, isLike: false, originalLikedCount: likedCount)
}
} else {
//
let likeNewStatus = LikeOrCancelStatus.liked
let newLikedCount = likedCount + 1
//
likeView.playLotteLike { [weak self] completed in
// UI
self?.info?.liked = true
self?.info?.likedNum = newLikedCount
self?.likeView.isLike = true
self?.likeView.countLabel.text = String.displayNumber(NSNumber(value: newLikedCount), scale: 1)
}
AIRoleProvider.request(.aiUserLikeOrCancel(aiId: aiId, likedStatus: likeNewStatus), modelType: EmptyModel.self) { [weak self] result in
self?.handleLikeRequestResult(result: result, aiId: aiId, isLike: true, originalLikedCount: likedCount)
}
}
}
//
private func handleLikeRequestResult(result: Result<EmptyModel?, ResponseError>, aiId: Int, isLike: Bool, originalLikedCount: Int) {
isRequesting = false
likeView.likeButton.isEnabled = true
switch result {
case .success:
//
break
case .failure:
//
if let currentAiId = self.info?.aiId, currentAiId == aiId {
if isLike {
//
info?.liked = false
info?.likedNum = originalLikedCount
likeView.isLike = false
likeView.countLabel.text = String.displayNumber(NSNumber(value: originalLikedCount), scale: 1)
} else {
//
info?.liked = true
info?.likedNum = originalLikedCount
likeView.isLike = true
likeView.countLabel.text = String.displayNumber(NSNumber(value: originalLikedCount), scale: 1)
}
}
}
//
container.aboutVc.loadStatistics()
}
private func alertDeleteRole() {
let alert = Alert(title: "Delete Role", text: "删除角色不可恢复。为保障用户体验,角色删除后,已经与该角色发生过聊天的或者付费行为的用户,还可以正常与该角色互动。")
let delete = AlertAction(title: "Delete", actionStyle: .destructive) { [weak self] in
self?.doDeleteRole()
}
let cancel = AlertAction(title: "Cancel", actionStyle: .cancel)
alert.addAction(delete)
alert.addAction(cancel)
alert.show()
}
@objc private func tapCreateButton() {
// let sheet = Sheet()
// sheet.title = "Creation Option"
// let option1 = SheetAction(title: "Static Image", autoDismiss: true) {[weak self] in
// self?.goGenerateMoreAlbums()
// }
// let option2 = SheetAction(title: "Dynamic Image", autoDismiss: true) {
// Hud.comingsoon()
// }
// sheet.addAction(option1)
// sheet.addAction(option2)
// sheet.show()
goGenerateMoreAlbums()
}
@objc private func tapEditAI(){
// Convert AIRoleInfo to
AppRouter.goCreateEditAIRole(aiId: aiId)
}
@objc private func tapEditAvatar(){
guard UserCore.shared.isSelf(id: info?.userId) else{
return
}
guard let imgUrl = info?.homeImageUrl else {return}
ImageDownloader.downloadImage(from: imgUrl) {[weak self] img in
guard let image = img else{return}
self?.cropAIAvatar(image: image)
}
}
// MARK: - Functions
private func goGenerateMoreAlbums(){
guard let aiId = info?.aiId, info != nil else {return}
let vc = RolePhotoGenerateController(type: .album)
vc.aiId = aiId
vc.introduction = info?.introduction
vc.sex = info?.sex
vc.birthday = info?.birthday
presentNaviRootVc(vc: vc)
}
private func doDeleteRole() {
guard let aiInfo = info else { return }
Hud.showIndicator()
AIRoleProvider.request(.aiDel(id: aiInfo.aiId!)) { [weak self] result in
Hud.hideIndicator()
switch result {
case .success:
self?.alertDeleteOK()
NotificationCenter.post(name: .aiRoleCreatedOrDelete)
UserCore.shared.refreshUserInfo(block: nil)
case let .failure(failure):
dlog(failure)
}
}
}
private func alertDeleteOK() {
let alert = Alert(title: "The Role has been deleted", text: "The role has been deleted and cannot be accessed and interacted")
let gotit = AlertAction(title: "OK", actionStyle: .confirm) { [weak self] in
self?.navigationController?.popViewController(animated: true)
}
alert.addAction(gotit)
alert.show()
}
@objc private func notificationAIRoleInfoUpdated(noti: Notification) {
loadAIInfo(block: nil)
}
private func cropAIAvatar(image:UIImage?){
guard let avatar = image else {return}
let image = avatar
// let image = UIImage(named: "egpic")!
let cropViewController = CropViewController(image: image) { [unowned self] croppedImage in
self.container.headerView.avatarIv.bindImage(img: croppedImage)
//
let photo = UploadPhotoM()
photo.image = croppedImage
photo.addThisItemTimeStamp = Date().timeStamp
avatarModel = photo
Hud.showIndicator()
CloudStorage.shared.s3BatchAddPhotos([photo], bucket: .ROLE) {[weak self] result in
if result{
//self?.doPublishRole()
self?.doUpdateRoleAvatar()
}else{
Hud.hideIndicator()
}
}
}
let navc = CLNavigationController(rootViewController: cropViewController)
UIWindow.getTopViewController()?.present(navc, animated: true, completion: nil)
}
private func doUpdateRoleAvatar(){
guard let photo = avatarModel, let remoteFullPath = photo.remoteFullPath else{
Hud.hideIndicator()
return
}
AIRoleProvider.request(.modifyAIAvatar(aiId: aiId, userHead: remoteFullPath), modelType: EmptyModel.self) {[weak self] result in
Hud.hideIndicator()
switch result {
case .success:
self?.info?.headImg = remoteFullPath
self?.container.headerView.config(info: self?.info)
self?.avatarModel = nil
case .failure:
break
}
}
}
}