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

447 lines
16 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.

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